Bug 1007021 - The ReflowActor observes iframes and works after page navigations; r=bgrins r=ochameau

This commit is contained in:
Patrick Brosset 2014-06-03 09:51:55 +02:00
Родитель 8ce74c206d
Коммит edc963261f
11 изменённых файлов: 492 добавлений и 73 удалений

Просмотреть файл

@ -2,10 +2,15 @@
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
subsuite = devtools
support-files =
doc_layoutview_iframe1.html
doc_layoutview_iframe2.html
head.js
[browser_layoutview.js]
[browser_layoutview_rotate-labels-on-sides.js]
[browser_layoutview_update-after-navigation.js]
[browser_layoutview_update-after-reload.js]
[browser_layoutview_update-in-iframes.js]
[browser_editablemodel.js]
[browser_editablemodel_allproperties.js]
[browser_editablemodel_border.js]

Просмотреть файл

@ -0,0 +1,99 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the layout-view continues to work after a page navigation and that
// it also works after going back
let test = asyncTest(function*() {
yield addTab(TEST_URL_ROOT + "doc_layoutview_iframe1.html");
let {toolbox, inspector, view} = yield openLayoutView();
yield runTests(inspector, view);
yield destroyToolbox(inspector);
});
addTest("Test that the layout-view works on the first page",
function*(inspector, view) {
info("Selecting the test node");
yield selectNode("p", inspector);
info("Checking that the layout-view shows the right value");
let paddingElt = view.doc.querySelector(".padding.top > span");
is(paddingElt.textContent, "50");
info("Listening for layout-view changes and modifying the padding");
let onUpdated = waitForUpdate(inspector);
getNode("p").style.padding = "20px";
yield onUpdated;
ok(true, "Layout-view got updated");
info("Checking that the layout-view shows the right value after update");
is(paddingElt.textContent, "20");
});
addTest("Navigate to the second page",
function*(inspector, view) {
yield navigateTo(TEST_URL_ROOT + "doc_layoutview_iframe2.html");
yield inspector.once("markuploaded");
});
addTest("Test that the layout-view works on the second page",
function*(inspector, view) {
info("Selecting the test node");
yield selectNode("p", inspector);
info("Checking that the layout-view shows the right value");
let sizeElt = view.doc.querySelector(".size > span");
is(sizeElt.textContent, "100x100");
info("Listening for layout-view changes and modifying the size");
let onUpdated = waitForUpdate(inspector);
getNode("p").style.width = "200px";
yield onUpdated;
ok(true, "Layout-view got updated");
info("Checking that the layout-view shows the right value after update");
is(sizeElt.textContent, "200x100");
});
addTest("Go back to the first page",
function*(inspector, view) {
content.history.back();
yield inspector.once("markuploaded");
});
addTest("Test that the layout-view works on the first page after going back",
function*(inspector, view) {
info("Selecting the test node");
yield selectNode("p", inspector);
info("Checking that the layout-view shows the right value, which is the" +
"modified value from step one because of the bfcache");
let paddingElt = view.doc.querySelector(".padding.top > span");
is(paddingElt.textContent, "20");
info("Listening for layout-view changes and modifying the padding");
let onUpdated = waitForUpdate(inspector);
getNode("p").style.padding = "100px";
yield onUpdated;
ok(true, "Layout-view got updated");
info("Checking that the layout-view shows the right value after update");
is(paddingElt.textContent, "100");
});
function navigateTo(url) {
info("Navigating to " + url);
let def = promise.defer();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
info("URL " + url + " loading complete");
waitForFocus(def.resolve, content);
}, true);
content.location = url;
return def.promise;
}

Просмотреть файл

