diff --git a/devtools/client/framework/browser-toolbox/test/browser.ini b/devtools/client/framework/browser-toolbox/test/browser.ini index d5c00bf310a0..4164d0224f3c 100644 --- a/devtools/client/framework/browser-toolbox/test/browser.ini +++ b/devtools/client/framework/browser-toolbox/test/browser.ini @@ -34,3 +34,4 @@ prefs = [browser_browser_toolbox_rtl.js] [browser_browser_toolbox_ruleview_stylesheet.js] [browser_browser_toolbox_shouldprocessupdates.js] +[browser_browser_toolbox_watchedByDevTools.js] diff --git a/devtools/client/framework/browser-toolbox/test/browser_browser_toolbox_watchedByDevTools.js b/devtools/client/framework/browser-toolbox/test/browser_browser_toolbox_watchedByDevTools.js new file mode 100644 index 000000000000..d35408bb3db6 --- /dev/null +++ b/devtools/client/framework/browser-toolbox/test/browser_browser_toolbox_watchedByDevTools.js @@ -0,0 +1,129 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Check that the "watchedByDevTools" flag is properly handled. + */ + +const EXAMPLE_NET_URI = + "http://example.net/document-builder.sjs?html=
net"; +const EXAMPLE_COM_URI = + "https://example.com/document-builder.sjs?html=
com"; +const EXAMPLE_ORG_URI = + "https://example.org/document-builder.sjs?headers=Cross-Origin-Opener-Policy:same-origin&html=
org
"; + +add_task(async function() { + const isMbtEnabled = Services.prefs.getBoolPref( + "devtools.browsertoolbox.fission", + false + ); + + const topWindow = Services.wm.getMostRecentWindow("navigator:browser"); + is( + topWindow.browsingContext.watchedByDevTools, + false, + "watchedByDevTools isn't set on the parent process browsing context when DevTools aren't opened" + ); + + // Open 2 tabs that we can check the flag on + const tabNet = await addTab(EXAMPLE_NET_URI); + const tabCom = await addTab(EXAMPLE_COM_URI); + + is( + tabNet.linkedBrowser.browsingContext.watchedByDevTools, + false, + "watchedByDevTools is not set on the .net tab" + ); + is( + tabCom.linkedBrowser.browsingContext.watchedByDevTools, + false, + "watchedByDevTools is not set on the .com tab" + ); + + info("Open the BrowserToolbox so the parent process will be watched"); + const ToolboxTask = await initBrowserToolboxTask(); + + is( + topWindow.browsingContext.watchedByDevTools, + true, + "watchedByDevTools is set when the browser toolbox is opened" + ); + + // Open a new tab when the browser toolbox is opened + const newTab = await addTab(EXAMPLE_COM_URI); + + // Only check the flag value on tab's browsing contexts when the MultiProcess Browser Toolbox + // is enabled as the flag isn't set there in the Legacy Browser Toolbox + if (isMbtEnabled) { + is( + tabNet.linkedBrowser.browsingContext.watchedByDevTools, + true, + "watchedByDevTools is set on the .net tab browsing context after opening the browser toolbox" + ); + is( + tabCom.linkedBrowser.browsingContext.watchedByDevTools, + true, + "watchedByDevTools is set on the .com tab browsing context after opening the browser toolbox" + ); + + info( + "Check that adding watchedByDevTools is set on a tab that was added when the browser toolbox was opened" + ); + is( + newTab.linkedBrowser.browsingContext.watchedByDevTools, + true, + "watchedByDevTools is set on the newly opened tab" + ); + + info( + "Check that watchedByDevTools persist when navigating to a page that creates a new browsing context" + ); + const previousBrowsingContextId = newTab.linkedBrowser.browsingContext.id; + const onBrowserLoaded = BrowserTestUtils.browserLoaded( + newTab.linkedBrowser, + false, + encodeURI(EXAMPLE_ORG_URI) + ); + BrowserTestUtils.loadURI(newTab.linkedBrowser, EXAMPLE_ORG_URI); + await onBrowserLoaded; + + isnot( + newTab.linkedBrowser.browsingContext.id, + previousBrowsingContextId, + "A new browsing context was created" + ); + + is( + newTab.linkedBrowser.browsingContext.watchedByDevTools, + true, + "watchedByDevTools is still set after navigating the tab to a page which forces a new browsing context" + ); + } + + info("Destroying browser toolbox"); + await ToolboxTask.destroy(); + + is( + topWindow.browsingContext.watchedByDevTools, + false, + "watchedByDevTools was reset when the browser toolbox was closed" + ); + + is( + tabNet.linkedBrowser.browsingContext.watchedByDevTools, + false, + "watchedByDevTools was reset on the .net tab after closing the browser toolbox" + ); + is( + tabCom.linkedBrowser.browsingContext.watchedByDevTools, + false, + "watchedByDevTools was reset on the .com tab after closing the browser toolbox" + ); + is( + newTab.linkedBrowser.browsingContext.watchedByDevTools, + false, + "watchedByDevTools was reset on the tab opened while the browser toolbox was opened" + ); +}); diff --git a/devtools/client/framework/test/browser.ini b/devtools/client/framework/test/browser.ini index c86b4e26f0fa..4e07f04796df 100644 --- a/devtools/client/framework/test/browser.ini +++ b/devtools/client/framework/test/browser.ini @@ -154,6 +154,7 @@ run-if = e10s [browser_toolbox_view_source_01.js] [browser_toolbox_view_source_02.js] [browser_toolbox_view_source_03.js] +[browser_toolbox_watchedByDevTools.js] [browser_toolbox_window_reload_target.js] [browser_toolbox_window_reload_target_force.js] [browser_toolbox_window_shortcuts.js] diff --git a/devtools/client/framework/test/browser_toolbox_watchedByDevTools.js b/devtools/client/framework/test/browser_toolbox_watchedByDevTools.js new file mode 100644 index 000000000000..8c0b77ed5327 --- /dev/null +++ b/devtools/client/framework/test/browser_toolbox_watchedByDevTools.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Check that the "watchedByDevTools" flag is properly handled. + */ +const EXAMPLE_NET_URI = + "http://example.net/document-builder.sjs?html=
net"; +const EXAMPLE_COM_URI = + "https://example.com/document-builder.sjs?html=
com"; +const EXAMPLE_ORG_URI = + "https://example.org/document-builder.sjs?headers=Cross-Origin-Opener-Policy:same-origin&html=
org
"; + +add_task(async function() { + const tab = await addTab(EXAMPLE_NET_URI); + + is( + tab.linkedBrowser.browsingContext.watchedByDevTools, + false, + "watchedByDevTools isn't set when DevTools aren't opened" + ); + + info( + "Open a toolbox for the opened tab and check that watchedByDevTools is set" + ); + await gDevTools.showToolboxForTab(tab, { toolId: "options" }); + + is( + tab.linkedBrowser.browsingContext.watchedByDevTools, + true, + "watchedByDevTools is set after opening a toolbox" + ); + + info( + "Check that watchedByDevTools persist when the tab navigates to a different origin" + ); + await navigateTo(EXAMPLE_COM_URI); + + is( + tab.linkedBrowser.browsingContext.watchedByDevTools, + true, + "watchedByDevTools is still set after navigating to a different origin" + ); + + info( + "Check that watchedByDevTools persist when navigating to a page that creates a new browsing context" + ); + const previousBrowsingContextId = tab.linkedBrowser.browsingContext.id; + await navigateTo(EXAMPLE_ORG_URI); + + isnot( + tab.linkedBrowser.browsingContext.id, + previousBrowsingContextId, + "A new browsing context was created" + ); + + is( + tab.linkedBrowser.browsingContext.watchedByDevTools, + true, + "watchedByDevTools is still set after navigating to a new browsing context" + ); + + info("Check that the flag is reset when the toolbox is closed"); + await gDevTools.closeToolboxForTab(tab); + is( + tab.linkedBrowser.browsingContext.watchedByDevTools, + false, + "watchedByDevTools is reset after closing the toolbox" + ); +}); diff --git a/devtools/server/actors/targets/browsing-context.js b/devtools/server/actors/targets/browsing-context.js index f52712afed66..ad223f997b9f 100644 --- a/devtools/server/actors/targets/browsing-context.js +++ b/devtools/server/actors/targets/browsing-context.js @@ -1674,9 +1674,14 @@ DebuggerProgressListener.prototype = { // - reporting the contents of HTML loaded in the docshells, // - or capturing stacks for the network monitor. // - // This attribute may already have been toggled by a parent BrowsingContext. - // Typically the parent process or tab target. Both are top level BrowsingContext. - if (docShell.browsingContext.top == docShell.browsingContext) { + // This flag is also set in frame-helper but in the case of the browser toolbox, we + // don't have the watcher enabled by default yet, and as a result we need to set it + // here for the parent process browsing context. + // This should be removed as part of Bug 1709529. + if ( + this._targetActor.typeName === "parentProcessTarget" && + docShell.browsingContext.top == docShell.browsingContext + ) { docShell.browsingContext.watchedByDevTools = true; } }, @@ -1711,11 +1716,12 @@ DebuggerProgressListener.prototype = { this._knownWindowIDs.delete(getWindowID(win)); } - // We can only toggle this attribute on top level BrowsingContext, - // this will be propagated over the whole tree of BC. - // So we only need to set it from Parent Process Target - // and Tab Target. Tab's BrowsingContext are actually considered as top level BC. - if (docShell.browsingContext.top == docShell.browsingContext) { + // We only reset it for parent process target actor as the flag should be set in parent + // process, and thus is set elsewhere for other type of BrowsingContextActor. + if ( + this._targetActor.typeName === "parentProcessTarget" && + docShell.browsingContext.top == docShell.browsingContext + ) { docShell.browsingContext.watchedByDevTools = false; } }, diff --git a/devtools/server/actors/watcher/target-helpers/frame-helper.js b/devtools/server/actors/watcher/target-helpers/frame-helper.js index 612febab9e02..719d0e6fc043 100644 --- a/devtools/server/actors/watcher/target-helpers/frame-helper.js +++ b/devtools/server/actors/watcher/target-helpers/frame-helper.js @@ -4,6 +4,7 @@ "use strict"; +const Services = require("Services"); const { WatcherRegistry, } = require("devtools/server/actors/watcher/WatcherRegistry.jsm"); @@ -16,6 +17,8 @@ const { shouldNotifyWindowGlobal, } = require("devtools/server/actors/watcher/target-helpers/utils.js"); +const browsingContextAttachedObserverByWatcher = new Map(); + /** * Force creating targets for all existing BrowsingContext, that, for a given Watcher Actor. * @@ -36,6 +39,13 @@ async function createTargets(watcher) { "Existing WindowGlobal" ); + // We need to set the watchedByDevTools flag on all top-level browsing context. In the + // case of a content toolbox, this is done in the tab descriptor, but when we're in the + // browser toolbox, such descriptor is not created. + if (browsingContext.top === browsingContext) { + browsingContext.watchedByDevTools = true; + } + // Await for the query in order to try to resolve only *after* we received these // already available targets. const promise = browsingContext.currentWindowGlobal @@ -48,6 +58,46 @@ async function createTargets(watcher) { }); promises.push(promise); } + + // If we have a browserElement, set the watchedByDevTools flag on its related browsing context + // TODO: We should also set the flag for the "parent process" browsing context when we're + // in the browser toolbox. This is blocked by Bug 1675763, and should be handled as part + // of Bug 1709529. + if (watcher.browserElement) { + // The `watchedByDevTools` enables gecko behavior tied to this flag, such as: + // - reporting the contents of HTML loaded in the docshells + // - capturing stacks for the network monitor. + watcher.browserElement.browsingContext.watchedByDevTools = true; + } + + if (!browsingContextAttachedObserverByWatcher.has(watcher)) { + // We store the browserId here as watcher.browserElement.browserId can momentary be + // set to 0 when there's a navigation to a new browsing context. + const browserId = watcher.browserElement?.browserId; + const onBrowsingContextAttached = browsingContext => { + // We want to set watchedByDevTools on new top-level browsing contexts: + // - in the case of the BrowserToolbox/BrowserConsole, that would be the browsing + // contexts of all the tabs we want to handle. + // - for the regular toolbox, browsing context that are being created when navigating + // to a page that forces a new browsing context. + if ( + browsingContext.top === browsingContext && + (!watcher.browserElement || browserId === browsingContext.browserId) + ) { + browsingContext.watchedByDevTools = true; + } + }; + Services.obs.addObserver( + onBrowsingContextAttached, + "browsing-context-attached" + ); + // We store the observer so we can retrieve it elsewhere (e.g. for removal in destroyTargets). + browsingContextAttachedObserverByWatcher.set( + watcher, + onBrowsingContextAttached + ); + } + return Promise.all(promises); } @@ -68,6 +118,10 @@ function destroyTargets(watcher) { "Existing WindowGlobal" ); + if (browsingContext.top === browsingContext) { + browsingContext.watchedByDevTools = false; + } + browsingContext.currentWindowGlobal .getActor("DevToolsFrame") .destroyTarget({ @@ -75,6 +129,18 @@ function destroyTargets(watcher) { browserId: watcher.browserId, }); } + + if (watcher.browserElement) { + watcher.browserElement.browsingContext.watchedByDevTools = false; + } + + if (browsingContextAttachedObserverByWatcher.has(watcher)) { + Services.obs.removeObserver( + browsingContextAttachedObserverByWatcher.get(watcher), + "browsing-context-attached" + ); + browsingContextAttachedObserverByWatcher.delete(watcher); + } } /**