From 4ceb0cb38a1d9158a90c614064e44797a19a01a9 Mon Sep 17 00:00:00 2001 From: Juan Date: Fri, 15 Oct 2021 11:27:13 -0400 Subject: [PATCH] Show warning in UI when duplicate installations of DevTools extension are detected (#22563) --- .../src/background.js | 16 ++++-- .../src/checkForDuplicateInstallations.js | 14 ++--- .../src/constants.js | 4 +- .../react-devtools-extensions/src/main.js | 35 +++++++++++- .../src/devtools/views/DevTools.js | 4 ++ .../views/DuplicateInstallationDialog.js | 55 +++++++++++++++++++ 6 files changed, 115 insertions(+), 13 deletions(-) create mode 100644 packages/react-devtools-shared/src/devtools/views/DuplicateInstallationDialog.js diff --git a/packages/react-devtools-extensions/src/background.js b/packages/react-devtools-extensions/src/background.js index 85595d26010bf..e8ff0f3f16202 100644 --- a/packages/react-devtools-extensions/src/background.js +++ b/packages/react-devtools-extensions/src/background.js @@ -1,12 +1,19 @@ -/* global chrome */ +// @flow strict-local 'use strict'; -const ports = {}; +declare var chrome: any; + +const ports: { + [tab: string]: {|devtools: any, 'content-script': any|}, +} = {}; const IS_FIREFOX = navigator.userAgent.indexOf('Firefox') >= 0; -import {EXTENSION_INSTALL_CHECK_MESSAGE} from './constants'; +import { + EXTENSION_INSTALL_CHECK, + SHOW_DUPLICATE_EXTENSION_WARNING, +} from './constants'; chrome.runtime.onConnect.addListener(function(port) { let tab = null; @@ -120,8 +127,9 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { chrome.runtime.onMessageExternal.addListener( (request, sender, sendResponse) => { - if (request === EXTENSION_INSTALL_CHECK_MESSAGE) { + if (request === EXTENSION_INSTALL_CHECK) { sendResponse(true); + chrome.runtime.sendMessage(SHOW_DUPLICATE_EXTENSION_WARNING); } }, ); diff --git a/packages/react-devtools-extensions/src/checkForDuplicateInstallations.js b/packages/react-devtools-extensions/src/checkForDuplicateInstallations.js index e54476283cc61..1ebafe50b8835 100644 --- a/packages/react-devtools-extensions/src/checkForDuplicateInstallations.js +++ b/packages/react-devtools-extensions/src/checkForDuplicateInstallations.js @@ -11,13 +11,13 @@ declare var chrome: any; import {__DEBUG__} from 'react-devtools-shared/src/constants'; import { - EXTENSION_INSTALL_CHECK_MESSAGE, + EXTENSION_INSTALL_CHECK, EXTENSION_INSTALLATION_TYPE, INTERNAL_EXTENSION_ID, LOCAL_EXTENSION_ID, } from './constants'; -const UNRECOGNIZED_EXTENSION_WARNING = +const UNRECOGNIZED_EXTENSION_ERROR = '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. ' + 'Please make sure you only have a single version of the extension installed or enabled. ' + 'If you are developing this extension locally, make sure to build the extension using the `yarn build::local` command.'; @@ -68,9 +68,9 @@ export function checkForDuplicateInstallations(callback: boolean => void) { // detect if there are other installations of DevTools present. // In this case, assume there are no duplicate exensions and show a warning about // potential conflicts. - console.error(UNRECOGNIZED_EXTENSION_WARNING); + console.error(UNRECOGNIZED_EXTENSION_ERROR); chrome.devtools.inspectedWindow.eval( - `console.error("${UNRECOGNIZED_EXTENSION_WARNING}")`, + `console.error("${UNRECOGNIZED_EXTENSION_ERROR}")`, ); callback(false); break; @@ -80,9 +80,9 @@ export function checkForDuplicateInstallations(callback: boolean => void) { // are other installations of DevTools present. // In this case, assume there are no duplicate exensions and show a warning about // potential conflicts. - console.error(UNRECOGNIZED_EXTENSION_WARNING); + console.error(UNRECOGNIZED_EXTENSION_ERROR); chrome.devtools.inspectedWindow.eval( - `console.error("${UNRECOGNIZED_EXTENSION_WARNING}")`, + `console.error("${UNRECOGNIZED_EXTENSION_ERROR}")`, ); callback(false); break; @@ -105,7 +105,7 @@ function checkForInstalledExtension(extensionId: string): Promise { return new Promise(resolve => { chrome.runtime.sendMessage( extensionId, - EXTENSION_INSTALL_CHECK_MESSAGE, + EXTENSION_INSTALL_CHECK, response => { if (__DEBUG__) { console.log( diff --git a/packages/react-devtools-extensions/src/constants.js b/packages/react-devtools-extensions/src/constants.js index 5e85e4aec40e7..310bbaca11563 100644 --- a/packages/react-devtools-extensions/src/constants.js +++ b/packages/react-devtools-extensions/src/constants.js @@ -11,7 +11,9 @@ declare var chrome: any; export const CURRENT_EXTENSION_ID = chrome.runtime.id; -export const EXTENSION_INSTALL_CHECK_MESSAGE = 'extension-install-check'; +export const EXTENSION_INSTALL_CHECK = 'extension-install-check'; +export const SHOW_DUPLICATE_EXTENSION_WARNING = + 'show-duplicate-extension-warning'; export const CHROME_WEBSTORE_EXTENSION_ID = 'fmkadmapgofadopljbjfkapdkoienihi'; export const INTERNAL_EXTENSION_ID = 'dnjnjgbfilfphmojnmhliehogmojhclc'; diff --git a/packages/react-devtools-extensions/src/main.js b/packages/react-devtools-extensions/src/main.js index 8280158e1db70..5203b511035d5 100644 --- a/packages/react-devtools-extensions/src/main.js +++ b/packages/react-devtools-extensions/src/main.js @@ -22,7 +22,11 @@ import { import DevTools from 'react-devtools-shared/src/devtools/views/DevTools'; import {__DEBUG__} from 'react-devtools-shared/src/constants'; import {logEvent} from 'react-devtools-shared/src/Logger'; -import {CURRENT_EXTENSION_ID, EXTENSION_INSTALLATION_TYPE} from './constants'; +import { + CURRENT_EXTENSION_ID, + EXTENSION_INSTALLATION_TYPE, + SHOW_DUPLICATE_EXTENSION_WARNING, +} from './constants'; import {checkForDuplicateInstallations} from './checkForDuplicateInstallations'; const LOCAL_STORAGE_SUPPORTS_PROFILING_KEY = @@ -108,11 +112,39 @@ function createPanelIfReactLoaded() { let mostRecentOverrideTab = null; let render = null; let root = null; + let warnIfDuplicateInstallation = false; const tabId = chrome.devtools.inspectedWindow.tabId; registerDevToolsEventLogger('extension'); + function onDuplicateExtensionMessage(message) { + if (message === SHOW_DUPLICATE_EXTENSION_WARNING) { + chrome.runtime.onMessage.removeListener( + onDuplicateExtensionMessage, + ); + + if (warnIfDuplicateInstallation === true) { + return; + } + warnIfDuplicateInstallation = true; + const errorMessage = + 'React Developer Tools: We detected that there are multiple versions of React Developer Tools ' + + 'installed and enabled in your browser at the same time, which will cause ' + + 'issues while using the extension. ' + + 'Please ensure that you have installed and enabled only a single ' + + 'version of React Developer Tools before proceeding.'; + console.error(errorMessage); + chrome.devtools.inspectedWindow.eval( + `console.error("${errorMessage}")`, + ); + if (render != null) { + render(); + } + } + } + chrome.runtime.onMessage.addListener(onDuplicateExtensionMessage); + function initBridgeAndStore() { const port = chrome.runtime.connect({ name: String(tabId), @@ -374,6 +406,7 @@ function createPanelIfReactLoaded() { hookNamesModuleLoaderFunction, overrideTab, profilerPortalContainer, + warnIfDuplicateInstallation, showTabBar: false, store, warnIfUnsupportedVersionDetected: true, diff --git a/packages/react-devtools-shared/src/devtools/views/DevTools.js b/packages/react-devtools-shared/src/devtools/views/DevTools.js index 781ad9d902f68..8f1b6a22c11bc 100644 --- a/packages/react-devtools-shared/src/devtools/views/DevTools.js +++ b/packages/react-devtools-shared/src/devtools/views/DevTools.js @@ -34,6 +34,7 @@ import {SchedulingProfilerContextController} from 'react-devtools-scheduling-pro import {ModalDialogContextController} from './ModalDialog'; import ReactLogo from './ReactLogo'; import UnsupportedBridgeProtocolDialog from './UnsupportedBridgeProtocolDialog'; +import DuplicateInstallationDialog from './DuplicateInstallationDialog'; import UnsupportedVersionDialog from './UnsupportedVersionDialog'; import WarnIfLegacyBackendDetected from './WarnIfLegacyBackendDetected'; import {useLocalStorage} from './hooks'; @@ -73,6 +74,7 @@ export type Props = {| enabledInspectedElementContextMenu?: boolean, showTabBar?: boolean, store: Store, + warnIfDuplicateInstallation?: boolean, warnIfLegacyBackendDetected?: boolean, warnIfUnsupportedVersionDetected?: boolean, viewAttributeSourceFunction?: ?ViewAttributeSource, @@ -132,6 +134,7 @@ export default function DevTools({ profilerPortalContainer, showTabBar = false, store, + warnIfDuplicateInstallation = false, warnIfLegacyBackendDetected = false, warnIfUnsupportedVersionDetected = false, viewAttributeSourceFunction, @@ -319,6 +322,7 @@ export default function DevTools({ + {warnIfDuplicateInstallation && } {warnIfLegacyBackendDetected && } {warnIfUnsupportedVersionDetected && } diff --git a/packages/react-devtools-shared/src/devtools/views/DuplicateInstallationDialog.js b/packages/react-devtools-shared/src/devtools/views/DuplicateInstallationDialog.js new file mode 100644 index 0000000000000..12a32ce4b7f43 --- /dev/null +++ b/packages/react-devtools-shared/src/devtools/views/DuplicateInstallationDialog.js @@ -0,0 +1,55 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + */ + +import * as React from 'react'; +import {Fragment, useContext, useEffect} from 'react'; +import {isInternalFacebookBuild} from 'react-devtools-feature-flags'; +import {ModalDialogContext} from './ModalDialog'; + +export default function DuplicateInstallationDialog(_: {||}) { + const {dispatch} = useContext(ModalDialogContext); + + useEffect(() => { + dispatch({ + canBeDismissed: false, + id: 'DuplicateInstallationDialog', + type: 'SHOW', + title: 'Duplicate Installations of DevTools Detected', + content: , + }); + }, []); + return null; +} + +function DialogContent(_: {||}) { + return ( + +

+ We detected that there are multiple versions of React Developer Tools + installed and enabled in your browser at the same time, which will cause + issues while using the extension. +

+ {isInternalFacebookBuild ? ( +

+ Before proceeding, please ensure that the only enabled version of + React Developer Tools is the internal (Chef-installed) version. To + manage your extensions, visit the about://extensions page + in your browser. +

+ ) : ( +

+ Please ensure that you have installed and enabled only a single + version of React Developer Tools before proceeding. To manage your + extensions, visit the about://extensions page in your + browser. +

+ )} +
+ ); +}