зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1036324 - Adds option to walker.parents() to not traverse DocShellTreeItems of different types
This option helps avoiding blank inspector situations when trying to use the right-click ctx menu in the browser to inspect an element *while* the page is reloading.
This commit is contained in:
Родитель
cf897f2347
Коммит
05d8be44f6
|
@ -2146,7 +2146,7 @@ MarkupElementContainer.prototype = Heritage.extend(MarkupContainer.prototype, {
|
|||
this.tooltipData.data = promise.resolve(res);
|
||||
});
|
||||
}, () => {
|
||||
this.tooltipData.data = promise.reject();
|
||||
this.tooltipData.data = promise.resolve({});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -2166,9 +2166,11 @@ MarkupElementContainer.prototype = Heritage.extend(MarkupContainer.prototype, {
|
|||
}
|
||||
|
||||
return this.tooltipData.data.then(({data, size}) => {
|
||||
tooltip.setImageContent(data, size);
|
||||
}, () => {
|
||||
tooltip.setBrokenImageContent();
|
||||
if (data && size) {
|
||||
tooltip.setImageContent(data, size);
|
||||
} else {
|
||||
tooltip.setBrokenImageContent();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -68,6 +68,7 @@ skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
|
|||
skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
|
||||
[browser_markupview_events_jquery_2.1.1.js]
|
||||
skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
|
||||
[browser_markupview_load_01.js]
|
||||
[browser_markupview_html_edit_01.js]
|
||||
[browser_markupview_html_edit_02.js]
|
||||
[browser_markupview_html_edit_03.js]
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/* 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";
|
||||
|
||||
// Tests that selecting an element with the 'Inspect Element' context
|
||||
// menu during a page reload doesn't cause the markup view to become empty.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1036324
|
||||
|
||||
const server = createTestHTTPServer();
|
||||
|
||||
// Register a slow image handler so we can simulate a long time between
|
||||
// a reload and the load event firing.
|
||||
server.registerContentType("gif", "image/gif");
|
||||
server.registerPathHandler("/slow.gif", function (metadata, response) {
|
||||
info ("Image has been requested");
|
||||
response.processAsync();
|
||||
setTimeout(() => {
|
||||
info ("Image is responding");
|
||||
response.finish();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// Test page load events.
|
||||
const TEST_URL = "data:text/html," +
|
||||
"<!DOCTYPE html>" +
|
||||
"<head><meta charset='utf-8' /></head>" +
|
||||
"<body>" +
|
||||
"<p>Slow script</p>" +
|
||||
"<img src='http://localhost:" + server.identity.primaryPort + "/slow.gif' /></script>" +
|
||||
"</body>" +
|
||||
"</html>";
|
||||
|
||||
add_task(function*() {
|
||||
let tab = yield addTab(TEST_URL);
|
||||
let {inspector} = yield openInspector();
|
||||
let domContentLoaded = waitForLinkedBrowserEvent(tab, "DOMContentLoaded");
|
||||
let pageLoaded = waitForLinkedBrowserEvent(tab, "load");
|
||||
|
||||
ok (inspector.markup, "There is a markup view");
|
||||
|
||||
// Select an element while the tab is in the middle of a slow reload.
|
||||
reloadTab();
|
||||
yield domContentLoaded;
|
||||
yield chooseWithInspectElementContextMenu("img");
|
||||
yield pageLoaded;
|
||||
|
||||
yield inspector.once("markuploaded");
|
||||
ok (inspector.markup, "There is a markup view");
|
||||
is (inspector.markup._elt.children.length, 1, "The markup view is rendering");
|
||||
});
|
||||
|
||||
function* chooseWithInspectElementContextMenu(selector) {
|
||||
yield executeInContent("Test:SynthesizeMouse", {
|
||||
center: true,
|
||||
selector: selector,
|
||||
options: {type: "contextmenu", button: 2}
|
||||
});
|
||||
executeInContent("Test:SynthesizeKey", {key: "Q", options: {}});
|
||||
}
|
||||
|
||||
function waitForLinkedBrowserEvent(tab, event) {
|
||||
let def = promise.defer();
|
||||
tab.linkedBrowser.addEventListener(event, function cb() {
|
||||
tab.linkedBrowser.removeEventListener(event, cb, true);
|
||||
def.resolve();
|
||||
}, true);
|
||||
return def.promise;
|
||||
}
|
|
@ -172,6 +172,13 @@ function executeInContent(name, data={}, objects={}, expectResponse=true) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the current tab location.
|
||||
*/
|
||||
function reloadTab() {
|
||||
return executeInContent("devtools:test:reload", {}, {}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple DOM node accesor function that takes either a node or a string css
|
||||
* selector as argument and returns the corresponding node
|
||||
|
@ -647,3 +654,34 @@ function* waitForMultipleChildrenUpdates(inspector) {
|
|||
return yield waitForMultipleChildrenUpdates(inspector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an HTTP server that can be used to simulate custom requests within
|
||||
* a test. It is automatically cleaned up when the test ends, so no need to
|
||||
* call `destroy`.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Httpd.js/HTTP_server_for_unit_tests
|
||||
* for more information about how to register handlers.
|
||||
*
|
||||
* The server can be accessed like:
|
||||
*
|
||||
* const server = createTestHTTPServer();
|
||||
* let url = "http://localhost: " + server.identity.primaryPort + "/path";
|
||||
*
|
||||
* @returns {HttpServer}
|
||||
*/
|
||||
function createTestHTTPServer() {
|
||||
const {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
|
||||
let server = new HttpServer();
|
||||
|
||||
registerCleanupFunction(function* cleanup() {
|
||||
let destroyed = promise.defer();
|
||||
server.stop(() => {
|
||||
destroyed.resolve();
|
||||
});
|
||||
yield destroyed.promise;
|
||||
});
|
||||
|
||||
server.start(-1);
|
||||
return server;
|
||||
}
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
const Cu = Components.utils;
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
devtools.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise");
|
||||
devtools.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm", "Task");
|
||||
const loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader);
|
||||
let EventUtils = {};
|
||||
loader.loadSubScript("chrome://marionette/content/EventUtils.js", EventUtils);
|
||||
|
||||
addMessageListener("devtools:test:history", function ({ data }) {
|
||||
content.history[data.direction]();
|
||||
|
@ -189,6 +192,55 @@ addMessageListener("devtools:test:setAttribute", function(msg) {
|
|||
sendAsyncMessage("devtools:test:setAttribute");
|
||||
});
|
||||
|
||||
/**
|
||||
* Synthesize a mouse event on an element. This handler doesn't send a message
|
||||
* back. Consumers should listen to specific events on the inspector/highlighter
|
||||
* to know when the event got synthesized.
|
||||
* @param {Object} msg The msg.data part expects the following properties:
|
||||
* - {Number} x
|
||||
* - {Number} y
|
||||
* - {Boolean} center If set to true, x/y will be ignored and
|
||||
* synthesizeMouseAtCenter will be used instead
|
||||
* - {Object} options Other event options
|
||||
* - {String} selector An optional selector that will be used to find the node to
|
||||
* synthesize the event on, if msg.objects doesn't contain the CPOW.
|
||||
* The msg.objects part should be the element.
|
||||
* @param {Object} data Event detail properties:
|
||||
*/
|
||||
addMessageListener("Test:SynthesizeMouse", function(msg) {
|
||||
let {x, y, center, options, selector} = msg.data;
|
||||
let {node} = msg.objects;
|
||||
|
||||
if (!node && selector) {
|
||||
node = superQuerySelector(selector);
|
||||
}
|
||||
|
||||
if (center) {
|
||||
EventUtils.synthesizeMouseAtCenter(node, options, node.ownerDocument.defaultView);
|
||||
} else {
|
||||
EventUtils.synthesizeMouse(node, x, y, options, node.ownerDocument.defaultView);
|
||||
}
|
||||
|
||||
// Most consumers won't need to listen to this message, unless they want to
|
||||
// wait for the mouse event to be synthesized and don't have another event
|
||||
// to listen to instead.
|
||||
sendAsyncMessage("Test:SynthesizeMouse");
|
||||
});
|
||||
|
||||
/**
|
||||
* Synthesize a key event for an element. This handler doesn't send a message
|
||||
* back. Consumers should listen to specific events on the inspector/highlighter
|
||||
* to know when the event got synthesized.
|
||||
* @param {Object} msg The msg.data part expects the following properties:
|
||||
* - {String} key
|
||||
* - {Object} options
|
||||
*/
|
||||
addMessageListener("Test:SynthesizeKey", function(msg) {
|
||||
let {key, options} = msg.data;
|
||||
|
||||
EventUtils.synthesizeKey(key, options, content);
|
||||
});
|
||||
|
||||
/**
|
||||
* Like document.querySelector but can go into iframes too.
|
||||
* ".container iframe || .sub-container div" will first try to find the node
|
||||
|
|
|
@ -1410,6 +1410,8 @@ var WalkerActor = protocol.ActorClass({
|
|||
* Named options, including:
|
||||
* `sameDocument`: If true, parents will be restricted to the same
|
||||
* document as the node.
|
||||
* `sameTypeRootTreeItem`: If true, this will not traverse across
|
||||
* different types of docshells.
|
||||
*/
|
||||
parents: method(function(node, options={}) {
|
||||
if (isNodeDead(node)) {
|
||||
|
@ -1420,16 +1422,23 @@ var WalkerActor = protocol.ActorClass({
|
|||
let parents = [];
|
||||
let cur;
|
||||
while((cur = walker.parentNode())) {
|
||||
if (options.sameDocument && cur.ownerDocument != node.rawNode.ownerDocument) {
|
||||
if (options.sameDocument && nodeDocument(cur) != nodeDocument(node.rawNode)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (options.sameTypeRootTreeItem &&
|
||||
nodeDocshell(cur).sameTypeRootTreeItem != nodeDocshell(node.rawNode).sameTypeRootTreeItem) {
|
||||
break;
|
||||
}
|
||||
|
||||
parents.push(this._ref(cur));
|
||||
}
|
||||
return parents;
|
||||
}, {
|
||||
request: {
|
||||
node: Arg(0, "domnode"),
|
||||
sameDocument: Option(1)
|
||||
sameDocument: Option(1),
|
||||
sameTypeRootTreeItem: Option(1)
|
||||
},
|
||||
response: {
|
||||
nodes: RetVal("array:domnode")
|
||||
|
@ -3226,7 +3235,7 @@ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
|
|||
let nodeType = types.getType("domnode");
|
||||
let returnNode = nodeType.read(nodeType.write(nodeActor, walkerActor), this);
|
||||
let top = returnNode;
|
||||
let extras = walkerActor.parents(nodeActor);
|
||||
let extras = walkerActor.parents(nodeActor, {sameTypeRootTreeItem: true});
|
||||
for (let extraActor of extras) {
|
||||
top = nodeType.read(nodeType.write(extraActor, walkerActor), this);
|
||||
}
|
||||
|
@ -3519,6 +3528,16 @@ function nodeDocument(node) {
|
|||
return node.ownerDocument || (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
|
||||
}
|
||||
|
||||
function nodeDocshell(node) {
|
||||
let doc = node ? nodeDocument(node) : null;
|
||||
let win = doc ? doc.defaultView : null;
|
||||
if (win) {
|
||||
return win.
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDocShell);
|
||||
}
|
||||
}
|
||||
|
||||
function isNodeDead(node) {
|
||||
return !node || !node.rawNode || Cu.isDeadWrapper(node.rawNode);
|
||||
}
|
||||
|
|
|
@ -1020,7 +1020,8 @@ var StyleRuleActor = protocol.ActorClass({
|
|||
// Elements don't have a parent stylesheet, and therefore
|
||||
// don't have an associated URI. Provide a URI for
|
||||
// those.
|
||||
form.href = this.rawNode.ownerDocument.location.href;
|
||||
let doc = this.rawNode.ownerDocument;
|
||||
form.href = doc.location ? doc.location.href : "";
|
||||
form.cssText = this.rawStyle.cssText || "";
|
||||
break;
|
||||
case Ci.nsIDOMCSSRule.CHARSET_RULE:
|
||||
|
@ -1231,7 +1232,7 @@ var StyleRuleFront = protocol.FrontClass(StyleRuleActor, {
|
|||
return this._form.href;
|
||||
}
|
||||
let sheet = this.parentStyleSheet;
|
||||
return sheet.href;
|
||||
return sheet ? sheet.href : "";
|
||||
},
|
||||
|
||||
get nodeHref() {
|
||||
|
|
Загрузка…
Ссылка в новой задаче