Bug 1685500 - [devtools] Create target actors for all iframes. r=ochameau.

This patch introduces a new pref, devtools.every-frame-target.enabled,
that when set to true will cause target actors to be created on every
iframe, no matter if they are remote or not, no matter if Fission is
enabled or not.

This adds a ignoreSubFrames property on the BrowsingContextActor so
it can focus solely on the docShell it was passed.

Differential Revision: https://phabricator.services.mozilla.com/D125517
This commit is contained in:
Nicolas Chevobbe 2021-09-15 05:35:37 +00:00
parent 327a609e3a
commit ac25598d5b
5 changed files with 62 additions and 11 deletions

View File

@ -2168,6 +2168,10 @@ pref("devtools.browsertoolbox.fission", false);
// This preference will enable watching top-level targets from the server side.
pref("devtools.target-switching.server.enabled", true);
// Setting this preference to true will result in DevTools creating a target for each frame
// (i.e. not only for top-level document and remote frames).
pref("devtools.every-frame-target.enabled", false);
// Toolbox Button preferences
pref("devtools.command-button-pick.enabled", true);
pref("devtools.command-button-frames.enabled", true);

View File

@ -30,6 +30,11 @@ const PREFERENCES = [
"devtools.target-switching.server.enabled",
"Enable experimental server side target switching",
],
[
"devtools.every-frame-target.enabled",
"When enabled, targets will be created for all iframes, no matter if " +
"they are remote or not, independently of Fission being enabled or not",
],
[
"devtools.testing.enableServerWatcherSupport",
"Enable experimental server-side resources (see watcher actor to get the " +

View File

@ -263,10 +263,13 @@ const browsingContextTargetPrototype = {
* implementation. But for server-side target switching this flag will be exposed
* to the client and should be available for all target actor classes. It will be
* used to detect target switching. (Bug 1644397)
* - ignoreSubFrames Boolean
* If true, the actor will only focus on the passed docShell and not on the whole
* docShell tree. This should be enabled when we have targets for all documents.
*/
initialize: function(
connection,
{ docShell, followWindowGlobalLifeCycle, isTopLevelTarget }
{ docShell, followWindowGlobalLifeCycle, isTopLevelTarget, ignoreSubFrames }
) {
Actor.prototype.initialize.call(this, connection);
@ -279,6 +282,7 @@ const browsingContextTargetPrototype = {
this.followWindowGlobalLifeCycle = followWindowGlobalLifeCycle;
this.isTopLevelTarget = !!isTopLevelTarget;
this.ignoreSubFrames = ignoreSubFrames;
// A map of actor names to actor instances provided by extensions.
this._extraActors = {};
@ -381,6 +385,10 @@ const browsingContextTargetPrototype = {
* @return {Array}
*/
get docShells() {
if (this.ignoreSubFrames) {
return [this.docShell];
}
return getChildDocShells(this.docShell);
},
@ -1408,6 +1416,10 @@ const browsingContextTargetPrototype = {
_windowReady(window, { isFrameSwitching, isBFCache } = {}) {
const isTopLevel = window == this.window;
if (this.ignoreSubFrames && !this.isTopLevel) {
return;
}
// We just reset iframe list on WillNavigate, so we now list all existing
// frames when we load a new document in the original window
if (window == this._originalWindow && !isFrameSwitching) {
@ -1438,6 +1450,10 @@ const browsingContextTargetPrototype = {
) {
const isTopLevel = window == this.window;
if (this.ignoreSubFrames && !this.isTopLevel) {
return;
}
// If this follows WindowGlobal lifecycle, this target will be destroyed, alongside its top level document.
// Only notify about in-process iframes.
// Note that OOP iframes won't emit window-ready and will also have their dedicated target.
@ -1467,8 +1483,12 @@ const browsingContextTargetPrototype = {
navigationStart,
}) {
let isTopLevel = window == this.window;
let reset = false;
if (this.ignoreSubFrames && !this.isTopLevel) {
return;
}
let reset = false;
if (window == this._originalWindow && !isFrameSwitching) {
// If the top level document changes and we are targeting an iframe, we
// need to reset to the upcoming new top level document. But for this
@ -1524,6 +1544,10 @@ const browsingContextTargetPrototype = {
_navigate(window, isFrameSwitching = false) {
const isTopLevel = window == this.window;
if (this.ignoreSubFrames && !this.isTopLevel) {
return;
}
// navigate event needs to be dispatched synchronously,
// by calling the listeners in the order or registration.
// This event is fired once the document is loaded,
@ -1685,7 +1709,10 @@ DebuggerProgressListener.prototype = {
handler.addEventListener("pagehide", this._onWindowHidden, true);
// Dispatch the _windowReady event on the targetActor for pre-existing windows
for (const win of this._getWindowsInDocShell(docShell)) {
const windows = this._targetActor.ignoreSubFrames
? [docShellWindow]
: this._getWindowsInDocShell(docShell);
for (const win of windows) {
this._targetActor._windowReady(win);
this._knownWindowIDs.set(getWindowID(win), win);
}
@ -1729,7 +1756,10 @@ DebuggerProgressListener.prototype = {
handler.removeEventListener("pageshow", this._onWindowCreated, true);
handler.removeEventListener("pagehide", this._onWindowHidden, true);
for (const win of this._getWindowsInDocShell(docShell)) {
const windows = this._targetActor.ignoreSubFrames
? [docShellWindow]
: this._getWindowsInDocShell(docShell);
for (const win of windows) {
this._knownWindowIDs.delete(getWindowID(win));
}

View File

@ -19,6 +19,11 @@ const {
const browsingContextAttachedObserverByWatcher = new Map();
const isEveryFrameTargetEnabled = Services.prefs.getBoolPref(
"devtools.every-frame-target.enabled",
false
);
/**
* Force creating targets for all existing BrowsingContext, that, for a given Watcher Actor.
*
@ -307,10 +312,11 @@ function getWatchingBrowsingContexts(watcher) {
* Browser Element and any of its (nested) children iframes.
*/
function getFilteredRemoteBrowsingContext(browserElement) {
return getAllRemoteBrowsingContexts(
browserElement?.browsingContext
).filter(browsingContext =>
shouldNotifyWindowGlobal(browsingContext, browserElement?.browserId)
return getAllRemoteBrowsingContexts(browserElement?.browsingContext).filter(
browsingContext =>
shouldNotifyWindowGlobal(browsingContext, browserElement?.browserId, {
acceptNonRemoteFrame: isEveryFrameTargetEnabled,
})
);
}

View File

@ -24,6 +24,11 @@ XPCOMUtils.defineLazyModuleGetters(this, {
"resource://devtools/server/connectors/js-window-actor/WindowGlobalLogger.jsm",
});
const isEveryFrameTargetEnabled = Services.prefs.getBoolPref(
"devtools.every-frame-target.enabled",
false
);
// Name of the attribute into which we save data in `sharedData` object.
const SHARED_DATA_KEY_NAME = "DevTools:watchedPerWatcher";
@ -81,13 +86,13 @@ function shouldNotifyWindowGlobal(
return false;
}
// We may process an iframe that runs in the same process as its parent
// and we don't want to create targets for them yet. Instead the BrowsingContextTargetActor
// We may process an iframe that runs in the same process as its parent and we don't want
// to create targets for them if same origin targets are not enabled. Instead the BrowsingContextTargetActor
// will inspect these children document via docShell tree (typically via `docShells` or `windows` getters).
// This is quite common when Fission is off as any iframe will run in same process
// as their parent document. But it can also happen with Fission enabled if iframes have
// children iframes using the same origin.
if (!windowGlobal.isProcessRoot) {
if (!isEveryFrameTargetEnabled && !windowGlobal.isProcessRoot) {
return false;
}
@ -392,6 +397,7 @@ class DevToolsFrameChild extends JSWindowActorChild {
// won't be created via the codepath. Except if we have a bfcache-in-parent navigation.
followWindowGlobalLifeCycle: true,
isTopLevelTarget,
ignoreSubFrames: isEveryFrameTargetEnabled,
});
targetActor.manage(targetActor);
targetActor.createdFromJsWindowActor = true;