Skip to content

Commit ad0f7f9

Browse files
committed
Bug 1989056 - [devtools] Set a fixed window on WindowGlobalTarget. r=devtools-reviewers,bomsy
Most targets are bound to a unique WindowGlobal (all but some related to the browser toolbox in the parent process). Given that accessing docShell.domWindow can easily on destruction, let's get a fixed reference to the window. The window itself may be throwing, but at least accessing to simple attributes should be functional. Also introduce a test to cover destroying iframes early after their creation and fix some leftover unhandled exceptions. Differential Revision: https://phabricator.services.mozilla.com/D267986
1 parent 2c578da commit ad0f7f9

File tree

5 files changed

+54
-18
lines changed

5 files changed

+54
-18
lines changed

devtools/client/framework/test/browser.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ skip-if = [
6565

6666
["browser_commands_from_url.js"]
6767

68+
["browser_destroying_iframes.js"]
69+
skip-if = [
70+
"asan", # Getting random unhandled exceptions which aren't crashing DevTools
71+
"tsan",
72+
]
73+
6874
["browser_devtools_api_destroy.js"]
6975

7076
["browser_dynamic_tool_enabling.js"]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/* Any copyright is dedicated to the Public Domain.
2+
* http://creativecommons.org/publicdomain/zero/1.0/ */
3+
"use strict";
4+
5+
// Testing that there's no breaking exception when destroying
6+
// an iframe early after its creation.
7+
8+
add_task(async function () {
9+
const { tab } = await openInspectorForURL("about:blank");
10+
const browser = tab.linkedBrowser;
11+
12+
// Create/remove an extra one now, after the load event.
13+
for (let i = 0; i < 10; i++) {
14+
await SpecialPowers.spawn(browser, [], async function () {
15+
const iframe = content.document.createElement("iframe");
16+
content.document.body.appendChild(iframe);
17+
await new Promise(res => (iframe.onload = res));
18+
iframe.remove();
19+
});
20+
}
21+
});

devtools/client/inspector/shared/walker-event-listener.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,17 @@ class WalkerEventListener {
6060
}
6161

6262
async _onTargetAvailable({ targetFront }) {
63-
const inspectorFront = await targetFront.getFront("inspector");
63+
let inspectorFront;
64+
try {
65+
inspectorFront = await targetFront.getFront("inspector");
66+
} catch (e) {
67+
// When iframes are destroyed early during their creation,
68+
// getFront method may throw
69+
if (targetFront.isDestroyed()) {
70+
return;
71+
}
72+
throw e;
73+
}
6474
// In case of multiple fast navigations, the front may already be destroyed,
6575
// in such scenario bail out and ignore this short lived target.
6676
if (inspectorFront.isDestroyed() || !this._listenerMap) {

devtools/server/actors/highlighters.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const registerHighlighter = (typeName, modulePath) => {
4848
* CustomHighlighterActor is a generic Actor that instantiates a custom implementation of
4949
* a highlighter class given its type name which must be registered in `highlighterTypes`.
5050
* CustomHighlighterActor proxies calls to methods of the highlighter class instance:
51-
* constructor(targetActor), show(node, options), hide(), destroy()
51+
* constructor(nargetActor), show(node, options), hide(), destroy()
5252
*/
5353
exports.CustomHighlighterActor = class CustomHighligherActor extends Actor {
5454
/**
@@ -308,7 +308,9 @@ class HighlighterEnvironment extends EventEmitter {
308308
if (this._targetActor && this._targetActor.isRootActor) {
309309
return this.window;
310310
}
311-
return this.docShell && this.docShell.chromeEventHandler;
311+
return (
312+
this._targetActor?.chromeEventHandler || this.docShell.chromeEventHandler
313+
);
312314
}
313315

314316
relayTargetEvent(name, data) {

devtools/server/actors/targets/window-global.js

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -373,14 +373,24 @@ class WindowGlobalTargetActor extends BaseTargetActor {
373373
writable: true,
374374
});
375375

376-
// When this target tracks only one WindowGlobal, set a fixed innerWindowId,
376+
// When this target tracks only one WindowGlobal, set a fixed innerWindowId and window,
377377
// so that it can easily be read safely while the related WindowGlobal is being destroyed.
378378
if (this.followWindowGlobalLifeCycle) {
379379
Object.defineProperty(this, "innerWindowId", {
380380
value: this.innerWindowId,
381381
configurable: false,
382382
writable: false,
383383
});
384+
Object.defineProperty(this, "window", {
385+
value: this.window,
386+
configurable: false,
387+
writable: false,
388+
});
389+
Object.defineProperty(this, "chromeEventHandler", {
390+
value: this.chromeEventHandler,
391+
configurable: false,
392+
writable: false,
393+
});
384394
}
385395

386396
// Save references to the original document we attached to
@@ -461,25 +471,12 @@ class WindowGlobalTargetActor extends BaseTargetActor {
461471
_targetScopedActorPool = null;
462472

463473
/**
464-
* An object on which listen for DOMWindowCreated and pageshow events.
474+
* A EventTarget object on which to listen for 'DOMWindowCreated' and 'pageshow' events.
465475
*/
466476
get chromeEventHandler() {
467477
return getDocShellChromeEventHandler(this.docShell);
468478
}
469479

470-
/**
471-
* Getter for the nsIMessageManager associated to the window global.
472-
*/
473-
get messageManager() {
474-
try {
475-
return this.docShell.messageManager;
476-
} catch (e) {
477-
// In some cases we can't get a docshell. We just have no message manager
478-
// then,
479-
return null;
480-
}
481-
}
482-
483480
/**
484481
* Getter for the list of all `docShell`s in the window global.
485482
* @return {Array}

0 commit comments

Comments
 (0)