@ -0,0 +1,42 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the layout-view continues to work after the page is reloaded
let test = asyncTest(function*() {
yield addTab(TEST_URL_ROOT + "doc_layoutview_iframe1.html");
let {toolbox, inspector, view} = yield openLayoutView();
info("Test that the layout-view works on the first page");
yield assertLayoutView(inspector, view);
info("Reload the page");
content.location.reload();
yield inspector.once("markuploaded");
info("Test that the layout-view works on the reloaded page");
yield assertLayoutView(inspector, view);
yield destroyToolbox(inspector);
});
function* assertLayoutView(inspector, view) {
info("Selecting the test node");
yield selectNode("p", inspector);
info("Checking that the layout-view shows the right value");
let paddingElt = view.doc.querySelector(".padding.top > span");
is(paddingElt.textContent, "50");
info("Listening for layout-view changes and modifying the padding");
let onUpdated = waitForUpdate(inspector);
getNode("p").style.padding = "20px";
yield onUpdated;
ok(true, "Layout-view got updated");
info("Checking that the layout-view shows the right value after update");
is(paddingElt.textContent, "20");
}

Просмотреть файл

@ -0,0 +1,61 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the layout-view for elements within iframes also updates when they
// change
let test = asyncTest(function*() {
yield addTab(TEST_URL_ROOT + "doc_layoutview_iframe1.html");
let iframe2 = getNode("iframe").contentDocument.querySelector("iframe");
let {toolbox, inspector, view} = yield openLayoutView();
yield runTests(inspector, view, iframe2);
yield destroyToolbox(inspector);
});
addTest("Test that resizing an element in an iframe updates its box model",
function*(inspector, view, iframe2) {
info("Selecting the nested test node");
let node = iframe2.contentDocument.querySelector("div");
yield selectNode(node, inspector);
info("Checking that the layout-view shows the right value");
let sizeElt = view.doc.querySelector(".size > span");
is(sizeElt.textContent, "400x200");
info("Listening for layout-view changes and modifying its size");
let onUpdated = waitForUpdate(inspector);
node.style.width = "200px";
yield onUpdated;
ok(true, "Layout-view got updated");
info("Checking that the layout-view shows the right value after update");
is(sizeElt.textContent, "200x200");
});
addTest("Test reflows are still sent to the layout-view after deleting an iframe",
function*(inspector, view, iframe2) {
info("Deleting the iframe2");
iframe2.remove();
yield inspector.once("inspector-updated");
info("Selecting the test node in iframe1");
let node = getNode("iframe").contentDocument.querySelector("p");
yield selectNode(node, inspector);
info("Checking that the layout-view shows the right value");
let sizeElt = view.doc.querySelector(".size > span");
is(sizeElt.textContent, "100x100");
info("Listening for layout-view changes and modifying its size");
let onUpdated = waitForUpdate(inspector);
node.style.width = "200px";
yield onUpdated;
ok(true, "Layout-view got updated");
info("Checking that the layout-view shows the right value after update");
is(sizeElt.textContent, "200x100");
});

Просмотреть файл

@ -0,0 +1,3 @@
<!DOCTYPE html>
<p style="padding:50px;color:#f06;">Root page</p>
<iframe src="doc_layoutview_iframe2.html"></iframe>

Просмотреть файл

@ -0,0 +1,3 @@
<!DOCTYPE html>
<p style="width:100px;height:100px;background:red;">iframe 1</p>
<iframe src="data:text/html,<div style='width:400px;height:200px;background:yellow;'>iframe 2</div>"></iframe>

Просмотреть файл

