Bug 921102 - 3 - Open/copy markup-view attribute links; r=bgrins

This part adds contextual menu items that become enabled when
the user right clicks on an attribute that has a link.
Depending on the nature of the link, a new tab will be opened or a node
selected.
The user can also choose to copy the link in the clipboard.

--HG--
extra : rebase_source : 00128a076003ebac34096d81d9e326bee6631259
extra : histedit_source : c1d67e84e9d57280040e5831233df523fff904e2
This commit is contained in:
Patrick Brosset 2015-05-02 22:37:45 +02:00
Родитель 10665e2af7
Коммит 3a5e7d4a71
7 изменённых файлов: 251 добавлений и 11 удалений

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

@ -692,6 +692,9 @@ InspectorPanel.prototype = {
unique.hidden = true;
}
// Enable/Disable the link open/copy items.
this._setupNodeLinkMenu();
// Enable the "edit HTML" item if the selection is an element and the root
// actor has the appropriate trait (isOuterHTMLEditable)
let editHTML = this.panelDoc.getElementById("node-menu-edithtml");
@ -750,6 +753,56 @@ InspectorPanel.prototype = {
}
},
/**
* Link menu items can be shown or hidden depending on the context and
* selected node, and their labels can vary.
*/
_setupNodeLinkMenu: function InspectorPanel_setupNodeLinkMenu() {
let linkSeparator = this.panelDoc.getElementById("node-menu-link-separator");
let linkFollow = this.panelDoc.getElementById("node-menu-link-follow");
let linkCopy = this.panelDoc.getElementById("node-menu-link-copy");
// Hide all by default.
linkSeparator.setAttribute("hidden", "true");
linkFollow.setAttribute("hidden", "true");
linkCopy.setAttribute("hidden", "true");
// Get information about the right-clicked node.
let popupNode = this.panelDoc.popupNode;
if (!popupNode || !popupNode.classList.contains("link")) {
return;
}
let type = popupNode.dataset.type;
// Bug 1158822 will make "resource" type URLs open in devtools, but for now
// they're considered like "uri".
if (type === "uri" || type === "resource") {
// First make sure the target can resolve relative URLs.
this.target.actorHasMethod("inspector", "resolveRelativeURL").then(canResolve => {
if (!canResolve) {
return;
}
linkSeparator.removeAttribute("hidden");
// Links can't be opened in new tabs in the browser toolbox.
if (!this.target.chrome) {
linkFollow.removeAttribute("hidden");
linkFollow.setAttribute("label", this.strings.GetStringFromName(
"inspector.menu.openUrlInNewTab.label"));
}
linkCopy.removeAttribute("hidden");
linkCopy.setAttribute("label", this.strings.GetStringFromName(
"inspector.menu.copyUrlToClipboard.label"));
}, console.error);
} else if (type === "idref") {
linkSeparator.removeAttribute("hidden");
linkFollow.removeAttribute("hidden");
linkFollow.setAttribute("label", this.strings.formatStringFromName(
"inspector.menu.selectElement.label", [popupNode.dataset.link], 1));
}
},
_initMarkup: function InspectorPanel_initMarkup() {
let doc = this.panelDoc;
@ -1037,6 +1090,51 @@ InspectorPanel.prototype = {
}
},
/**
* This method is here for the benefit of the node-menu-link-follow menu item
* in the inspector contextual-menu. It's behavior depends on which node was
* right-clicked when the menu was opened.
*/
followAttributeLink: function InspectorPanel_followLink(e) {
let type = this.panelDoc.popupNode.dataset.type;
let link = this.panelDoc.popupNode.dataset.link;
// "resource" type links should open appropriate tool instead (bug 1158822).
if (type === "uri" || type === "resource") {
// Open link in a new tab.
// When the inspector menu was setup on click (see _setupNodeLinkMenu), we
// already checked that resolveRelativeURL existed.
this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
let browserWin = this.target.tab.ownerDocument.defaultView;
browserWin.openUILinkIn(url, "tab");
}, console.error);
} else if (type == "idref") {
// Select the node in the same document.
this.walker.document(this.selection.nodeFront).then(doc => {
this.walker.querySelector(doc, "#" + CSS.escape(link)).then(node => {
if (!node) {
return;
}
this.selection.setNodeFront(node);
}, console.error);
}, console.error);
}
},
/**
* This method is here for the benefit of the node-menu-link-copy menu item
* in the inspector contextual-menu. It's behavior depends on which node was
* right-clicked when the menu was opened.
*/
copyAttributeLink: function InspectorPanel_copyLink(e) {
let link = this.panelDoc.popupNode.dataset.link;
// When the inspector menu was setup on click (see _setupNodeLinkMenu), we
// already checked that resolveRelativeURL existed.
this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
clipboardHelper.copyString(url);
}, console.error);
},
/**
* Trigger a high-priority layout change for things that need to be
* updated immediately

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

@ -91,11 +91,15 @@
label="&inspectorScrollNodeIntoView.label;"
accesskey="&inspectorScrollNodeIntoView.accesskey;"
oncommand="inspector.scrollNodeIntoView()"/>
<menuseparator/>
<menuitem id="node-menu-delete"
label="&inspectorHTMLDelete.label;"
accesskey="&inspectorHTMLDelete.accesskey;"
oncommand="inspector.deleteNode()"/>
<menuseparator id="node-menu-link-separator"/>
<menuitem id="node-menu-link-follow"
oncommand="inspector.followAttributeLink()"/>
<menuitem id="node-menu-link-copy"
oncommand="inspector.copyAttributeLink()"/>
<menuseparator/>
<menuitem id="node-menu-pseudo-hover"
label=":hover" type="checkbox"

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

@ -1815,16 +1815,6 @@ MarkupContainer.prototype = {
return;
}
// output-parser generated links handling.
if (target.nodeName === "a") {
event.stopPropagation();
event.preventDefault();
let browserWin = this.markup._inspector.target
.tab.ownerDocument.defaultView;
browserWin.openUILinkIn(target.href, "tab");
return;
}
// target is the MarkupContainer itself.
this._isMouseDown = true;
this.hovered = false;

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

@ -72,3 +72,36 @@ inspector.collapsePane=Collapse pane
# that expands the right panel (rules, computed, box-model, etc...) in the
# inspector UI.
inspector.expandPane=Expand pane
# LOCALIZATION NOTE (inspector.menu.openUrlInNewTab.label): This is the label of
# a menu item in the inspector contextual-menu that appears when the user right-
# clicks on the attribute of a node in the inspector that is a URL, and that
# allows to open that URL in a new tab.
inspector.menu.openUrlInNewTab.label=Open Link in New Tab
# LOCALIZATION NOTE (inspector.menu.copyUrlToClipboard.label): This is the label
# of a menu item in the inspector contextual-menu that appears when the user
# right-clicks on the attribute of a node in the inspector that is a URL, and
# that allows to copy that URL in the clipboard.
inspector.menu.copyUrlToClipboard.label=Copy Link Address
# LOCALIZATION NOTE (inspector.menu.openFileInDebugger.label): This is the label
# of a menu item in the inspector contextual-menu that appears when the user
# right-clicks on the attribute of a node in the inspector that is a URL to a
# javascript filename, and that allows to open the corresponding file in the
# debugger.
inspector.menu.openFileInDebugger.label=Open File in Debugger
# LOCALIZATION NOTE (inspector.menu.openFileInStyleEditor.label): This is the
# label of a menu item in the inspector contextual-menu that appears when the
# user right-clicks on the attribute of a node in the inspector that is a URL to
# a css filename, and that allows to open the corresponding file in the style
# editor.
inspector.menu.openFileInStyleEditor.label=Open File in Style-Editor
# LOCALIZATION NOTE (inspector.menu.selectElement.label): This is the label of a
# menu item in the inspector contextual-menu that appears when the user right-
# clicks on the attribute of a node in the inspector that is the ID of another
# element in the DOM (like with <label for="input-id">), and that allows to
# select that element in the inspector.
inspector.menu.selectElement.label=Select Element #%S

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

@ -3471,6 +3471,30 @@ var InspectorActor = exports.InspectorActor = protocol.ActorClass({
}, {
request: {url: Arg(0), maxDim: Arg(1, "nullable:number")},
response: RetVal("imageData")
}),
/**
* Resolve a URL to its absolute form, in the scope of a given content window.
* @param {String} url.
* @param {NodeActor} node If provided, the owner window of this node will be
* used to resolve the URL. Otherwise, the top-level content window will be
* used instead.
* @return {String} url.
*/
resolveRelativeURL: method(function(url, node) {
let document = isNodeDead(node)
? this.window.document
: nodeDocument(node.rawNode);
if (!document) {
return url;
} else {
let baseURI = Services.io.newURI(document.location.href, null, null);
return Services.io.newURI(url, null, baseURI).spec;
}
}, {
request: {url: Arg(0, "string"), node: Arg(1, "nullable:domnode")},
response: {value: RetVal("string")}
})
});

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

