mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 977043 - Tweak TabActor to support changing its targeted context to an iframe. r=bgrins,past
This commit is contained in:
parent
053c4183ad
commit
f369af1400
@ -225,6 +225,7 @@ const UnsolicitedNotifications = {
|
||||
"reflowActivity": "reflowActivity",
|
||||
"addonListChanged": "addonListChanged",
|
||||
"tabNavigated": "tabNavigated",
|
||||
"frameUpdate": "frameUpdate",
|
||||
"pageError": "pageError",
|
||||
"documentLoad": "documentLoad",
|
||||
"enteredFrame": "enteredFrame",
|
||||
|
@ -38,7 +38,7 @@ Object.defineProperty(ContentActor.prototype, "docShell", {
|
||||
return this._chromeGlobal.docShell;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
configurable: true
|
||||
});
|
||||
|
||||
ContentActor.prototype.exit = function() {
|
||||
|
@ -33,9 +33,11 @@ XPCOMUtils.defineLazyGetter(this, "StyleSheetActor", () => {
|
||||
return require("devtools/server/actors/stylesheets").StyleSheetActor;
|
||||
});
|
||||
|
||||
// Also depends on following symbols, shared by common scope with main.js:
|
||||
// DebuggerServer, CommonCreateExtraActors, CommonAppendExtraActors, ActorPool,
|
||||
// ThreadActor
|
||||
function getWindowID(window) {
|
||||
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.currentInnerWindowID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Browser-specific actors.
|
||||
@ -559,7 +561,7 @@ function TabActor(aConnection)
|
||||
shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
|
||||
});
|
||||
|
||||
this.traits = { reconfigure: true };
|
||||
this.traits = { reconfigure: true, frames: true };
|
||||
}
|
||||
|
||||
// XXX (bug 710213): TabActor attach/detach/exit/disconnect is a
|
||||
@ -620,7 +622,10 @@ TabActor.prototype = {
|
||||
|
||||
let docShells = [];
|
||||
while (docShellsEnum.hasMoreElements()) {
|
||||
docShells.push(docShellsEnum.getNext());
|
||||
let docShell = docShellsEnum.getNext();
|
||||
docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
docShells.push(docShell);
|
||||
}
|
||||
|
||||
return docShells;
|
||||
@ -807,11 +812,176 @@ TabActor.prototype = {
|
||||
this._pushContext();
|
||||
|
||||
this._progressListener = new DebuggerProgressListener(this);
|
||||
this._progressListener.watch(this.docShell);
|
||||
|
||||
// Save references to the original document we attached to
|
||||
this._originalWindow = this.window;
|
||||
|
||||
// Ensure replying to attach() request first
|
||||
// before notifying about new docshells.
|
||||
DevToolsUtils.executeSoon(() => this._watchDocshells());
|
||||
|
||||
this._attached = true;
|
||||
},
|
||||
|
||||
_watchDocshells: function BTA_watchDocshells() {
|
||||
// In child processes, we watch all docshells living in the process.
|
||||
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
||||
Services.obs.addObserver(this, "webnavigation-create", false);
|
||||
}
|
||||
Services.obs.addObserver(this, "webnavigation-destroy", false);
|
||||
|
||||
// We watch for all child docshells under the current document,
|
||||
this._progressListener.watch(this.docShell);
|
||||
|
||||
// And list all already existing ones.
|
||||
this._updateChildDocShells();
|
||||
},
|
||||
|
||||
onSwitchToFrame: function BTA_onSwitchToFrame(aRequest) {
|
||||
let windowId = aRequest.windowId;
|
||||
let win;
|
||||
try {
|
||||
win = Services.wm.getOuterWindowWithId(windowId);
|
||||
} catch(e) {}
|
||||
if (!win) {
|
||||
return { error: "noWindow",
|
||||
message: "The related docshell is destroyed or not found" };
|
||||
} else if (win == this.window) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Reply first before changing the document
|
||||
DevToolsUtils.executeSoon(() => this._changeTopLevelDocument(win));
|
||||
|
||||
return {};
|
||||
},
|
||||
|
||||
onListFrames: function BTA_onListFrames(aRequest) {
|
||||
let windows = this._docShellsToWindows(this.docShells);
|
||||
return { frames: windows };
|
||||
},
|
||||
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
// Ignore any event that comes before/after the tab actor is attached
|
||||
// That typically happens during firefox shutdown.
|
||||
if (!this.attached) {
|
||||
return;
|
||||
}
|
||||
if (aTopic == "webnavigation-create") {
|
||||
aSubject.QueryInterface(Ci.nsIDocShell);
|
||||
// webnavigation-create is fired very early during docshell construction.
|
||||
// In new root docshells within child processes, involving TabChild,
|
||||
// this event is from within this call:
|
||||
// http://hg.mozilla.org/mozilla-central/annotate/74d7fb43bb44/dom/ipc/TabChild.cpp#l912
|
||||
// whereas the chromeEventHandler (and most likely other stuff) is set later:
|
||||
// http://hg.mozilla.org/mozilla-central/annotate/74d7fb43bb44/dom/ipc/TabChild.cpp#l944
|
||||
// So wait a tick before watching it:
|
||||
DevToolsUtils.executeSoon(() => {
|
||||
// In child processes, we have new root docshells,
|
||||
// let's watch them and all their child docshells.
|
||||
if (this._isRootDocShell(aSubject)) {
|
||||
this._progressListener.watch(aSubject);
|
||||
}
|
||||
this._notifyDocShellsUpdate([aSubject]);
|
||||
});
|
||||
} else if (aTopic == "webnavigation-destroy") {
|
||||
let webProgress = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
this._notifyDocShellDestroy(webProgress);
|
||||
}
|
||||
},
|
||||
|
||||
_isRootDocShell: function (docShell) {
|
||||
// Root docshells like top level xul windows don't have chromeEventHandler.
|
||||
// Root docshells in child processes have one, it is TabChildGlobal,
|
||||
// which isn't a DOM Element.
|
||||
// Non-root docshell have a chromeEventHandler that is either
|
||||
// xul:iframe, xul:browser or html:iframe.
|
||||
return !docShell.chromeEventHandler ||
|
||||
!(docShell.chromeEventHandler instanceof Ci.nsIDOMElement);
|
||||
},
|
||||
|
||||
// Convert docShell list to windows objects list being sent to the client
|
||||
_docShellsToWindows: function (docshells) {
|
||||
return docshells.map(docShell => {
|
||||
let window = docShell.DOMWindow;
|
||||
let id = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
let parentID = undefined;
|
||||
// Ignore the parent of the original document on non-e10s firefox,
|
||||
// as we get the xul window as parent and don't care about it.
|
||||
if (window.parent && window != this._originalWindow) {
|
||||
parentID = window.parent
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
}
|
||||
return {
|
||||
id: id,
|
||||
url: window.location.href,
|
||||
title: window.title,
|
||||
parentID: parentID
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
_notifyDocShellsUpdate: function (docshells) {
|
||||
let windows = this._docShellsToWindows(docshells);
|
||||
this.conn.send({ from: this.actorID,
|
||||
type: "frameUpdate",
|
||||
frames: windows
|
||||
});
|
||||
},
|
||||
|
||||
_updateChildDocShells: function () {
|
||||
this._notifyDocShellsUpdate(this.docShells);
|
||||
},
|
||||
|
||||
_notifyDocShellDestroy: function (webProgress) {
|
||||
let id = webProgress.DOMWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
this.conn.send({ from: this.actorID,
|
||||
type: "frameUpdate",
|
||||
frames: [{
|
||||
id: id,
|
||||
destroy: true
|
||||
}]
|
||||
});
|
||||
|
||||
// Stop watching this docshell if it's a root one.
|
||||
// (child processes spawn new root docshells)
|
||||
webProgress.QueryInterface(Ci.nsIDocShell);
|
||||
if (this._isRootDocShell(webProgress)) {
|
||||
this._progressListener.unwatch(webProgress);
|
||||
}
|
||||
|
||||
if (webProgress.DOMWindow == this._originalWindow) {
|
||||
// If for some reason (typically during Firefox shutdown), the original
|
||||
// document is destroyed, we detach the tab actor to unregister all listeners
|
||||
// and prevent any exception.
|
||||
this.exit();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the currently targeted context is destroyed,
|
||||
// and we aren't on the top-level document,
|
||||
// we have to switch to the top-level one.
|
||||
if (webProgress.DOMWindow == this.window &&
|
||||
this.window != this._originalWindow) {
|
||||
this._changeTopLevelDocument(this._originalWindow);
|
||||
}
|
||||
},
|
||||
|
||||
_notifyDocShellDestroyAll: function () {
|
||||
this.conn.send({ from: this.actorID,
|
||||
type: "frameUpdate",
|
||||
destroyAll: true
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a thread actor and a pool for context-lifetime actors. It then sets
|
||||
* up the content window for debugging.
|
||||
@ -856,6 +1026,13 @@ TabActor.prototype = {
|
||||
}
|
||||
this._progressListener.destroy();
|
||||
this._progressListener = null;
|
||||
this._originalWindow = null;
|
||||
|
||||
// Removes the observers being set in _watchDocShells
|
||||
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
||||
Services.obs.removeObserver(this, "webnavigation-create", false);
|
||||
}
|
||||
Services.obs.removeObserver(this, "webnavigation-destroy", false);
|
||||
|
||||
this._popContext();
|
||||
|
||||
@ -1043,17 +1220,64 @@ TabActor.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_changeTopLevelDocument: function (window) {
|
||||
// Fake a will-navigate on the previous document
|
||||
// to let a chance to unregister it
|
||||
this._willNavigate(this.window, window.location.href, null, true);
|
||||
|
||||
this._windowDestroyed(this.window);
|
||||
|
||||
DevToolsUtils.executeSoon(() => {
|
||||
this._setWindow(window);
|
||||
|
||||
// Then fake window-ready and navigate on the given document
|
||||
this._windowReady(window, true);
|
||||
DevToolsUtils.executeSoon(() => {
|
||||
this._navigate(window, true);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_setWindow: function (window) {
|
||||
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
// Here is the very important call where we switch the currently
|
||||
// targeted context (it will indirectly update this.window and
|
||||
// many other attributes defined from docShell).
|
||||
Object.defineProperty(this, "docShell", {
|
||||
value: docShell,
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
events.emit(this, "changed-toplevel-document");
|
||||
let id = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
this.conn.send({ from: this.actorID,
|
||||
type: "frameUpdate",
|
||||
selected: id
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle location changes, by clearing the previous debuggees and enabling
|
||||
* debugging, which may have been disabled temporarily by the
|
||||
* DebuggerProgressListener.
|
||||
*/
|
||||
_windowReady: function (window) {
|
||||
_windowReady: function (window, isFrameSwitching = false) {
|
||||
let isTopLevel = window == this.window;
|
||||
|
||||
// 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) {
|
||||
this._updateChildDocShells();
|
||||
}
|
||||
|
||||
events.emit(this, "window-ready", {
|
||||
window: window,
|
||||
isTopLevel: isTopLevel
|
||||
isTopLevel: isTopLevel,
|
||||
id: getWindowID(window)
|
||||
});
|
||||
|
||||
// TODO bug 997119: move that code to ThreadActor by listening to window-ready
|
||||
@ -1062,9 +1286,11 @@ TabActor.prototype = {
|
||||
threadActor.clearDebuggees();
|
||||
if (threadActor.dbg) {
|
||||
threadActor.dbg.enabled = true;
|
||||
threadActor.global = window;
|
||||
threadActor.maybePauseOnExceptions();
|
||||
}
|
||||
// Update the global no matter if the debugger is on or off,
|
||||
// otherwise the global will be wrong when enabled later.
|
||||
threadActor.global = window;
|
||||
}
|
||||
|
||||
for (let sheetActor of this._styleSheetActors.values()) {
|
||||
@ -1080,19 +1306,37 @@ TabActor.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_windowDestroyed: function (window) {
|
||||
_windowDestroyed: function (window, id = null) {
|
||||
events.emit(this, "window-destroyed", {
|
||||
window: window,
|
||||
isTopLevel: window == this.window
|
||||
isTopLevel: window == this.window,
|
||||
id: id || getWindowID(window)
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Start notifying server codebase and client about a new document
|
||||
* Start notifying server and client about a new document
|
||||
* being loaded in the currently targeted context.
|
||||
*/
|
||||
_willNavigate: function (window, newURI, request) {
|
||||
_willNavigate: function (window, newURI, request, isFrameSwitching = false) {
|
||||
let isTopLevel = window == this.window;
|
||||
let reset = false;
|
||||
|
||||
if (window == this._originalWindow && !isFrameSwitching) {
|
||||
// Clear the iframe list if the original top-level document changes.
|
||||
this._notifyDocShellDestroyAll();
|
||||
|
||||
// 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 will-navigate event, we will dispatch on the old window.
|
||||
// (The inspector codebase expect to receive will-navigate for the currently
|
||||
// displayed document in order to cleanup the markup view)
|
||||
if (this.window != this._originalWindow) {
|
||||
reset=true;
|
||||
window = this.window;
|
||||
isTopLevel = true;
|
||||
}
|
||||
}
|
||||
|
||||
// will-navigate event needs to be dispatched synchronously,
|
||||
// by calling the listeners in the order or registration.
|
||||
@ -1128,15 +1372,20 @@ TabActor.prototype = {
|
||||
type: "tabNavigated",
|
||||
url: newURI,
|
||||
nativeConsoleAPI: true,
|
||||
state: "start"
|
||||
state: "start",
|
||||
isFrameSwitching: isFrameSwitching
|
||||
});
|
||||
|
||||
if (reset) {
|
||||
this._setWindow(this._originalWindow);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify server and client about a new document done loading in the current
|
||||
* targeted context.
|
||||
*/
|
||||
_navigate: function (window) {
|
||||
_navigate: function (window, isFrameSwitching = false) {
|
||||
let isTopLevel = window == this.window;
|
||||
|
||||
// navigate event needs to be dispatched synchronously,
|
||||
@ -1166,7 +1415,8 @@ TabActor.prototype = {
|
||||
url: this.url,
|
||||
title: this.title,
|
||||
nativeConsoleAPI: this.hasNativeConsoleAPI(this.window),
|
||||
state: "stop"
|
||||
state: "stop",
|
||||
isFrameSwitching: isFrameSwitching
|
||||
});
|
||||
},
|
||||
|
||||
@ -1222,7 +1472,9 @@ TabActor.prototype.requestTypes = {
|
||||
"detach": TabActor.prototype.onDetach,
|
||||
"reload": TabActor.prototype.onReload,
|
||||
"navigateTo": TabActor.prototype.onNavigateTo,
|
||||
"reconfigure": TabActor.prototype.onReconfigure
|
||||
"reconfigure": TabActor.prototype.onReconfigure,
|
||||
"switchToFrame": TabActor.prototype.onSwitchToFrame,
|
||||
"listFrames": TabActor.prototype.onListFrames
|
||||
};
|
||||
|
||||
exports.TabActor = TabActor;
|
||||
@ -1258,7 +1510,7 @@ Object.defineProperty(BrowserTabActor.prototype, "docShell", {
|
||||
return null;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
configurable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(BrowserTabActor.prototype, "title", {
|
||||
@ -1637,14 +1889,17 @@ DebuggerProgressListener.prototype = {
|
||||
// Dispatch the _windowReady event on the tabActor for pre-existing windows
|
||||
for (let win of this._getWindowsInDocShell(docShell)) {
|
||||
this._tabActor._windowReady(win);
|
||||
this._knownWindowIDs.set(this._getWindowID(win), win);
|
||||
this._knownWindowIDs.set(getWindowID(win), win);
|
||||
}
|
||||
},
|
||||
|
||||
unwatch: function(docShell) {
|
||||
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
webProgress.removeProgressListener(this);
|
||||
// During process shutdown, the docshell may already be cleaned up and throw
|
||||
try {
|
||||
webProgress.removeProgressListener(this);
|
||||
} catch(e) {}
|
||||
|
||||
// TODO: fix docShell.chromeEventHandler in child processes!
|
||||
let handler = docShell.chromeEventHandler ||
|
||||
@ -1656,7 +1911,7 @@ DebuggerProgressListener.prototype = {
|
||||
handler.removeEventListener("pagehide", this._onWindowHidden, true);
|
||||
|
||||
for (let win of this._getWindowsInDocShell(docShell)) {
|
||||
this._knownWindowIDs.delete(this._getWindowID(win));
|
||||
this._knownWindowIDs.delete(getWindowID(win));
|
||||
}
|
||||
},
|
||||
|
||||
@ -1675,12 +1930,6 @@ DebuggerProgressListener.prototype = {
|
||||
return windows;
|
||||
},
|
||||
|
||||
_getWindowID: function(window) {
|
||||
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.currentInnerWindowID;
|
||||
},
|
||||
|
||||
onWindowCreated: DevToolsUtils.makeInfallible(function(evt) {
|
||||
if (!this._tabActor.attached) {
|
||||
return;
|
||||
@ -1697,7 +1946,7 @@ DebuggerProgressListener.prototype = {
|
||||
this._tabActor._windowReady(window);
|
||||
|
||||
if (evt.type !== "pageshow") {
|
||||
this._knownWindowIDs.set(this._getWindowID(window), window);
|
||||
this._knownWindowIDs.set(getWindowID(window), window);
|
||||
}
|
||||
}, "DebuggerProgressListener.prototype.onWindowCreated"),
|
||||
|
||||
@ -1730,7 +1979,7 @@ DebuggerProgressListener.prototype = {
|
||||
let window = this._knownWindowIDs.get(innerID);
|
||||
if (window) {
|
||||
this._knownWindowIDs.delete(innerID);
|
||||
this._tabActor._windowDestroyed(window);
|
||||
this._tabActor._windowDestroyed(window, innerID);
|
||||
}
|
||||
}, "DebuggerProgressListener.prototype.observe"),
|
||||
|
||||
@ -1745,12 +1994,23 @@ DebuggerProgressListener.prototype = {
|
||||
let isDocument = aFlag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
|
||||
let isWindow = aFlag & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
|
||||
|
||||
// Catch any iframe location change
|
||||
if (isDocument && isStop) {
|
||||
// Watch document stop to ensure having the new iframe url.
|
||||
aProgress.QueryInterface(Ci.nsIDocShell);
|
||||
this._tabActor._notifyDocShellsUpdate([aProgress]);
|
||||
}
|
||||
|
||||
let window = aProgress.DOMWindow;
|
||||
if (isDocument && isStart) {
|
||||
// One of the earliest events that tells us a new URI
|
||||
// is being loaded in this window.
|
||||
let newURI = aRequest instanceof Ci.nsIChannel ? aRequest.URI.spec : null;
|
||||
this._tabActor._willNavigate(window, newURI, aRequest);
|
||||
}
|
||||
if (isWindow && isStop) {
|
||||
// Somewhat equivalent of load event.
|
||||
// (window.document.readyState == complete)
|
||||
this._tabActor._navigate(window);
|
||||
}
|
||||
}, "DebuggerProgressListener.prototype.onStateChange")
|
||||
|
Loading…
Reference in New Issue
Block a user