mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-15 22:44:13 +00:00
Bug 1040653 - Make eyedropper work with e10s. r=mratcliffe
This commit is contained in:
parent
a63486fa3f
commit
c2a7709c61
20
browser/devtools/eyedropper/eyedropper-child.js
Normal file
20
browser/devtools/eyedropper/eyedropper-child.js
Normal file
@ -0,0 +1,20 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
addMessageListener("Eyedropper:RequestContentScreenshot", sendContentScreenshot);
|
||||
|
||||
function sendContentScreenshot() {
|
||||
let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
let width = content.innerWidth;
|
||||
let height = content.innerHeight;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
canvas.mozOpaque = true;
|
||||
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
ctx.drawWindow(content, content.scrollX, content.scrollY, width, height, "#fff");
|
||||
|
||||
sendAsyncMessage("Eyedropper:Screenshot", canvas.toDataURL());
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
const {Cc, Ci, Cu} = require("chrome");
|
||||
const {rgbToHsl} = require("devtools/css-color").colorUtils;
|
||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
|
||||
const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
@ -112,6 +113,8 @@ function Eyedropper(chromeWindow, opts = { copyOnSelect: true }) {
|
||||
this._chromeWindow = chromeWindow;
|
||||
this._chromeDocument = chromeWindow.document;
|
||||
|
||||
this._OS = XULRuntime.OS;
|
||||
|
||||
this._dragging = true;
|
||||
this.loaded = false;
|
||||
|
||||
@ -126,6 +129,10 @@ function Eyedropper(chromeWindow, opts = { copyOnSelect: true }) {
|
||||
width: CANVAS_WIDTH, // width of canvas to draw zoomed area onto
|
||||
height: CANVAS_WIDTH // height of canvas
|
||||
};
|
||||
|
||||
let mm = this._contentTab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript("resource:///modules/devtools/eyedropper/eyedropper-child.js", true);
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
@ -167,6 +174,30 @@ Eyedropper.prototype = {
|
||||
return rgb;
|
||||
},
|
||||
|
||||
get _contentTab() {
|
||||
return this._chromeWindow.gBrowser.selectedTab;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch a screenshot of the content.
|
||||
*
|
||||
* @return {promise}
|
||||
* Promise that resolves with the screenshot as a dataURL
|
||||
*/
|
||||
getContentScreenshot: function() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let mm = this._contentTab.linkedBrowser.messageManager;
|
||||
function onScreenshot(message) {
|
||||
mm.removeMessageListener("Eyedropper:Screenshot", onScreenshot);
|
||||
deferred.resolve(message.data);
|
||||
}
|
||||
mm.addMessageListener("Eyedropper:Screenshot", onScreenshot);
|
||||
mm.sendAsyncMessage("Eyedropper:RequestContentScreenshot");
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the eyedropper. Add listeners for a mouse move in the window to
|
||||
* show the eyedropper.
|
||||
@ -174,15 +205,31 @@ Eyedropper.prototype = {
|
||||
open: function() {
|
||||
if (this.isOpen) {
|
||||
// the eyedropper is aready open, don't create another panel.
|
||||
return;
|
||||
return promise.resolve();
|
||||
}
|
||||
let deferred = promise.defer();
|
||||
|
||||
this.isOpen = true;
|
||||
|
||||
this._OS = XULRuntime.OS;
|
||||
|
||||
this._chromeDocument.addEventListener("mousemove", this._onFirstMouseMove);
|
||||
|
||||
this._showCrosshairs();
|
||||
|
||||
// Get screenshot of content so we can inspect colors
|
||||
this.getContentScreenshot().then((dataURL) => {
|
||||
this._contentImage = new this._chromeWindow.Image();
|
||||
this._contentImage.src = dataURL;
|
||||
|
||||
// Wait for screenshot to load
|
||||
this._contentImage.onload = () => {
|
||||
// Then start showing the eyedropper UI
|
||||
this._chromeDocument.addEventListener("mousemove", this._onFirstMouseMove);
|
||||
deferred.resolve();
|
||||
|
||||
this.isStarted = true;
|
||||
this.emit("started");
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -209,6 +256,25 @@ Eyedropper.prototype = {
|
||||
this._hideCursor();
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether the coordinates are over the content or chrome.
|
||||
*
|
||||
* @param {number} clientX
|
||||
* x-coordinate of mouse relative to browser window.
|
||||
* @param {number} clientY
|
||||
* y-coordinate of mouse relative to browser window.
|
||||
*/
|
||||
_isInContent: function(clientX, clientY) {
|
||||
let box = this._contentTab.linkedBrowser.getBoundingClientRect();
|
||||
if (clientX > box.left &&
|
||||
clientX < box.right &&
|
||||
clientY > box.top &&
|
||||
clientY < box.bottom) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the current coordinates to inspect from where a mousemove originated.
|
||||
*
|
||||
@ -216,21 +282,23 @@ Eyedropper.prototype = {
|
||||
* Event for the mouse move.
|
||||
*/
|
||||
_setCoordinates: function(event) {
|
||||
let inContent = this._isInContent(event.clientX, event.clientY);
|
||||
let win = this._chromeWindow;
|
||||
|
||||
let x, y;
|
||||
if (this._OS == "Linux") {
|
||||
// event.clientX is off on Linux, so calculate it by hand
|
||||
let windowX = win.screenX + (win.outerWidth - win.innerWidth);
|
||||
x = event.screenX - windowX;
|
||||
// offset of mouse from browser window
|
||||
let x = event.clientX;
|
||||
let y = event.clientY;
|
||||
|
||||
let windowY = win.screenY + (win.outerHeight - win.innerHeight);
|
||||
y = event.screenY - windowY;
|
||||
}
|
||||
else {
|
||||
x = event.clientX;
|
||||
y = event.clientY;
|
||||
if (inContent) {
|
||||
// calculate the offset of the mouse from the content window
|
||||
let box = this._contentTab.linkedBrowser.getBoundingClientRect();
|
||||
x = x - box.left;
|
||||
y = y - box.top;
|
||||
|
||||
this._zoomArea.contentWidth = box.width;
|
||||
this._zoomArea.contentHeight = box.height;
|
||||
}
|
||||
this._zoomArea.inContent = inContent;
|
||||
|
||||
// don't let it inspect outside the browser window
|
||||
x = Math.max(0, Math.min(x, win.outerWidth - 1));
|
||||
@ -548,29 +616,66 @@ Eyedropper.prototype = {
|
||||
* Draw the inspected area onto the canvas using the zoom level.
|
||||
*/
|
||||
_drawWindow: function() {
|
||||
let { width, height, x, y } = this._zoomArea;
|
||||
let { width, height, x, y, inContent,
|
||||
contentWidth, contentHeight } = this._zoomArea;
|
||||
|
||||
let zoomedWidth = width / this.zoom;
|
||||
let zoomedHeight = height / this.zoom;
|
||||
|
||||
let drawX = x - (zoomedWidth / 2);
|
||||
let drawY = y - (zoomedHeight / 2);
|
||||
let leftX = x - (zoomedWidth / 2);
|
||||
let topY = y - (zoomedHeight / 2);
|
||||
|
||||
// draw the portion of the window we're inspecting
|
||||
this._ctx.drawWindow(this._chromeWindow, drawX, drawY, zoomedWidth,
|
||||
zoomedHeight, "white");
|
||||
if (inContent) {
|
||||
// draw from content source image "s" to destination rect "d"
|
||||
let sx = leftX;
|
||||
let sy = topY;
|
||||
let sw = zoomedWidth;
|
||||
let sh = zoomedHeight;
|
||||
let dx = 0;
|
||||
let dy = 0;
|
||||
|
||||
// we're at the content edge, so we have to crop the drawing
|
||||
if (leftX < 0) {
|
||||
sx = 0;
|
||||
sw = zoomedWidth + leftX;
|
||||
dx = -leftX;
|
||||
}
|
||||
else if (leftX + zoomedWidth > contentWidth) {
|
||||
sw = contentWidth - leftX;
|
||||
}
|
||||
if (topY < 0) {
|
||||
sy = 0;
|
||||
sh = zoomedHeight + topY;
|
||||
dy = -topY;
|
||||
}
|
||||
else if (topY + zoomedHeight > contentHeight) {
|
||||
sh = contentHeight - topY;
|
||||
}
|
||||
let dw = sw;
|
||||
let dh = sh;
|
||||
|
||||
// we don't want artifacts when we're inspecting the edges of content
|
||||
if (leftX < 0 || topY < 0 ||
|
||||
leftX + zoomedWidth > contentWidth ||
|
||||
topY + zoomedHeight > contentHeight) {
|
||||
this._ctx.fillStyle = "white";
|
||||
this._ctx.fillRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
// draw from the screenshot to the eyedropper canvas
|
||||
this._ctx.drawImage(this._contentImage, sx, sy, sw,
|
||||
sh, dx, dy, dw, dh);
|
||||
}
|
||||
else {
|
||||
// the mouse is over the chrome, so draw that instead of the content
|
||||
this._ctx.drawWindow(this._chromeWindow, leftX, topY, zoomedWidth,
|
||||
zoomedHeight, "white");
|
||||
}
|
||||
|
||||
// now scale it
|
||||
let sx = 0;
|
||||
let sy = 0;
|
||||
let sw = zoomedWidth;
|
||||
let sh = zoomedHeight;
|
||||
let dx = 0;
|
||||
let dy = 0;
|
||||
let dw = width;
|
||||
let dh = height;
|
||||
|
||||
this._ctx.drawImage(this._canvas, sx, sy, sw, sh, dx, dy, dw, dh);
|
||||
this._ctx.drawImage(this._canvas, 0, 0, zoomedWidth, zoomedHeight,
|
||||
0, 0, width, height);
|
||||
|
||||
let rgb = this.centerColor;
|
||||
this._colorPreview.style.backgroundColor = toColorString(rgb, "rgb");
|
||||
@ -635,6 +740,7 @@ Eyedropper.prototype = {
|
||||
this._removePanelListeners();
|
||||
this._removeListeners();
|
||||
|
||||
this.isStarted = false;
|
||||
this.isOpen = false;
|
||||
this._isSelecting = false;
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
EXTRA_JS_MODULES.devtools.eyedropper += [
|
||||
'commands.js',
|
||||
'eyedropper-child.js',
|
||||
'eyedropper.js'
|
||||
]
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
[DEFAULT]
|
||||
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
color-block.html
|
||||
|
@ -10,53 +10,69 @@ const DIV_COLOR = "#0000FF";
|
||||
* - Opening eyedropper and pressing ESC closes the eyedropper
|
||||
* - Opening eyedropper and clicking copies the center color
|
||||
*/
|
||||
function test() {
|
||||
addTab(TESTCASE_URI).then(testEscape);
|
||||
}
|
||||
let test = asyncTest(function*() {
|
||||
yield addTab(TESTCASE_URI);
|
||||
|
||||
function testEscape() {
|
||||
info("added tab");
|
||||
|
||||
yield testEscape();
|
||||
|
||||
info("testing selecting a color");
|
||||
|
||||
yield testSelect();
|
||||
});
|
||||
|
||||
function* testEscape() {
|
||||
let dropper = new Eyedropper(window);
|
||||
|
||||
dropper.once("destroy", (event) => {
|
||||
ok(true, "escape closed the eyedropper");
|
||||
yield inspectPage(dropper, false);
|
||||
|
||||
// now test selecting a color
|
||||
testSelect();
|
||||
});
|
||||
let destroyed = dropper.once("destroy");
|
||||
pressESC();
|
||||
yield destroyed;
|
||||
|
||||
inspectPage(dropper, false).then(pressESC);
|
||||
ok(true, "escape closed the eyedropper");
|
||||
}
|
||||
|
||||
function testSelect() {
|
||||
function* testSelect() {
|
||||
let dropper = new Eyedropper(window);
|
||||
|
||||
dropper.once("select", (event, color) => {
|
||||
is(color, DIV_COLOR, "correct color selected");
|
||||
});
|
||||
let selected = dropper.once("select");
|
||||
let copied = waitForClipboard(() => {}, DIV_COLOR);
|
||||
|
||||
// wait for DIV_COLOR to be copied to the clipboard then finish the test.
|
||||
waitForClipboard(DIV_COLOR, () => {
|
||||
inspectPage(dropper); // setup: inspect the page
|
||||
}, finish, finish);
|
||||
yield inspectPage(dropper);
|
||||
|
||||
let color = yield selected;
|
||||
is(color, DIV_COLOR, "correct color selected");
|
||||
|
||||
// wait for DIV_COLOR to be copied to the clipboard
|
||||
yield copied;
|
||||
}
|
||||
|
||||
/* Helpers */
|
||||
|
||||
function inspectPage(dropper, click=true) {
|
||||
dropper.open();
|
||||
function* inspectPage(dropper, click=true) {
|
||||
yield dropper.open();
|
||||
|
||||
let target = content.document.getElementById("test");
|
||||
let win = content.window;
|
||||
info("dropper opened");
|
||||
|
||||
EventUtils.synthesizeMouse(target, 20, 20, { type: "mousemove" }, win);
|
||||
let target = document.documentElement;
|
||||
let win = window;
|
||||
|
||||
return dropperLoaded(dropper).then(() => {
|
||||
EventUtils.synthesizeMouse(target, 30, 30, { type: "mousemove" }, win);
|
||||
// get location of the <div> in the content, offset from browser window
|
||||
let box = gBrowser.selectedTab.linkedBrowser.getBoundingClientRect();
|
||||
let x = box.left + 100;
|
||||
let y = box.top + 100;
|
||||
|
||||
if (click) {
|
||||
EventUtils.synthesizeMouse(target, 30, 30, {}, win);
|
||||
}
|
||||
});
|
||||
EventUtils.synthesizeMouse(target, x, y, { type: "mousemove" }, win);
|
||||
|
||||
yield dropperLoaded(dropper);
|
||||
|
||||
EventUtils.synthesizeMouse(target, x + 10, y + 10, { type: "mousemove" }, win);
|
||||
|
||||
if (click) {
|
||||
EventUtils.synthesizeMouse(target, x + 10, y + 10, {}, win);
|
||||
}
|
||||
}
|
||||
|
||||
function pressESC() {
|
||||
|
@ -31,26 +31,29 @@ function spawnTest() {
|
||||
}
|
||||
|
||||
function inspectAndWaitForCopy() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
waitForClipboard(DIV_COLOR, () => {
|
||||
return waitForClipboard(() => {
|
||||
inspectPage(); // setup: inspect the page
|
||||
}, deferred.resolve, deferred.reject);
|
||||
|
||||
return deferred.promise;
|
||||
}, DIV_COLOR);
|
||||
}
|
||||
|
||||
function inspectPage() {
|
||||
let target = content.document.getElementById("test");
|
||||
let win = content.window;
|
||||
let target = document.documentElement;
|
||||
let win = window;
|
||||
|
||||
EventUtils.synthesizeMouse(target, 20, 20, { type: "mousemove" }, win);
|
||||
// get location of the <div> in the content, offset from browser window
|
||||
let box = gBrowser.selectedTab.linkedBrowser.getBoundingClientRect();
|
||||
let x = box.left + 100;
|
||||
let y = box.top + 100;
|
||||
|
||||
let dropper = EyedropperManager.getInstance(window);
|
||||
|
||||
return dropperLoaded(dropper).then(() => {
|
||||
EventUtils.synthesizeMouse(target, 30, 30, { type: "mousemove" }, win);
|
||||
return dropperStarted(dropper).then(() => {
|
||||
EventUtils.synthesizeMouse(target, x, y, { type: "mousemove" }, win);
|
||||
|
||||
EventUtils.synthesizeMouse(target, 30, 30, {}, win);
|
||||
});
|
||||
return dropperLoaded(dropper).then(() => {
|
||||
EventUtils.synthesizeMouse(target, x + 10, y + 10, { type: "mousemove" }, win);
|
||||
|
||||
EventUtils.synthesizeMouse(target, x + 10, y + 10, {}, win);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
@ -10,8 +10,8 @@
|
||||
#test {
|
||||
margin: 100px;
|
||||
background-color: blue;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
@ -13,6 +13,13 @@ Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
/**
|
||||
* Define an async test based on a generator function
|
||||
*/
|
||||
function asyncTest(generator) {
|
||||
return () => Task.spawn(generator).then(null, ok.bind(null, false)).then(finish);
|
||||
}
|
||||
|
||||
function cleanup()
|
||||
{
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
@ -38,6 +45,19 @@ function addTab(uri) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function waitForClipboard(setup, expected) {
|
||||
let deferred = promise.defer();
|
||||
SimpleTest.waitForClipboard(expected, setup, deferred.resolve, deferred.reject);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function dropperStarted(dropper) {
|
||||
if (dropper.isStarted) {
|
||||
return promise.resolve();
|
||||
}
|
||||
return dropper.once("started");
|
||||
}
|
||||
|
||||
function dropperLoaded(dropper) {
|
||||
if (dropper.loaded) {
|
||||
return promise.resolve();
|
||||
|
@ -7,16 +7,24 @@
|
||||
const PAGE_CONTENT = [
|
||||
'<style type="text/css">',
|
||||
' body {',
|
||||
' background-color: #ff5;',
|
||||
' padding: 50px',
|
||||
' background-color: white;',
|
||||
' padding: 0px',
|
||||
' }',
|
||||
' div {',
|
||||
' width: 100px;',
|
||||
' height: 100px;',
|
||||
'',
|
||||
' #div1 {',
|
||||
' background-color: #ff5;',
|
||||
' width: 20px;',
|
||||
' height: 20px;',
|
||||
' }',
|
||||
'',
|
||||
' #div2 {',
|
||||
' margin-left: 20px;',
|
||||
' width: 20px;',
|
||||
' height: 20px;',
|
||||
' background-color: #f09;',
|
||||
' }',
|
||||
'</style>',
|
||||
'<body><div></div></body>'
|
||||
'<body><div id="div1"></div><div id="div2"></div></body>'
|
||||
].join("\n");
|
||||
|
||||
const ORIGINAL_COLOR = "rgb(255, 0, 153)"; // #f09
|
||||
@ -30,9 +38,9 @@ let test = asyncTest(function*() {
|
||||
content.document.body.innerHTML = PAGE_CONTENT;
|
||||
|
||||
let {toolbox, inspector, view} = yield openRuleView();
|
||||
yield selectNode("div", inspector);
|
||||
yield selectNode("#div2", inspector);
|
||||
|
||||
let property = getRuleViewProperty(view, "div", "background-color");
|
||||
let property = getRuleViewProperty(view, "#div2", "background-color");
|
||||
let swatch = property.valueSpan.querySelector(".ruleview-colorswatch");
|
||||
ok(swatch, "Color swatch is displayed for the bg-color property");
|
||||
|
||||
@ -112,24 +120,38 @@ function openEyedropper(view, swatch) {
|
||||
}
|
||||
|
||||
function inspectPage(dropper, click=true) {
|
||||
let target = content.document.body;
|
||||
let win = content.window;
|
||||
let target = document.documentElement;
|
||||
let win = window;
|
||||
|
||||
EventUtils.synthesizeMouse(target, 10, 10, { type: "mousemove" }, win);
|
||||
// get location of the content, offset from browser window
|
||||
let box = gBrowser.selectedTab.linkedBrowser.getBoundingClientRect();
|
||||
let x = box.left + 1;
|
||||
let y = box.top + 1;
|
||||
|
||||
return dropperLoaded(dropper).then(() => {
|
||||
EventUtils.synthesizeMouse(target, 20, 20, { type: "mousemove" }, win);
|
||||
if (click) {
|
||||
EventUtils.synthesizeMouse(target, 20, 20, {}, win);
|
||||
}
|
||||
return dropperStarted(dropper).then(() => {
|
||||
EventUtils.synthesizeMouse(target, x, y, { type: "mousemove" }, win);
|
||||
|
||||
return dropperLoaded(dropper).then(() => {
|
||||
EventUtils.synthesizeMouse(target, x + 10, y + 10, { type: "mousemove" }, win);
|
||||
|
||||
if (click) {
|
||||
EventUtils.synthesizeMouse(target, x + 10, y + 10, {}, win);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function dropperStarted(dropper) {
|
||||
if (dropper.isStarted) {
|
||||
return promise.resolve();
|
||||
}
|
||||
return dropper.once("started");
|
||||
}
|
||||
|
||||
function dropperLoaded(dropper) {
|
||||
if (dropper.loaded) {
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
return dropper.once("load");
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user