Bug 964014 - Adds copy-image-data-uri option to images in the markup-view, r=harth

This commit is contained in:
Patrick Brosset
2014-01-30 17:33:53 +01:00
parent 2cc8877986
commit dd27ff1d92
7 changed files with 245 additions and 23 deletions

View File

@@ -549,11 +549,13 @@ InspectorPanel.prototype = {
* Disable the delete item if needed. Update the pseudo classes.
*/
_setupNodeMenu: function InspectorPanel_setupNodeMenu() {
let isSelectionElement = this.selection.isElementNode();
// Set the pseudo classes
for (let name of ["hover", "active", "focus"]) {
let menu = this.panelDoc.getElementById("node-menu-pseudo-" + name);
if (this.selection.isElementNode()) {
if (isSelectionElement) {
let checked = this.selection.nodeFront.hasPseudoClassLock(":" + name);
menu.setAttribute("checked", checked);
menu.removeAttribute("disabled");
@@ -575,8 +577,7 @@ InspectorPanel.prototype = {
let unique = this.panelDoc.getElementById("node-menu-copyuniqueselector");
let copyInnerHTML = this.panelDoc.getElementById("node-menu-copyinner");
let copyOuterHTML = this.panelDoc.getElementById("node-menu-copyouter");
let selectionIsElement = this.selection.isElementNode();
if (selectionIsElement) {
if (isSelectionElement) {
unique.removeAttribute("disabled");
copyInnerHTML.removeAttribute("disabled");
copyOuterHTML.removeAttribute("disabled");
@@ -586,12 +587,24 @@ InspectorPanel.prototype = {
copyOuterHTML.setAttribute("disabled", "true");
}
// 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");
if (this.isOuterHTMLEditable && selectionIsElement) {
if (this.isOuterHTMLEditable && isSelectionElement) {
editHTML.removeAttribute("disabled");
} else {
editHTML.setAttribute("disabled", "true");
}
// Enable the "copy image data-uri" item if the selection is previewable
// which essentially checks if it's an image or canvas tag
let copyImageData = this.panelDoc.getElementById("node-menu-copyimagedatauri");
let markupContainer = this.markup.getContainer(this.selection.nodeFront);
if (markupContainer && markupContainer.isPreviewable()) {
copyImageData.removeAttribute("disabled");
} else {
copyImageData.setAttribute("disabled", "true");
}
},
_resetNodeMenu: function InspectorPanel_resetNodeMenu() {
@@ -717,7 +730,19 @@ InspectorPanel.prototype = {
this._copyLongStr(this.walker.outerHTML(this.selection.nodeFront));
},
_copyLongStr: function(promise) {
/**
* Copy the data-uri for the currently selected image in the clipboard.
*/
copyImageDataUri: function InspectorPanel_copyImageDataUri()
{
let container = this.markup.getContainer(this.selection.nodeFront);
if (container && container.isPreviewable()) {
container.copyImageDataUri();
}
},
_copyLongStr: function InspectorPanel_copyLongStr(promise)
{
return promise.then(longstr => {
return longstr.string().then(toCopy => {
longstr.release().then(null, console.error);

View File

@@ -52,6 +52,9 @@
label="&inspectorCopyUniqueSelector.label;"
accesskey="&inspectorCopyUniqueSelector.accesskey;"
oncommand="inspector.copyUniqueSelector()"/>
<menuitem id="node-menu-copyimagedatauri"
label="&inspectorCopyImageDataUri.label;"
oncommand="inspector.copyImageDataUri()"/>
<menuseparator/>
<menuitem id="node-menu-delete"
label="&inspectorHTMLDelete.label;"

View File

@@ -28,6 +28,7 @@ const EventEmitter = require("devtools/shared/event-emitter");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/Templater.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
loader.lazyGetter(this, "DOMParser", function() {
return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
@@ -1279,20 +1280,27 @@ MarkupContainer.prototype = {
return "[MarkupContainer for " + this.node + "]";
},
_prepareImagePreview: function() {
isPreviewable: function() {
if (this.node.tagName) {
let tagName = this.node.tagName.toLowerCase();
let srcAttr = this.editor.getAttributeElement("src");
let isImage = tagName === "img" && srcAttr;
let isCanvas = tagName === "canvas";
return isImage || isCanvas;
} else {
return false;
}
},
_prepareImagePreview: function() {
if (this.isPreviewable()) {
// Get the image data for later so that when the user actually hovers over
// the element, the tooltip does contain the image
if (isImage || isCanvas) {
let def = promise.defer();
this.tooltipData = {
target: isImage ? srcAttr : this.editor.tag,
target: this.editor.getAttributeElement("src") || this.editor.tag,
data: def.promise
};
@@ -1308,7 +1316,18 @@ MarkupContainer.prototype = {
}
});
}
},
copyImageDataUri: function() {
// We need to send again a request to gettooltipData even if one was sent for
// the tooltip, because we want the full-size image
this.node.getImageData().then(data => {
if (data) {
data.data.string().then(str => {
clipboardHelper.copyString(str, this.markup.doc);
});
}
});
},
_buildTooltipContent: function(target, tooltip) {
@@ -2068,3 +2087,8 @@ function parseAttributeValues(attr, doc) {
loader.lazyGetter(MarkupView.prototype, "strings", () => Services.strings.createBundle(
"chrome://browser/locale/devtools/inspector.properties"
));
XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
return Cc["@mozilla.org/widget/clipboardhelper;1"].
getService(Ci.nsIClipboardHelper);
});

View File

@@ -21,3 +21,4 @@ skip-if = true
[browser_inspector_markup_subset.js]
[browser_inspector_markup_765105_tooltip.js]
[browser_inspector_markup_950732.js]
[browser_inspector_markup_964014_copy_image_data.js]

File diff suppressed because one or more lines are too long

View File

@@ -7,6 +7,7 @@ const Cu = Components.utils;
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let TargetFactory = devtools.TargetFactory;
let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
let promise = devtools.require("sdk/core/promise");
// Clear preferences that may be set during the course of tests.
function clearUserPrefs() {
@@ -27,3 +28,38 @@ function getContainerForRawNode(markupView, rawNode) {
let container = markupView.getContainer(front);
return container;
}
/**
* Open the toolbox, with the inspector tool visible.
* @return a promise that resolves when the inspector is ready
*/
function openInspector() {
let deferred = promise.defer();
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
let inspector = toolbox.getCurrentPanel();
inspector.once("inspector-updated", () => {
deferred.resolve(inspector, toolbox);
});
}).then(null, console.error);
return deferred.promise;
}
/**
* Set the inspector's current selection to the first match of the given css
* selector
* @return a promise that resolves when the inspector is updated with the new
* node
*/
function selectNode(selector, inspector) {
let deferred = promise.defer();
let node = content.document.querySelector(selector);
ok(node, "A node was found for selector " + selector + ". Selecting it now");
inspector.selection.setNode(node, "test");
inspector.once("inspector-updated", () => {
deferred.resolve(node);
});
return deferred.promise;
}

View File

@@ -17,3 +17,5 @@
<!ENTITY inspectorSearchHTML.label "Search HTML">
<!ENTITY inspectorSearchHTML.key "F">
<!ENTITY inspectorCopyImageDataUri.label "Copy Image Data-URL">