@ -63,6 +63,7 @@ skip-if = buildapp == 'mulet'
[test_inspector-release.html]
[test_inspector-reload.html]
[test_inspector-remove.html]
[test_inspector-resolve-url.html]
[test_inspector-retain.html]
[test_inspector-scroll-into-view.html]
[test_inspector-traversal.html]

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

@ -0,0 +1,90 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=921102
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 921102</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
<script type="application/javascript;version=1.8">
Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
const {Promise: promise} = Components.utils.import("resource://gre/modules/Promise.jsm", {});
const inspector = devtools.require("devtools/server/actors/inspector");
window.onload = function() {
SimpleTest.waitForExplicitFinish();
runNextTest();
}
var gInspector;
var gDoc;
addTest(function() {
let url = document.getElementById("inspectorContent").href;
attachURL(url, function(err, client, tab, doc) {
gDoc = doc;
let {InspectorFront} = devtools.require("devtools/server/actors/inspector");
gInspector = InspectorFront(client, tab);
runNextTest();
});
});
addTest(function() {
info("Resolve a relative URL without providing a context node");
gInspector.resolveRelativeURL("test.png?id=4#wow").then(url => {
is(url, "chrome://mochitests/content/chrome/toolkit/devtools/server/tests/" +
"mochitest/test.png?id=4#wow");
runNextTest();
});
});
addTest(function() {
info("Resolve an absolute URL without providing a context node");
gInspector.resolveRelativeURL("chrome://mochitests/content/chrome/toolkit/" +
"devtools/server/").then(url => {
is(url, "chrome://mochitests/content/chrome/toolkit/devtools/server/");
runNextTest();
});
});
addTest(function() {
info("Resolve a relative URL providing a context node");
let node = gDoc.querySelector(".big-horizontal");
gInspector.resolveRelativeURL("test.png?id=4#wow", node).then(url => {
is(url, "chrome://mochitests/content/chrome/toolkit/devtools/server/tests/" +
"mochitest/test.png?id=4#wow");
runNextTest();
});
});
addTest(function() {
info("Resolve an absolute URL providing a context node");
let node = gDoc.querySelector(".big-horizontal");
gInspector.resolveRelativeURL("chrome://mochitests/content/chrome/toolkit/" +
"devtools/server/", node).then(url => {
is(url, "chrome://mochitests/content/chrome/toolkit/devtools/server/");
runNextTest();
});
});
addTest(function() {
gInspector = gDoc = null;
runNextTest();
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=921102">Mozilla Bug 921102</a>
<a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>