@ -5,53 +5,47 @@
// This test makes sure that the style editor does not store any
// content CSS files in the permanent cache when opened from PB mode.
let gUI;
function test() {
waitForExplicitFinish();
let windowsToClose = [];
let gUI;
let testURI = 'http://' + TEST_HOST + '/browser/browser/devtools/styleeditor/test/test_private.html';
function checkCache() {
checkDiskCacheFor(TEST_HOST, function() {
gUI = null;
finish();
});
}
info("Opening a new private window");
let win = OpenBrowserWindow({private: true});
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);
executeSoon(startTest);
}, false);
function doTest(aWindow) {
aWindow.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
function startTest() {
win.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
win.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
info("Clearing the browser cache");
cache.clear();
openStyleEditorInWindow(aWindow, function(panel) {
info("Opening the style editor in the private window");
openStyleEditorInWindow(win, function(panel) {
gUI = panel.UI;
gUI.on("editor-added", onEditorAdded);
});
}, true);
aWindow.gBrowser.selectedBrowser.loadURI(testURI);
info("Loading the test URL in the new private window");
win.content.location = testURI;
}
function onEditorAdded(aEvent, aEditor) {
info("The style editor is ready")
aEditor.getSourceEditor().then(checkCache);
}
function testOnWindow(options, callback) {
let win = OpenBrowserWindow(options);
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);
windowsToClose.push(win);
executeSoon(function() callback(win));
}, false);
};
registerCleanupFunction(function() {
windowsToClose.forEach(function(win) {
function checkCache() {
checkDiskCacheFor(TEST_HOST, function() {
win.close();
win = null;
gUI = null;
finish();
});
});
testOnWindow({private: true}, function(win) {
doTest(win);
});
}
}

Просмотреть файл

@ -30,6 +30,7 @@ const protocol = require("devtools/server/protocol");
const {method, Arg, RetVal, types} = protocol;
const events = require("sdk/event/core");
const Heritage = require("sdk/core/heritage");
const {setTimeout, clearTimeout} = require("sdk/timers");
const EventEmitter = require("devtools/toolkit/event-emitter");
exports.register = function(handle) {
@ -148,7 +149,6 @@ exports.ReflowFront = protocol.FrontClass(ReflowActor, {
*/
function Observable(tabActor, callback) {
this.tabActor = tabActor;
this.win = tabActor.window;
this.callback = callback;
}
@ -199,7 +199,6 @@ Observable.prototype = {
destroy: function() {
this.stop();
this.callback = null;
this.win = null;
this.tabActor = null;
}
};
@ -237,6 +236,8 @@ function LayoutChangesObserver(tabActor) {
EventEmitter.decorate(this);
}
exports.LayoutChangesObserver = LayoutChangesObserver;
LayoutChangesObserver.prototype = Heritage.extend(Observable.prototype, {
/**
* How long does this observer waits before emitting a batched reflows event.
@ -281,12 +282,20 @@ LayoutChangesObserver.prototype = Heritage.extend(Observable.prototype, {
this.emit("reflows", this.reflows);
this.reflows = [];
}
this.eventLoopTimer = this.win.setTimeout(this._startEventLoop,
this.eventLoopTimer = this._setTimeout(this._startEventLoop,
this.EVENT_BATCHING_DELAY);
},
_stopEventLoop: function() {
this.win.clearTimeout(this.eventLoopTimer);
this._clearTimeout(this.eventLoopTimer);
},
// Exposing set/clearTimeout here to let tests override them if needed
_setTimeout: function(cb, ms) {
return setTimeout(cb, ms);
},
_clearTimeout: function(t) {
return clearTimeout(t);
},
/**
@ -362,21 +371,60 @@ exports.releaseLayoutChangesObserver = releaseLayoutChangesObserver;
*/
function ReflowObserver(tabActor, callback) {
Observable.call(this, tabActor, callback);
this.docshell = this.win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
this._onWindowReady = this._onWindowReady.bind(this);
events.on(this.tabActor, "window-ready", this._onWindowReady);
this._onWindowDestroyed = this._onWindowDestroyed.bind(this);
events.on(this.tabActor, "window-destroyed", this._onWindowDestroyed);
}
ReflowObserver.prototype = Heritage.extend(Observable.prototype, {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
Ci.nsISupportsWeakReference]),
_onWindowReady: function({window}) {
if (this.observing) {
this._startListeners([window]);
}
},
_onWindowDestroyed: function({window}) {
if (this.observing) {
this._stopListeners([window]);
}
},
_start: function() {
this.docshell.addWeakReflowObserver(this);
this._startListeners(this.tabActor.windows);
},
_stop: function() {
this.docshell.removeWeakReflowObserver(this);
if (this.tabActor.attached && this.tabActor.docShell) {
// It's only worth stopping if the tabActor is still attached
this._stopListeners(this.tabActor.windows);
}
},
_startListeners: function(windows) {
for (let window of windows) {
let docshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
docshell.addWeakReflowObserver(this);
}
},
_stopListeners: function(windows) {
for (let window of windows) {
// Corner cases where a global has already been freed may happen, in which
// case, no need to remove the observer
try {
let docshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
docshell.removeWeakReflowObserver(this);
} catch (e) {}
}
},
reflow: function(start, end) {
@ -388,7 +436,13 @@ ReflowObserver.prototype = Heritage.extend(Observable.prototype, {
},
destroy: function() {
if (this._isDestroyed) {
return;
}
this._isDestroyed = true;
events.off(this.tabActor, "window-ready", this._onWindowReady);
events.off(this.tabActor, "window-destroyed", this._onWindowDestroyed);
Observable.prototype.destroy.call(this);
this.docshell = null;
}
});

Просмотреть файл

@ -141,18 +141,52 @@ RootActor.prototype = {
*/
get window() Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType),
/**
* The list of all windows
*/
get windows() {
return this.docShells.map(docShell => {
return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
});
},
/**
* URL of the chrome window.
*/
get url() { return this.window ? this.window.document.location.href : null; },
/**
* The top level window's docshell
*/
get docShell() {
return this.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
},
/**
* The list of all docshells
*/
get docShells() {
let docShellsEnum = this.docShell.getDocShellEnumerator(
Ci.nsIDocShellTreeItem.typeAll,
Ci.nsIDocShell.ENUMERATE_FORWARDS
);
let docShells = [];
while (docShellsEnum.hasMoreElements()) {
docShells.push(docShellsEnum.getNext());
}
return docShells;
},
/**
* Getter for the best nsIWebProgress for to watching this window.
*/
get webProgress() {
return this.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
return this.docShell
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
},

Просмотреть файл

@ -13,7 +13,7 @@ let { RootActor } = require("devtools/server/actors/root");
let { AddonThreadActor, ThreadActor } = require("devtools/server/actors/script");
let { DebuggerServer } = require("devtools/server/main");
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
let { dbg_assert, dumpn } = DevToolsUtils;
let { dbg_assert } = DevToolsUtils;
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -567,6 +567,24 @@ TabActor.prototype = {
throw "The docShell getter should be implemented by a subclass of TabActor";
},
/**
* Getter for the list of all docshell in this tabActor
* @return {Array}
*/
get docShells() {
let docShellsEnum = this.docShell.getDocShellEnumerator(
Ci.nsIDocShellTreeItem.typeAll,
Ci.nsIDocShell.ENUMERATE_FORWARDS
);
let docShells = [];
while (docShellsEnum.hasMoreElements()) {
docShells.push(docShellsEnum.getNext());
}
return docShells;
},
/**
* Getter for the tab content's DOM window.
*/
@ -576,6 +594,17 @@ TabActor.prototype = {
.getInterface(Ci.nsIDOMWindow);
},
/**
* Getter for the list of all content DOM windows in this tabActor
* @return {Array}
*/
get windows() {
return this.docShells.map(docShell => {
return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
});
},
/**
* Getter for the nsIWebProgress for watching this window.
*/
@ -740,7 +769,7 @@ TabActor.prototype = {
/**
* Does the actual work of detaching from a tab.
*
* @returns false if the tab wasn't attached or true of detahing succeeds.
* @returns false if the tab wasn't attached or true of detaching succeeds.
*/
_detach: function BTA_detach() {
if (!this.attached) {
@ -752,6 +781,7 @@ TabActor.prototype = {
if (this.docShell) {
this._progressListener.unwatch(this.docShell);
}
this._progressListener.destroy();
this._progressListener = null;
this._popContext();
@ -945,7 +975,6 @@ TabActor.prototype = {
*/
_windowReady: function (window) {
let isTopLevel = window == this.window;
dumpn("window-ready: " + window.location + " isTopLevel:" + isTopLevel);
events.emit(this, "window-ready", {
window: window,
@ -970,6 +999,13 @@ TabActor.prototype = {
}
},
_windowDestroyed: function (window) {
events.emit(this, "window-destroyed", {
window: window,
isTopLevel: window == this.window
});
},
/**
* Start notifying server codebase and client about a new document
* being loaded in the currently targeted context.
@ -989,7 +1025,6 @@ TabActor.prototype = {
request: request
});
// We don't do anything for inner frames in TabActor.
// (we will only update thread actor on window-ready)
if (!isTopLevel) {
@ -1390,6 +1425,15 @@ BrowserAddonActor.prototype.requestTypes = {
function DebuggerProgressListener(aTabActor) {
this._tabActor = aTabActor;
this._onWindowCreated = this.onWindowCreated.bind(this);
this._onWindowHidden = this.onWindowHidden.bind(this);
// Watch for windows destroyed (global observer that will need filtering)
Services.obs.addObserver(this, "inner-window-destroyed", false);
// XXX: for now we maintain the list of windows we know about in this instance
// so that we can discriminate windows we care about when observing
// inner-window-destroyed events. Bug 1016952 would remove the need for this.
this._knownWindowIDs = new Map();
}
DebuggerProgressListener.prototype = {
@ -1399,7 +1443,13 @@ DebuggerProgressListener.prototype = {
Ci.nsISupports,
]),
watch: function DPL_watch(docShell) {
destroy: function() {
Services.obs.removeObserver(this, "inner-window-destroyed", false);
this._knownWindowIDs.clear();
this._knownWindowIDs = null;
},
watch: function(docShell) {
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATUS |
@ -1407,52 +1457,116 @@ DebuggerProgressListener.prototype = {
Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
// TODO: fix docShell.chromeEventHandler in child processes!
let chromeEventHandler = docShell.chromeEventHandler ||
docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
let handler = docShell.chromeEventHandler ||
docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
// Watch for globals being created in this docshell tree.
chromeEventHandler.addEventListener("DOMWindowCreated",
this._onWindowCreated, true);
chromeEventHandler.addEventListener("pageshow",
this._onWindowCreated, true);
handler.addEventListener("DOMWindowCreated", this._onWindowCreated, true);
handler.addEventListener("pageshow", this._onWindowCreated, true);
handler.addEventListener("pagehide", this._onWindowHidden, true);
// 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);
}
},
unwatch: function DPL_unwatch(docShell) {
unwatch: function(docShell) {
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
webProgress.removeProgressListener(this);
// TODO: fix docShell.chromeEventHandler in child processes!
let chromeEventHandler = docShell.chromeEventHandler ||
docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
chromeEventHandler.removeEventListener("DOMWindowCreated",
this._onWindowCreated, true);
chromeEventHandler.removeEventListener("pageshow",
this._onWindowCreated, true);
let handler = docShell.chromeEventHandler ||
docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
handler.removeEventListener("DOMWindowCreated", this._onWindowCreated, true);
handler.removeEventListener("pageshow", this._onWindowCreated, true);
handler.removeEventListener("pagehide", this._onWindowHidden, true);
for (let win of this._getWindowsInDocShell(docShell)) {
this._knownWindowIDs.delete(this._getWindowID(win));
}
},
onWindowCreated:
DevToolsUtils.makeInfallible(function DPL_onWindowCreated(evt) {
// Ignore any event if the tab actor isn't attached.
_getWindowsInDocShell: function(docShell) {
let docShellsEnum = docShell.getDocShellEnumerator(
Ci.nsIDocShellTreeItem.typeAll,
Ci.nsIDocShell.ENUMERATE_FORWARDS
);
let windows = [];
while (docShellsEnum.hasMoreElements()) {
let w = docShellsEnum.getNext().QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
windows.push(w);
}
return windows;
},
_getWindowID: function(window) {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.currentInnerWindowID;
},
onWindowCreated: DevToolsUtils.makeInfallible(function(evt) {
if (!this._tabActor.attached) {
return;
}
// pageshow events for non-persisted pages have already been handled by a
// prior DOMWindowCreated event.
// prior DOMWindowCreated event. For persisted pages, act as if the window
// had just been created since it's been unfrozen from bfcache.
if (evt.type == "pageshow" && !evt.persisted) {
return;
}
let window = evt.target.defaultView;
this._tabActor._windowReady(window);
if (evt.type !== "pageshow") {
this._knownWindowIDs.set(this._getWindowID(window), window);
}
}, "DebuggerProgressListener.prototype.onWindowCreated"),
onWindowHidden: DevToolsUtils.makeInfallible(function(evt) {
if (!this._tabActor.attached) {
return;
}
// Only act as if the window has been destroyed if the 'pagehide' event
// was sent for a persisted window (persisted is set when the page is put
// and frozen in the bfcache). If the page isn't persisted, the observer's
// inner-window-destroyed event will handle it.
if (!evt.persisted) {
return;
}
let window = evt.target.defaultView;
this._tabActor._windowDestroyed(window);
}, "DebuggerProgressListener.prototype.onWindowHidden"),
observe: DevToolsUtils.makeInfallible(function(subject, topic) {
if (!this._tabActor.attached) {
return;
}
// Because this observer will be called for all inner-window-destroyed in
// the application, we need to filter out events for windows we are not
// watching
let innerID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
let window = this._knownWindowIDs.get(innerID);
if (window) {
this._knownWindowIDs.delete(innerID);
this._tabActor._windowDestroyed(window);
}
}, "DebuggerProgressListener.prototype.observe"),
onStateChange:
DevToolsUtils.makeInfallible(function DPL_onStateChange(aProgress, aRequest, aFlag, aStatus) {
// Ignore any event if the tab actor isn't attached.
DevToolsUtils.makeInfallible(function(aProgress, aRequest, aFlag, aStatus) {
if (!this._tabActor.attached) {
return;
}

Просмотреть файл

@ -5,13 +5,22 @@
let {
getLayoutChangesObserver,
releaseLayoutChangesObserver
releaseLayoutChangesObserver,
LayoutChangesObserver
} = devtools.require("devtools/server/actors/layout");
// Override set/clearTimeout on LayoutChangesObserver to avoid depending on
// time in this unit test. This means that LayoutChangesObserver.eventLoopTimer
// will be the timeout callback instead of the timeout itself, so test cases
// will need to execute it to fake a timeout
LayoutChangesObserver.prototype._setTimeout = cb => cb;
LayoutChangesObserver.prototype._clearTimeout = function() {};
// Mock the tabActor since we only really want to test the LayoutChangesObserver
// and don't want to depend on a window object, nor want to test protocol.js
function MockTabActor() {
this.window = new MockWindow();
this.windows = [this.window];
}
function MockWindow() {}
@ -22,7 +31,9 @@ MockWindow.prototype = {
getInterface: function() {
return {
QueryInterface: function() {
self.docShell = new MockDocShell();
if (!self.docShell) {
self.docShell = new MockDocShell();
}
return self.docShell;
}
};
@ -91,7 +102,6 @@ function eventsAreBatched() {
// Note that in this test, we mock the TabActor and its window property, so we
// also mock the setTimeout/clearTimeout mechanism and just call the callback
// manually
let tabActor = new MockTabActor();
let observer = getLayoutChangesObserver(tabActor);