|  | 
|  | 1 | +/** | 
|  | 2 | + * Copyright (c) Facebook, Inc. and its affiliates. | 
|  | 3 | + * | 
|  | 4 | + * This source code is licensed under the MIT license found in the | 
|  | 5 | + * LICENSE file in the root directory of this source tree. | 
|  | 6 | + * | 
|  | 7 | + * @flow strict-local | 
|  | 8 | + */ | 
|  | 9 | + | 
|  | 10 | +declare var chrome: any; | 
|  | 11 | + | 
|  | 12 | +import {__DEBUG__} from 'react-devtools-shared/src/constants'; | 
|  | 13 | +import { | 
|  | 14 | +  EXTENSION_INSTALL_CHECK_MESSAGE, | 
|  | 15 | +  EXTENSION_INSTALLATION_TYPE, | 
|  | 16 | +  INTERNAL_EXTENSION_ID, | 
|  | 17 | +  LOCAL_EXTENSION_ID, | 
|  | 18 | +} from './constants'; | 
|  | 19 | + | 
|  | 20 | +const UNRECOGNIZED_EXTENSION_WARNING = | 
|  | 21 | +  'React Developer Tools: You are running an unrecognized installation of the React Developer Tools extension, which might conflict with other versions of the extension installed in your browser. ' + | 
|  | 22 | +  'Please make sure you only have a single version of the extension installed or enabled. ' + | 
|  | 23 | +  'If you are developing this extension locally, make sure to build the extension using the `yarn build:<browser>:local` command.'; | 
|  | 24 | + | 
|  | 25 | +export function checkForDuplicateInstallations(callback: boolean => void) { | 
|  | 26 | +  switch (EXTENSION_INSTALLATION_TYPE) { | 
|  | 27 | +    case 'public': { | 
|  | 28 | +      // If this is the public extension (e.g. from Chrome Web Store), check if an internal | 
|  | 29 | +      // or local build of the extension is also installed, and if so, disable this extension. | 
|  | 30 | +      // TODO show warning if other installations are present. | 
|  | 31 | +      checkForInstalledExtensions([ | 
|  | 32 | +        INTERNAL_EXTENSION_ID, | 
|  | 33 | +        LOCAL_EXTENSION_ID, | 
|  | 34 | +      ]).then(areExtensionsInstalled => { | 
|  | 35 | +        if (areExtensionsInstalled.some(isInstalled => isInstalled)) { | 
|  | 36 | +          callback(true); | 
|  | 37 | +        } else { | 
|  | 38 | +          callback(false); | 
|  | 39 | +        } | 
|  | 40 | +      }); | 
|  | 41 | +      break; | 
|  | 42 | +    } | 
|  | 43 | +    case 'internal': { | 
|  | 44 | +      // If this is the internal extension, check if a local build of the extension | 
|  | 45 | +      // is also installed, and if so, disable this extension. | 
|  | 46 | +      // If the public version of the extension is also installed, that extension | 
|  | 47 | +      // will disable itself. | 
|  | 48 | +      // TODO show warning if other installations are present. | 
|  | 49 | +      checkForInstalledExtension(LOCAL_EXTENSION_ID).then(isInstalled => { | 
|  | 50 | +        if (isInstalled) { | 
|  | 51 | +          callback(true); | 
|  | 52 | +        } else { | 
|  | 53 | +          callback(false); | 
|  | 54 | +        } | 
|  | 55 | +      }); | 
|  | 56 | +      break; | 
|  | 57 | +    } | 
|  | 58 | +    case 'local': { | 
|  | 59 | +      if (__DEV__) { | 
|  | 60 | +        // If this is the local extension (i.e. built locally during development), | 
|  | 61 | +        // always keep this one enabled. Other installations disable themselves if | 
|  | 62 | +        // they detect the local build is installed. | 
|  | 63 | +        callback(false); | 
|  | 64 | +        break; | 
|  | 65 | +      } | 
|  | 66 | + | 
|  | 67 | +      // If this extension wasn't built locally during development, we can't reliably | 
|  | 68 | +      // detect if there are other installations of DevTools present. | 
|  | 69 | +      // In this case, assume there are no duplicate exensions and show a warning about | 
|  | 70 | +      // potential conflicts. | 
|  | 71 | +      console.error(UNRECOGNIZED_EXTENSION_WARNING); | 
|  | 72 | +      chrome.devtools.inspectedWindow.eval( | 
|  | 73 | +        `console.error("${UNRECOGNIZED_EXTENSION_WARNING}")`, | 
|  | 74 | +      ); | 
|  | 75 | +      callback(false); | 
|  | 76 | +      break; | 
|  | 77 | +    } | 
|  | 78 | +    case 'unknown': { | 
|  | 79 | +      // If we don't know how this extension was built, we can't reliably detect if there | 
|  | 80 | +      // are other installations of DevTools present. | 
|  | 81 | +      // In this case, assume there are no duplicate exensions and show a warning about | 
|  | 82 | +      // potential conflicts. | 
|  | 83 | +      console.error(UNRECOGNIZED_EXTENSION_WARNING); | 
|  | 84 | +      chrome.devtools.inspectedWindow.eval( | 
|  | 85 | +        `console.error("${UNRECOGNIZED_EXTENSION_WARNING}")`, | 
|  | 86 | +      ); | 
|  | 87 | +      callback(false); | 
|  | 88 | +      break; | 
|  | 89 | +    } | 
|  | 90 | +    default: { | 
|  | 91 | +      (EXTENSION_INSTALLATION_TYPE: empty); | 
|  | 92 | +    } | 
|  | 93 | +  } | 
|  | 94 | +} | 
|  | 95 | + | 
|  | 96 | +function checkForInstalledExtensions( | 
|  | 97 | +  extensionIds: string[], | 
|  | 98 | +): Promise<boolean[]> { | 
|  | 99 | +  return Promise.all( | 
|  | 100 | +    extensionIds.map(extensionId => checkForInstalledExtension(extensionId)), | 
|  | 101 | +  ); | 
|  | 102 | +} | 
|  | 103 | + | 
|  | 104 | +function checkForInstalledExtension(extensionId: string): Promise<boolean> { | 
|  | 105 | +  return new Promise(resolve => { | 
|  | 106 | +    chrome.runtime.sendMessage( | 
|  | 107 | +      extensionId, | 
|  | 108 | +      EXTENSION_INSTALL_CHECK_MESSAGE, | 
|  | 109 | +      response => { | 
|  | 110 | +        if (__DEBUG__) { | 
|  | 111 | +          console.log( | 
|  | 112 | +            'checkForDuplicateInstallations: Duplicate installation check responded with', | 
|  | 113 | +            { | 
|  | 114 | +              response, | 
|  | 115 | +              error: chrome.runtime.lastError?.message, | 
|  | 116 | +              currentExtension: EXTENSION_INSTALLATION_TYPE, | 
|  | 117 | +              checkingExtension: extensionId, | 
|  | 118 | +            }, | 
|  | 119 | +          ); | 
|  | 120 | +        } | 
|  | 121 | +        if (chrome.runtime.lastError != null) { | 
|  | 122 | +          resolve(false); | 
|  | 123 | +        } else { | 
|  | 124 | +          resolve(true); | 
|  | 125 | +        } | 
|  | 126 | +      }, | 
|  | 127 | +    ); | 
|  | 128 | +  }); | 
|  | 129 | +} | 
0 commit comments