Merge autoland to m-c, a=merge

MozReview-Commit-ID: 3nuTAHtJkkD
This commit is contained in:
Phil Ringnalda 2016-10-19 18:25:54 -07:00
Родитель 036a7c4324 6790a0b48f
Коммит 7fd865b93f
63 изменённых файлов: 1168 добавлений и 488 удалений

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

@ -172,10 +172,6 @@ var gTests = [
content.location.reload(); content.location.reload();
yield promise; yield promise;
if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
}
yield expectObserverCalled("recording-window-ended"); yield expectObserverCalled("recording-window-ended");
yield expectNoObserverCalled(); yield expectNoObserverCalled();
yield checkNotSharing(); yield checkNotSharing();

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

@ -58,6 +58,10 @@ var gTests = [
// And finally verify the attribute is removed when closing the stream. // And finally verify the attribute is removed when closing the stream.
yield closeStream(); yield closeStream();
// TODO(Bug 1304997): Fix the race in closeStream() and remove this
// promiseWaitForCondition().
yield promiseWaitForCondition(() => !tab.getAttribute("sharing"));
is(tab.getAttribute("sharing"), "", is(tab.getAttribute("sharing"), "",
"the tab no longer has the 'sharing' attribute after closing the stream"); "the tab no longer has the 'sharing' attribute after closing the stream");
} }

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

@ -110,9 +110,6 @@ var gTests = [
yield promiseReloadFrame("frame1"); yield promiseReloadFrame("frame1");
yield promise; yield promise;
if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
}
yield expectObserverCalled("recording-window-ended"); yield expectObserverCalled("recording-window-ended");
yield expectNoObserverCalled(); yield expectNoObserverCalled();
yield checkNotSharing(); yield checkNotSharing();
@ -188,9 +185,6 @@ var gTests = [
yield expectObserverCalled("recording-window-ended"); yield expectObserverCalled("recording-window-ended");
yield checkSharingUI({video: false, audio: true}); yield checkSharingUI({video: false, audio: true});
if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
}
yield expectNoObserverCalled(); yield expectNoObserverCalled();
yield closeStream(false, "frame1"); yield closeStream(false, "frame1");
@ -225,9 +219,6 @@ var gTests = [
content.location.reload(); content.location.reload();
yield promise; yield promise;
if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
}
yield expectObserverCalled("recording-window-ended"); yield expectObserverCalled("recording-window-ended");
yield expectNoObserverCalled(); yield expectNoObserverCalled();
yield checkNotSharing(); yield checkNotSharing();

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

@ -51,11 +51,8 @@ var gTests = [
gBrowser.selectedBrowser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true); gBrowser.selectedBrowser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
yield BrowserTestUtils.closeWindow(win); yield BrowserTestUtils.closeWindow(win);
if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
}
yield expectObserverCalled("recording-window-ended"); yield expectObserverCalled("recording-window-ended");
yield expectObserverCalled("recording-device-events");
yield expectNoObserverCalled(); yield expectNoObserverCalled();
yield checkNotSharing(); yield checkNotSharing();
} }

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

@ -35,17 +35,18 @@ function requestDevice(aAudio, aVideo, aShare) {
opts.fake = true; opts.fake = true;
} }
window.navigator.mozGetUserMedia(opts, function(stream) { window.navigator.mediaDevices.getUserMedia(opts)
gStream = stream; .then(stream => {
message("ok"); gStream = stream;
}, function(err) { message("error: " + err); }); message("ok");
}, err => message("error: " + err));
} }
message("pending"); message("pending");
function closeStream() { function closeStream() {
if (!gStream) if (!gStream)
return; return;
gStream.stop(); gStream.getTracks().forEach(t => t.stop());
gStream = null; gStream = null;
message("closed"); message("closed");
} }

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

@ -34,13 +34,6 @@ addMessageListener("Test:ExpectObserverCalled", ({data}) => {
--gObservedTopics[data]; --gObservedTopics[data];
}); });
addMessageListener("Test:TodoObserverNotCalled", ({data}) => {
sendAsyncMessage("Test:TodoObserverNotCalled:Reply",
{count: gObservedTopics[data]});
if (gObservedTopics[data] == 1)
gObservedTopics[data] = 0;
});
addMessageListener("Test:ExpectNoObserverCalled", data => { addMessageListener("Test:ExpectNoObserverCalled", data => {
sendAsyncMessage("Test:ExpectNoObserverCalled:Reply", gObservedTopics); sendAsyncMessage("Test:ExpectNoObserverCalled:Reply", gObservedTopics);
gObservedTopics = {}; gObservedTopics = {};

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

@ -233,18 +233,6 @@ function expectNoObserverCalled() {
}); });
} }
function promiseTodoObserverNotCalled(aTopic) {
return new Promise(resolve => {
let mm = _mm();
mm.addMessageListener("Test:TodoObserverNotCalled:Reply",
function listener({data}) {
mm.removeMessageListener("Test:TodoObserverNotCalled:Reply", listener);
resolve(data.count);
});
mm.sendAsyncMessage("Test:TodoObserverNotCalled", aTopic);
});
}
function promiseMessage(aMessage, aAction) { function promiseMessage(aMessage, aAction) {
let promise = new Promise((resolve, reject) => { let promise = new Promise((resolve, reject) => {
let mm = _mm(); let mm = _mm();
@ -357,11 +345,6 @@ function* stopSharing(aType = "camera") {
yield promiseRecordingEvent; yield promiseRecordingEvent;
yield expectObserverCalled("getUserMedia:revoke"); yield expectObserverCalled("getUserMedia:revoke");
yield expectObserverCalled("recording-window-ended"); yield expectObserverCalled("recording-window-ended");
if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
}
yield expectNoObserverCalled(); yield expectNoObserverCalled();
yield* checkNotSharing(); yield* checkNotSharing();
} }
@ -398,12 +381,6 @@ function* closeStream(aAlreadyClosed, aFrameId) {
if (promises) if (promises)
yield Promise.all(promises); yield Promise.all(promises);
// If a GC occurs before MediaStream.stop() is dispatched, we'll receive
// recording-device-events for each track instead of one for the stream.
if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
todo(false, "Stream was GC'd before MediaStream.stop() was dispatched (bug 1284038)");
}
yield* assertWebRTCIndicatorStatus(null); yield* assertWebRTCIndicatorStatus(null);
} }

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

@ -93,13 +93,15 @@ exports.getHighlighterUtils = function (toolbox) {
/** /**
* Start/stop the element picker on the debuggee target. * Start/stop the element picker on the debuggee target.
* @param {Boolean} doFocus - Optionally focus the content area once the picker is
* activated.
* @return A promise that resolves when done * @return A promise that resolves when done
*/ */
let togglePicker = exported.togglePicker = function () { let togglePicker = exported.togglePicker = function (doFocus) {
if (isPicking) { if (isPicking) {
return stopPicker(); return cancelPicker();
} else { } else {
return startPicker(); return startPicker(doFocus);
} }
}; };
@ -109,10 +111,12 @@ exports.getHighlighterUtils = function (toolbox) {
* on the target page to highlight the hovered/picked element. * on the target page to highlight the hovered/picked element.
* Depending on the server-side capabilities, this may fire events when nodes * Depending on the server-side capabilities, this may fire events when nodes
* are hovered. * are hovered.
* @param {Boolean} doFocus - Optionally focus the content area once the picker is
* activated.
* @return A promise that resolves when the picker has started or immediately * @return A promise that resolves when the picker has started or immediately
* if it is already started * if it is already started
*/ */
let startPicker = exported.startPicker = requireInspector(function* () { let startPicker = exported.startPicker = requireInspector(function* (doFocus = false) {
if (isPicking) { if (isPicking) {
return; return;
} }
@ -120,14 +124,14 @@ exports.getHighlighterUtils = function (toolbox) {
toolbox.pickerButtonChecked = true; toolbox.pickerButtonChecked = true;
yield toolbox.selectTool("inspector"); yield toolbox.selectTool("inspector");
toolbox.on("select", stopPicker); toolbox.on("select", cancelPicker);
if (isRemoteHighlightable()) { if (isRemoteHighlightable()) {
toolbox.walker.on("picker-node-hovered", onPickerNodeHovered); toolbox.walker.on("picker-node-hovered", onPickerNodeHovered);
toolbox.walker.on("picker-node-picked", onPickerNodePicked); toolbox.walker.on("picker-node-picked", onPickerNodePicked);
toolbox.walker.on("picker-node-canceled", onPickerNodeCanceled); toolbox.walker.on("picker-node-canceled", onPickerNodeCanceled);
yield toolbox.highlighter.pick(); yield toolbox.highlighter.pick(doFocus);
toolbox.emit("picker-started"); toolbox.emit("picker-started");
} else { } else {
// If the target doesn't have the highlighter actor, we can use the // If the target doesn't have the highlighter actor, we can use the
@ -164,10 +168,18 @@ exports.getHighlighterUtils = function (toolbox) {
yield toolbox.walker.cancelPick(); yield toolbox.walker.cancelPick();
} }
toolbox.off("select", stopPicker); toolbox.off("select", cancelPicker);
toolbox.emit("picker-stopped"); toolbox.emit("picker-stopped");
}); });
/**
* Stop the picker, but also emit an event that the picker was canceled.
*/
let cancelPicker = exported.cancelPicker = Task.async(function* () {
yield stopPicker();
toolbox.emit("picker-canceled");
});
/** /**
* When a node is hovered by the mouse when the highlighter is in picker mode * When a node is hovered by the mouse when the highlighter is in picker mode
* @param {Object} data Information about the node being hovered * @param {Object} data Information about the node being hovered
@ -190,7 +202,7 @@ exports.getHighlighterUtils = function (toolbox) {
* gets the focus. * gets the focus.
*/ */
function onPickerNodeCanceled() { function onPickerNodeCanceled() {
stopPicker(); cancelPicker();
toolbox.win.focus(); toolbox.win.focus();
} }

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

@ -123,6 +123,10 @@ function Toolbox(target, selectedTool, hostType, hostOptions) {
this._toggleMinimizeMode = this._toggleMinimizeMode.bind(this); this._toggleMinimizeMode = this._toggleMinimizeMode.bind(this);
this._onTabbarFocus = this._onTabbarFocus.bind(this); this._onTabbarFocus = this._onTabbarFocus.bind(this);
this._onTabbarArrowKeypress = this._onTabbarArrowKeypress.bind(this); this._onTabbarArrowKeypress = this._onTabbarArrowKeypress.bind(this);
this._onPickerClick = this._onPickerClick.bind(this);
this._onPickerKeypress = this._onPickerKeypress.bind(this);
this._onPickerStarted = this._onPickerStarted.bind(this);
this._onPickerStopped = this._onPickerStopped.bind(this);
this._target.on("close", this.destroy); this._target.on("close", this.destroy);
@ -149,6 +153,9 @@ function Toolbox(target, selectedTool, hostType, hostOptions) {
gDevTools.on("tool-registered", this._toolRegistered); gDevTools.on("tool-registered", this._toolRegistered);
gDevTools.on("tool-unregistered", this._toolUnregistered); gDevTools.on("tool-unregistered", this._toolUnregistered);
this.on("picker-started", this._onPickerStarted);
this.on("picker-stopped", this._onPickerStopped);
} }
exports.Toolbox = Toolbox; exports.Toolbox = Toolbox;
@ -984,8 +991,39 @@ Toolbox.prototype = {
let container = this.doc.querySelector("#toolbox-picker-container"); let container = this.doc.querySelector("#toolbox-picker-container");
container.appendChild(this._pickerButton); container.appendChild(this._pickerButton);
this._togglePicker = this.highlighterUtils.togglePicker.bind(this.highlighterUtils); this._pickerButton.addEventListener("click", this._onPickerClick, false);
this._pickerButton.addEventListener("click", this._togglePicker, false); },
/**
* Toggle the picker, but also decide whether or not the highlighter should
* focus the window. This is only desirable when the toolbox is mounted to the
* window. When devtools is free floating, then the target window should not
* pop in front of the viewer when the picker is clicked.
*/
_onPickerClick: function () {
let focus = this.hostType === Toolbox.HostType.BOTTOM ||
this.hostType === Toolbox.HostType.SIDE;
this.highlighterUtils.togglePicker(focus);
},
/**
* If the picker is activated, then allow the Escape key to deactivate the
* functionality instead of the default behavior of toggling the console.
*/
_onPickerKeypress: function (event) {
if (event.keyCode === KeyCodes.DOM_VK_ESCAPE) {
this.highlighterUtils.cancelPicker();
// Stop the console from toggling.
event.stopImmediatePropagation();
}
},
_onPickerStarted: function () {
this.doc.addEventListener("keypress", this._onPickerKeypress, true);
},
_onPickerStopped: function () {
this.doc.removeEventListener("keypress", this._onPickerKeypress, true);
}, },
/** /**

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

@ -21,7 +21,6 @@
<link rel="stylesheet" href="resource://devtools/client/shared/components/sidebar-toggle.css"/> <link rel="stylesheet" href="resource://devtools/client/shared/components/sidebar-toggle.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabs.css"/> <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabs.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabbar.css"/> <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabbar.css"/>
<link rel="stylesheet" href="resource://devtools/client/inspector/components/side-panel.css"/>
<link rel="stylesheet" href="resource://devtools/client/inspector/components/inspector-tab-panel.css"/> <link rel="stylesheet" href="resource://devtools/client/inspector/components/inspector-tab-panel.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/split-box.css"/> <link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/split-box.css"/>
<link rel="stylesheet" href="resource://devtools/client/inspector/layout/components/Accordion.css"/> <link rel="stylesheet" href="resource://devtools/client/inspector/layout/components/Accordion.css"/>

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

@ -108,6 +108,7 @@ function MarkupView(inspector, frame, controllerWindow) {
this._onFocus = this._onFocus.bind(this); this._onFocus = this._onFocus.bind(this);
this._onMouseMove = this._onMouseMove.bind(this); this._onMouseMove = this._onMouseMove.bind(this);
this._onMouseOut = this._onMouseOut.bind(this); this._onMouseOut = this._onMouseOut.bind(this);
this._onToolboxPickerCanceled = this._onToolboxPickerCanceled.bind(this);
this._onToolboxPickerHover = this._onToolboxPickerHover.bind(this); this._onToolboxPickerHover = this._onToolboxPickerHover.bind(this);
this._onCollapseAttributesPrefChange = this._onCollapseAttributesPrefChange =
this._onCollapseAttributesPrefChange.bind(this); this._onCollapseAttributesPrefChange.bind(this);
@ -127,6 +128,7 @@ function MarkupView(inspector, frame, controllerWindow) {
this.walker.on("mutations", this._mutationObserver); this.walker.on("mutations", this._mutationObserver);
this.walker.on("display-change", this._onDisplayChange); this.walker.on("display-change", this._onDisplayChange);
this.inspector.selection.on("new-node-front", this._onNewSelection); this.inspector.selection.on("new-node-front", this._onNewSelection);
this.toolbox.on("picker-canceled", this._onToolboxPickerCanceled);
this.toolbox.on("picker-node-hovered", this._onToolboxPickerHover); this.toolbox.on("picker-node-hovered", this._onToolboxPickerHover);
this._onNewSelection(); this._onNewSelection();
@ -189,6 +191,16 @@ MarkupView.prototype = {
}, e => console.error(e)); }, e => console.error(e));
}, },
/**
* If the element picker gets canceled, make sure and re-center the view on the
* current selected element.
*/
_onToolboxPickerCanceled: function () {
if (this._selectedContainer) {
scrollIntoViewIfNeeded(this._selectedContainer.editor.elt);
}
},
isDragging: false, isDragging: false,
_onMouseMove: function (event) { _onMouseMove: function (event) {
@ -375,6 +387,9 @@ MarkupView.prototype = {
this.getContainer(nodeFront).hovered = true; this.getContainer(nodeFront).hovered = true;
this._hoveredNode = nodeFront; this._hoveredNode = nodeFront;
// Emit an event that the container view is actually hovered now, as this function
// can be called by an asynchronous caller.
this.emit("showcontainerhovered");
}, },
_onMouseOut: function (event) { _onMouseOut: function (event) {

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

@ -26,6 +26,7 @@ support-files =
doc_inspector_infobar_02.html doc_inspector_infobar_02.html
doc_inspector_infobar_03.html doc_inspector_infobar_03.html
doc_inspector_infobar_textnode.html doc_inspector_infobar_textnode.html
doc_inspector_long-divs.html
doc_inspector_menu.html doc_inspector_menu.html
doc_inspector_outerhtml.html doc_inspector_outerhtml.html
doc_inspector_remove-iframe-during-load.html doc_inspector_remove-iframe-during-load.html
@ -67,6 +68,7 @@ skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keybo
[browser_inspector_highlighter-03.js] [browser_inspector_highlighter-03.js]
[browser_inspector_highlighter-04.js] [browser_inspector_highlighter-04.js]
[browser_inspector_highlighter-by-type.js] [browser_inspector_highlighter-by-type.js]
[browser_inspector_highlighter-cancel.js]
[browser_inspector_highlighter-comments.js] [browser_inspector_highlighter-comments.js]
[browser_inspector_highlighter-cssgrid_01.js] [browser_inspector_highlighter-cssgrid_01.js]
[browser_inspector_highlighter-csstransform_01.js] [browser_inspector_highlighter-csstransform_01.js]

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

@ -0,0 +1,52 @@
/* 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/. */
"use strict";
// Test that canceling the element picker zooms back on the focused element. Bug 1224304.
const TEST_URL = URL_ROOT + "doc_inspector_long-divs.html";
add_task(function* () {
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
yield selectAndHighlightNode("#focus-here", inspector);
ok((yield testActor.assertHighlightedNode("#focus-here")),
"The highlighter focuses on div#focus-here");
ok(isSelectedMarkupNodeInView(),
"The currently selected node is on the screen.");
// Start the picker but skip focusing manually focusing on the target, let the element
// picker do the focusing.
yield startPicker(toolbox, true);
yield moveMouseOver("#zoom-here");
ok(!isSelectedMarkupNodeInView(),
"The currently selected node is off the screen.");
yield cancelPickerByShortcut();
ok(isSelectedMarkupNodeInView(),
"The currently selected node is focused back on the screen.");
function cancelPickerByShortcut() {
info("Key pressed. Waiting for picker to be canceled.");
testActor.synthesizeKey({key: "VK_ESCAPE", options: {}});
return inspector.toolbox.once("picker-canceled");
}
function moveMouseOver(selector) {
info(`Waiting for element ${selector} to be hovered in the markup view`);
testActor.synthesizeMouse({
options: {type: "mousemove"},
center: true,
selector: selector
});
return inspector.markup.once("showcontainerhovered");
}
function isSelectedMarkupNodeInView() {
const selectedNodeContainer = inspector.markup._selectedContainer.elt;
const bounds = selectedNodeContainer.getBoundingClientRect();
return bounds.top > 0 && bounds.bottom > 0;
}
});

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

@ -0,0 +1,104 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Inspector Long Div Listing</title>
<style>
div {
background-color: #0002;
padding-left: 1em;
}
</style>
</head>
<body>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div id="focus-here">focus here</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div>
<div>
<div>
<div>
<div id="zoom-here">zoom-here</div>
</div>
</div>
</div>
</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</body>
</html>

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

@ -69,15 +69,19 @@ var navigateTo = function (toolbox, url) {
/** /**
* Start the element picker and focus the content window. * Start the element picker and focus the content window.
* @param {Toolbox} toolbox * @param {Toolbox} toolbox
* @param {Boolean} skipFocus - Allow tests to bypass the focus event.
*/ */
var startPicker = Task.async(function* (toolbox) { var startPicker = Task.async(function* (toolbox, skipFocus) {
info("Start the element picker"); info("Start the element picker");
toolbox.win.focus();
yield toolbox.highlighterUtils.startPicker(); yield toolbox.highlighterUtils.startPicker();
// Make sure the content window is focused since the picker does not focus if (!skipFocus) {
// the content window by default. // By default make sure the content window is focused since the picker may not focus
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () { // the content window by default.
content.focus(); yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
}); content.focus();
});
}
}); });
/** /**

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

@ -59,6 +59,11 @@ responsive.screenshotGeneratedFilename=Screen Shot %1$S at %2$S
# non-remote tab. # non-remote tab.
responsive.remoteOnly=Responsive Design Mode is only available for remote browser tabs, such as those used for web content in multi-process Firefox. responsive.remoteOnly=Responsive Design Mode is only available for remote browser tabs, such as those used for web content in multi-process Firefox.
# LOCALIZATION NOTE (responsive.noContainerTabs): Message displayed in the tab's
# notification box if a user tries to open Responsive Design Mode in a
# container tab.
responsive.noContainerTabs=Responsive Design Mode is currently unavailable in container tabs.
# LOCALIZATION NOTE (responsive.noThrottling): UI option in a menu to configure # LOCALIZATION NOTE (responsive.noThrottling): UI option in a menu to configure
# network throttling. This option is the default and disables throttling so you # network throttling. This option is the default and disables throttling so you
# just have normal network conditions. There is not very much room in the UI # just have normal network conditions. There is not very much room in the UI

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

@ -76,6 +76,11 @@ const ResponsiveUIManager = exports.ResponsiveUIManager = {
this.showRemoteOnlyNotification(window, tab, options); this.showRemoteOnlyNotification(window, tab, options);
return promise.reject(new Error("RDM only available for remote tabs.")); return promise.reject(new Error("RDM only available for remote tabs."));
} }
// Remove this once we support this case in bug 1306975.
if (tab.linkedBrowser.hasAttribute("usercontextid")) {
this.showNoContainerTabsNotification(window, tab, options);
return promise.reject(new Error("RDM not available for container tabs."));
}
if (!this.isActiveForTab(tab)) { if (!this.isActiveForTab(tab)) {
this.initMenuCheckListenerFor(window); this.initMenuCheckListenerFor(window);
@ -213,7 +218,16 @@ const ResponsiveUIManager = exports.ResponsiveUIManager = {
} }
}), }),
showRemoteOnlyNotification(window, tab, { command } = {}) { showRemoteOnlyNotification(window, tab, options) {
this.showErrorNotification(window, tab, options, getStr("responsive.remoteOnly"));
},
showNoContainerTabsNotification(window, tab, options) {
this.showErrorNotification(window, tab, options,
getStr("responsive.noContainerTabs"));
},
showErrorNotification(window, tab, { command } = {}, msg) {
// Default to using the browser's per-tab notification box // Default to using the browser's per-tab notification box
let nbox = window.gBrowser.getNotificationBox(tab.linkedBrowser); let nbox = window.gBrowser.getNotificationBox(tab.linkedBrowser);
@ -228,14 +242,14 @@ const ResponsiveUIManager = exports.ResponsiveUIManager = {
} }
} }
let value = "devtools-responsive-remote-only"; let value = "devtools-responsive-error";
if (nbox.getNotificationWithValue(value)) { if (nbox.getNotificationWithValue(value)) {
// Notification already displayed // Notification already displayed
return; return;
} }
nbox.appendNotification( nbox.appendNotification(
getStr("responsive.remoteOnly"), msg,
value, value,
null, null,
nbox.PRIORITY_CRITICAL_MEDIUM, nbox.PRIORITY_CRITICAL_MEDIUM,

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

@ -0,0 +1,41 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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/. */
"use strict";
// Make this available to both AMD and CJS environments
define(function (require, exports, module) {
// Dependencies
const React = require("devtools/client/shared/vendor/react");
// Shortcuts
const { span } = React.DOM;
/**
* Renders a Infinity object
*/
const InfinityRep = React.createClass({
displayName: "Infinity",
render: function () {
return (
span({className: "objectBox objectBox-number"},
this.props.object.type
)
);
}
});
function supportsObject(object, type) {
return type == "Infinity" || type == "-Infinity";
}
// Exports from this module
exports.InfinityRep = {
rep: InfinityRep,
supportsObject: supportsObject
};
});

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

@ -15,6 +15,8 @@ DevToolsModules(
'grip-array.js', 'grip-array.js',
'grip-map.js', 'grip-map.js',
'grip.js', 'grip.js',
'infinity.js',
'nan.js',
'null.js', 'null.js',
'number.js', 'number.js',
'object-with-text.js', 'object-with-text.js',

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

@ -0,0 +1,41 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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/. */
"use strict";
// Make this available to both AMD and CJS environments
define(function (require, exports, module) {
// Dependencies
const React = require("devtools/client/shared/vendor/react");
// Shortcuts
const { span } = React.DOM;
/**
* Renders a NaN object
*/
const NaNRep = React.createClass({
displayName: "NaN",
render: function () {
return (
span({className: "objectBox objectBox-nan"},
"NaN"
)
);
}
});
function supportsObject(object, type) {
return type == "NaN";
}
// Exports from this module
exports.NaNRep = {
rep: NaNRep,
supportsObject: supportsObject
};
});

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

@ -39,8 +39,7 @@ define(function (require, exports, module) {
}); });
function supportsObject(object, type) { function supportsObject(object, type) {
return type == "boolean" || type == "number" || return ["boolean", "number", "-0"].includes(type);
(type == "object" && object.type == "-0");
} }
// Exports from this module // Exports from this module

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

@ -21,6 +21,8 @@ define(function (require, exports, module) {
const { ArrayRep } = require("./array"); const { ArrayRep } = require("./array");
const { Obj } = require("./object"); const { Obj } = require("./object");
const { SymbolRep } = require("./symbol"); const { SymbolRep } = require("./symbol");
const { InfinityRep } = require("./infinity");
const { NaNRep } = require("./nan");
// DOM types (grips) // DOM types (grips)
const { Attribute } = require("./attribute"); const { Attribute } = require("./attribute");
@ -62,6 +64,8 @@ define(function (require, exports, module) {
StringRep, StringRep,
Number, Number,
SymbolRep, SymbolRep,
InfinityRep,
NaNRep,
]; ];
/** /**
@ -102,8 +106,8 @@ define(function (require, exports, module) {
let type = typeof object; let type = typeof object;
if (type == "object" && object instanceof String) { if (type == "object" && object instanceof String) {
type = "string"; type = "string";
} else if (object && type == "object" && object.type === "symbol") { } else if (object && type == "object" && object.type) {
type = "symbol"; type = object.type;
} }
if (isGrip(object)) { if (isGrip(object)) {

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

@ -16,6 +16,8 @@ support-files =
[test_reps_grip.html] [test_reps_grip.html]
[test_reps_grip-array.html] [test_reps_grip-array.html]
[test_reps_grip-map.html] [test_reps_grip-map.html]
[test_reps_infinity.html]
[test_reps_nan.html]
[test_reps_null.html] [test_reps_null.html]
[test_reps_number.html] [test_reps_number.html]
[test_reps_object.html] [test_reps_object.html]

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

@ -0,0 +1,71 @@
<!DOCTYPE HTML>
<html>
<!--
Test Infinity rep
-->
<head>
<meta charset="utf-8">
<title>Rep test - Infinity</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">
</head>
<body>
<pre id="test">
<script src="head.js" type="application/javascript;version=1.8"></script>
<script type="application/javascript;version=1.8">
"use strict";
window.onload = Task.async(function* () {
let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
let { InfinityRep } = browserRequire("devtools/client/shared/components/reps/infinity");
try {
yield testInfinity();
yield testNegativeInfinity();
} catch (e) {
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
} finally {
SimpleTest.finish();
}
function testInfinity() {
const stub = getGripStub("testInfinity");
const renderedRep = shallowRenderComponent(Rep, { object: stub });
is(renderedRep.type, InfinityRep.rep,
`Rep correctly selects ${InfinityRep.rep.displayName} for Infinity value`);
const renderedComponent = renderComponent(InfinityRep.rep, { object: stub });
is(renderedComponent.textContent, "Infinity",
"Infinity rep has expected text content for Infinity");
}
function testNegativeInfinity() {
const stub = getGripStub("testNegativeInfinity");
const renderedRep = shallowRenderComponent(Rep, { object: stub });
is(renderedRep.type, InfinityRep.rep,
`Rep correctly selects ${InfinityRep.rep.displayName} for negative Infinity value`);
const renderedComponent = renderComponent(InfinityRep.rep, { object: stub });
is(renderedComponent.textContent, "-Infinity",
"Infinity rep has expected text content for negative Infinity");
}
function getGripStub(name) {
switch (name) {
case "testInfinity":
return {
type: "Infinity"
};
case "testNegativeInfinity":
return {
type: "-Infinity"
};
}
return null;
}
});
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html>
<!--
Test NaN rep
-->
<head>
<meta charset="utf-8">
<title>Rep test - NaN</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">
</head>
<body>
<pre id="test">
<script src="head.js" type="application/javascript;version=1.8"></script>
<script type="application/javascript;version=1.8">
"use strict";
window.onload = Task.async(function* () {
let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
let { NaNRep } = browserRequire("devtools/client/shared/components/reps/nan");
try {
yield testNaN();
} catch (e) {
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
} finally {
SimpleTest.finish();
}
function testNaN() {
const stub = {
type: "NaN"
};
const renderedRep = shallowRenderComponent(Rep, {object: stub});
is(renderedRep.type, NaNRep.rep,
`Rep correctly selects ${NaNRep.rep.displayName} for NaN value`);
const renderedComponent = renderComponent(NaNRep.rep, {object: stub});
is(renderedComponent.textContent, "NaN", "NaN rep has expected text content");
}
});
</script>
</pre>
</body>
</html>

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

@ -43,14 +43,7 @@ add_task(function* () {
info("Run tests for a Tooltip with a XUL panel"); info("Run tests for a Tooltip with a XUL panel");
useXulWrapper = true; useXulWrapper = true;
yield runTests(doc);
let isLinux = Services.appinfo.OS === "Linux";
if (!isLinux) {
// Skip these tests on linux when using a XUL Panel wrapper because some linux window
// manager don't support nicely XUL Panels with noautohide _and_ noautofocus.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1301342#c11
yield runTests(doc);
}
}); });
function* runTests(doc) { function* runTests(doc) {

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

@ -229,6 +229,7 @@ function HTMLTooltip(toolboxDoc, {
this._position = null; this._position = null;
this._onClick = this._onClick.bind(this); this._onClick = this._onClick.bind(this);
this._onXulPanelHidden = this._onXulPanelHidden.bind(this);
this._toggle = new TooltipToggle(this); this._toggle = new TooltipToggle(this);
this.startTogglingOnHover = this._toggle.start.bind(this._toggle); this.startTogglingOnHover = this._toggle.start.bind(this._toggle);
@ -539,6 +540,12 @@ HTMLTooltip.prototype = {
return false; return false;
}, },
_onXulPanelHidden: function () {
if (this.isVisible()) {
this.hide();
}
},
/** /**
* If the tootlip is configured to autofocus and a focusable element can be found, * If the tootlip is configured to autofocus and a focusable element can be found,
* focus it. * focus it.
@ -572,7 +579,6 @@ HTMLTooltip.prototype = {
panel.setAttribute("animate", false); panel.setAttribute("animate", false);
panel.setAttribute("consumeoutsideclicks", false); panel.setAttribute("consumeoutsideclicks", false);
panel.setAttribute("noautofocus", true); panel.setAttribute("noautofocus", true);
panel.setAttribute("noautohide", true);
panel.setAttribute("ignorekeys", true); panel.setAttribute("ignorekeys", true);
panel.setAttribute("tooltip", "aHTMLTooltip"); panel.setAttribute("tooltip", "aHTMLTooltip");
@ -586,12 +592,20 @@ HTMLTooltip.prototype = {
}, },
_showXulWrapperAt: function (left, top) { _showXulWrapperAt: function (left, top) {
this.xulPanelWrapper.addEventListener("popuphidden", this._onXulPanelHidden);
let onPanelShown = listenOnce(this.xulPanelWrapper, "popupshown"); let onPanelShown = listenOnce(this.xulPanelWrapper, "popupshown");
this.xulPanelWrapper.openPopupAtScreen(left, top, false); this.xulPanelWrapper.openPopupAtScreen(left, top, false);
return onPanelShown; return onPanelShown;
}, },
_hideXulWrapper: function () { _hideXulWrapper: function () {
this.xulPanelWrapper.removeEventListener("popuphidden", this._onXulPanelHidden);
if (this.xulPanelWrapper.state === "closed") {
// XUL panel is already closed, resolve immediately.
return Promise.resolve();
}
let onPanelHidden = listenOnce(this.xulPanelWrapper, "popuphidden"); let onPanelHidden = listenOnce(this.xulPanelWrapper, "popuphidden");
this.xulPanelWrapper.hidePopup(); this.xulPanelWrapper.hidePopup();
return onPanelHidden; return onPanelHidden;

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

@ -15,6 +15,7 @@
padding: 2px; padding: 2px;
} }
*|*:root[platform="mac"] > scrollbar,
*|*:root[platform="mac"] *|*:not(html|select) > scrollbar { *|*:root[platform="mac"] *|*:not(html|select) > scrollbar {
border: none; border: none;
} }
@ -40,10 +41,15 @@
border-radius: 3px !important; border-radius: 3px !important;
} }
*|*:root[platform="mac"] > scrollbar slider,
*|*:root[platform="mac"] *|*:not(html|select) > scrollbar slider { *|*:root[platform="mac"] *|*:not(html|select) > scrollbar slider {
-moz-appearance: none !important; -moz-appearance: none !important;
} }
*|*:root[platform="win"] > scrollbar scrollbarbutton,
*|*:root[platform="linux"] > scrollbar scrollbarbutton,
*|*:root[platform="win"] > scrollbar gripper,
*|*:root[platform="linux"] > scrollbar gripper,
*|*:root[platform="win"] *|*:not(html|select) > scrollbar scrollbarbutton, *|*:root[platform="win"] *|*:not(html|select) > scrollbar scrollbarbutton,
*|*:root[platform="linux"] *|*:not(html|select) > scrollbar scrollbarbutton, *|*:root[platform="linux"] *|*:not(html|select) > scrollbar scrollbarbutton,
*|*:root[platform="win"] *|*:not(html|select) > scrollbar gripper, *|*:root[platform="win"] *|*:not(html|select) > scrollbar gripper,

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

@ -353,6 +353,16 @@ var HighlighterActor = exports.HighlighterActor = protocol.ActorClassWithSpec(hi
return null; return null;
}, },
/**
* This pick method also focuses the highlighter's target window.
*/
pickAndFocus: function() {
// Go ahead and pass on the results to help future-proof this method.
let pickResults = this.pick();
this._highlighterEnv.window.focus();
return pickResults;
},
_findAndAttachElement: function (event) { _findAndAttachElement: function (event) {
// originalTarget allows access to the "real" element before any retargeting // originalTarget allows access to the "real" element before any retargeting
// is applied, such as in the case of XBL anonymous elements. See also // is applied, such as in the case of XBL anonymous elements. See also

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

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict"; "use strict";
const { FrontClassWithSpec } = require("devtools/shared/protocol"); const { FrontClassWithSpec, custom } = require("devtools/shared/protocol");
const { const {
customHighlighterSpec, customHighlighterSpec,
highlighterSpec highlighterSpec
@ -15,7 +15,16 @@ const HighlighterFront = FrontClassWithSpec(highlighterSpec, {
this.actorID = json.actor; this.actorID = json.actor;
// FF42+ HighlighterActors starts exposing custom form, with traits object // FF42+ HighlighterActors starts exposing custom form, with traits object
this.traits = json.traits || {}; this.traits = json.traits || {};
} },
pick: custom(function (doFocus) {
if (doFocus && this.pickAndFocus) {
return this.pickAndFocus();
}
return this._pick();
}, {
impl: "_pick"
})
}); });
exports.HighlighterFront = HighlighterFront; exports.HighlighterFront = HighlighterFront;

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

@ -28,6 +28,7 @@ const highlighterSpec = generateActorSpec({
request: {} request: {}
}, },
pick: {}, pick: {},
pickAndFocus: {},
cancelPick: {} cancelPick: {}
} }
}); });

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

@ -2908,9 +2908,13 @@ CanvasRenderingContext2D::UpdateFilter()
// with. // with.
presShell->FlushPendingNotifications(Flush_Frames); presShell->FlushPendingNotifications(Flush_Frames);
bool sourceGraphicIsTainted =
(mCanvasElement && mCanvasElement->IsWriteOnly());
CurrentState().filter = CurrentState().filter =
nsFilterInstance::GetFilterDescription(mCanvasElement, nsFilterInstance::GetFilterDescription(mCanvasElement,
CurrentState().filterChain, CurrentState().filterChain,
sourceGraphicIsTainted,
CanvasUserSpaceMetrics(GetSize(), CanvasUserSpaceMetrics(GetSize(),
CurrentState().fontFont, CurrentState().fontFont,
CurrentState().fontLanguage, CurrentState().fontLanguage,
@ -2918,8 +2922,7 @@ CanvasRenderingContext2D::UpdateFilter()
presShell->GetPresContext()), presShell->GetPresContext()),
gfxRect(0, 0, mWidth, mHeight), gfxRect(0, 0, mWidth, mHeight),
CurrentState().filterAdditionalImages); CurrentState().filterAdditionalImages);
CurrentState().filterSourceGraphicTainted = CurrentState().filterSourceGraphicTainted = sourceGraphicIsTainted;
(mCanvasElement && mCanvasElement->IsWriteOnly());
} }
// //

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

@ -264,12 +264,6 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
[test_filter.html] [test_filter.html]
skip-if = (e10s && debug && os == 'win') skip-if = (e10s && debug && os == 'win')
[test_filter_tainted.html] [test_filter_tainted.html]
skip-if = (e10s && debug && os == 'win')
[test_filter_tainted_source_graphics.html]
skip-if = (e10s && debug && os == 'win')
[test_filter_tainted_displacement_map.html]
skip-if = (e10s && debug && os == 'win')
[test_filter_tainted_displacement_map_source_graphics.html]
[test_offscreencanvas_toblob.html] [test_offscreencanvas_toblob.html]
subsuite = gpu subsuite = gpu
tags = offscreencanvas tags = offscreencanvas

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

@ -1,45 +0,0 @@
<!DOCTYPE HTML>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<body onload="runTest()">
<svg style="display: block; width: 0; height: 0">
<defs>
<filter id="tainted">
<feImage xlink:href="http://example.com/tests/dom/canvas/test/crossorigin/image.png" />
</filter>
</defs>
</svg>
<script>
function runTest() {
SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.filter = 'url(#tainted)';
ctx.rect(0, 0, 16, 16);
ctx.fill();
var expected_error = 'SecurityError';
var data;
try {
data = ctx.getImageData(0, 0, 16, 16);
actual_error = "";
} catch (e) {
actual_error = e.name;
}
is(actual_error, expected_error, 'Canvas should have been tainted and throw a SecurityError');
SimpleTest.finish();
});
}
SimpleTest.waitForExplicitFinish();
</script>

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

@ -4,53 +4,329 @@
<body onload="runTest()" style="margin: 0; padding: 0"> <body onload="runTest()" style="margin: 0; padding: 0">
<svg style="display: block; width: 0; height: 0"> <svg style="display: block; width: 0; height: 0">
<defs> <defs>
<filter id="tainted"> <filter id="colormatrix-make-green">
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
0 0 0 1 0"/>
</filter>
<filter id="use-feFlood-as-map-on-SourceGraphic">
<feFlood flood-color="lime" result='green'/>
<!-- should shift everything up and to the left by 8 pixels (only respects A channel) -->
<feDisplacementMap in="SourceGraphic" in2="green" scale="16"/>
</filter>
<filter id="render-cross-origin-red-feImage">
<feImage xlink:href="http://example.com/tests/dom/canvas/test/image_red-16x16.png"/> <feImage xlink:href="http://example.com/tests/dom/canvas/test/image_red-16x16.png"/>
</filter> </filter>
<filter id="generate-green">
<feFlood flood-color="lime" result='green'/>
</filter>
<filter id="use-SourceGraphic-as-map-on-red">
<feImage xlink:href='image_red-16x16.png' result='img'/>
<feDisplacementMap in="img" in2="SourceGraphic" scale="20"/>
</filter>
<filter id="use-cross-origin-green-feImage-as-map-on-same-origin-red">
<feImage xlink:href='image_red-16x16.png' result='img'/>
<feImage xlink:href='http://example.com/tests/dom/canvas/test/image_green-16x16.png' result='map'/>
<feDisplacementMap in="img" in2="map" scale="20"/>
</filter>
<filter id="use-cross-origin-red-feImage-as-map-on-SourceGraphic">
<feImage xlink:href='http://example.com/tests/dom/canvas/test/image_red-16x16.png' result='img'/>
<feDisplacementMap in="SourceGraphic" in2="img" scale="20"/>
</filter>
</defs> </defs>
</svg> </svg>
<canvas id="c" width="16" height="16"></canvas> <div id="canvasContainer" style="height:100px;"></div>
<img id="same-origin-green" src="image_green-16x16.png"/>
<img id="same-origin-red" src="image_red-16x16.png"/>
<img id="cross-origin-green" src="http://example.com/tests/dom/canvas/test/image_green-16x16.png"/>
<img id="cross-origin-red" src="http://example.com/tests/dom/canvas/test/image_red-16x16.png"/>
<script> <script>
function isPixel(ctx, x,y, r,g,b,a, pos, color, d) { function isPixel(ctx, x, y, r, g, b, a, d) {
var pixel = ctx.getImageData(x, y, 1, 1); var pixel = ctx.getImageData(x, y, 1, 1);
var pr = pixel.data[0], var pr = pixel.data[0],
pg = pixel.data[1], pg = pixel.data[1],
pb = pixel.data[2], pb = pixel.data[2],
pa = pixel.data[3]; pa = pixel.data[3];
ok(r - d <= pr && pr <= r + d && var checkSucceeded = r - d <= pr && pr <= r + d &&
g - d <= pg && pg <= g + d && g - d <= pg && pg <= g + d &&
b - d <= pb && pb <= b + d && b - d <= pb && pb <= b + d &&
a - d <= pa && pa <= a + d, a - d <= pa && pa <= a + d;
'pixel ' + pos + ' is ' + pr + ',' + pg + ',' + pb + ',' + pa + '; expected ' + color + ' +/- ' + d); ok(checkSucceeded,
`pixel ${x},${y} is ${pr},${pg},${pb},${pa}; expected ${r},${g},${b},${a} +/- ${d}`);
return checkSucceeded;
}
function expectCanvasCtxToBeTainted(ctx) {
try {
ctx.getImageData(0, 0, 16, 16);
ok(false, 'Canvas should have been tainted and should throw a SecurityError when calling getImageData.');
} catch (e) {
is(e.name, 'SecurityError', 'Canvas should have been tainted and should throw a SecurityError when calling getImageData.');
}
}
function expectCanvasCtxToBeUntainted(ctx) {
try {
var data = ctx.getImageData(0, 0, 16, 16);
ok(data, 'Canvas should have returned some ImageData from getImageData, and not thrown an error.');
} catch (e) {
ok(false, 'Canvas should not have prevented getImageData.');
}
}
function newCanvas() {
var canvas = document.createElement('canvas');
canvas.width = 16;
canvas.height = 16;
return canvas;
}
// This uses SpecialPowers + drawWindow and can check pixels even in tainted canvases.
function checkCanvasPixel(canvas, x, y, r, g, b, a, fuzz) {
var container = document.getElementById('canvasContainer');
container.appendChild(canvas);
var captureCanvas = newCanvas();
var captureCtx = SpecialPowers.wrap(captureCanvas.getContext('2d'));
captureCtx.drawWindow(window, 0, 0, 16, 16, 'rgb(255, 255, 255)', 0);
if (!isPixel(captureCtx, x, y, r, g, b, a, fuzz)) {
info(captureCanvas.toDataURL('image/png'));
}
container.removeChild(canvas);
} }
function runTest() { function runTest() {
SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () { SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () {
var sameOriginGreenImage = document.getElementById('same-origin-green');
var canvas = document.getElementById('c'); var sameOriginRedImage = document.getElementById('same-origin-red');
var crossOriginGreenImage = document.getElementById('cross-origin-green');
var crossOriginRedImage = document.getElementById('cross-origin-red');
var canvas = newCanvas();
var ctx = canvas.getContext('2d'); var ctx = canvas.getContext('2d');
ctx.filter = 'url(#tainted)'; // A CSS filter should not taint the canvas.
ctx.filter = 'grayscale(100%)';
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 16, 16);
checkCanvasPixel(canvas, 8,8, 92,92,92,255, 5);
expectCanvasCtxToBeUntainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// An SVG filter should not taint the canvas.
ctx.filter = 'url(#colormatrix-make-green)';
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 16, 16);
checkCanvasPixel(canvas, 8,8, 0,255,0,255, 5);
expectCanvasCtxToBeUntainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// A CSS -> SVG -> CSS filter chain should not taint the canvas.
ctx.filter = 'grayscale(100%) url(#colormatrix-make-green) grayscale(100%)';
is(ctx.filter, 'grayscale(100%) url(#colormatrix-make-green) grayscale(100%)', 'filter chain should parse correctly');
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 16, 16);
checkCanvasPixel(canvas, 8,8, 183,183,183,255, 5);
expectCanvasCtxToBeUntainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// An SVG -> CSS -> SVG filter chain should not taint the canvas.
ctx.filter = 'url(#colormatrix-make-green) grayscale(100%) url(#colormatrix-make-green)';
is(ctx.filter, 'url(#colormatrix-make-green) grayscale(100%) url(#colormatrix-make-green)', 'filter chain should parse correctly');
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 16, 16);
checkCanvasPixel(canvas, 8,8, 0,255,0,255, 5);
expectCanvasCtxToBeUntainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// feDisplacementMap with untainted map input from feFlood should work and not taint
ctx.filter = 'url(#use-feFlood-as-map-on-SourceGraphic)';
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 16, 16);
checkCanvasPixel(canvas, 4,4, 0,0,255,255, 1);
checkCanvasPixel(canvas, 12,4, 255,255,255,255, 1);
checkCanvasPixel(canvas, 4,12, 255,255,255,255, 1);
checkCanvasPixel(canvas, 12,12, 255,255,255,255, 1);
expectCanvasCtxToBeUntainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// feDisplacementMap with untainted map input from different SVG filter should work and not taint
ctx.filter = 'url(#generate-green) url(#use-SourceGraphic-as-map-on-red)';
is(ctx.filter, 'url(#generate-green) url(#use-SourceGraphic-as-map-on-red)', 'filter chain should parse correctly');
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 16, 16);
checkCanvasPixel(canvas, 4,4, 255,0,0,255, 1);
checkCanvasPixel(canvas, 12,4, 255,255,255,255, 1);
checkCanvasPixel(canvas, 4,12, 255,255,255,255, 1);
checkCanvasPixel(canvas, 12,12, 255,255,255,255, 1);
expectCanvasCtxToBeUntainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// feDisplacementMap with untainted map input from different CSS filter should work and not taint
ctx.filter = 'url(#generate-green) grayscale(100%) url(#use-SourceGraphic-as-map-on-red)';
is(ctx.filter, 'url(#generate-green) grayscale(100%) url(#use-SourceGraphic-as-map-on-red)', 'filter chain should parse correctly');
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 16, 16);
checkCanvasPixel(canvas, 4,4, 255,0,0,255, 1);
checkCanvasPixel(canvas, 12,4, 255,255,255,255, 1);
checkCanvasPixel(canvas, 4,12, 255,255,255,255, 1);
checkCanvasPixel(canvas, 12,12, 255,255,255,255, 1);
expectCanvasCtxToBeUntainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// drawImage with cross-origin image and SVG filter should apply the filter and taint the canvas.
ctx.filter = 'url(#colormatrix-make-green)';
ctx.drawImage(crossOriginRedImage, 0, 0);
checkCanvasPixel(canvas, 8,8, 0,255,0,255, 1);
expectCanvasCtxToBeTainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// Create new untainted canvas.
canvas = newCanvas();
ctx = canvas.getContext('2d');
// drawImage with cross-origin image and CSS filter should apply the filter and taint the canvas.
ctx.filter = 'grayscale(100%)';
ctx.drawImage(crossOriginRedImage, 0, 0);
checkCanvasPixel(canvas, 8,8, 53,53,53,255, 1);
expectCanvasCtxToBeTainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// feDisplacementMap with untainted map input from different CSS filter should work even on tainted canvas
ctx.filter = 'url(#generate-green) grayscale(100%) url(#use-SourceGraphic-as-map-on-red)';
is(ctx.filter, 'url(#generate-green) grayscale(100%) url(#use-SourceGraphic-as-map-on-red)', 'filter chain should parse correctly');
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 16, 16);
checkCanvasPixel(canvas, 4,4, 255,0,0,255, 1);
checkCanvasPixel(canvas, 12,4, 255,255,255,255, 1);
checkCanvasPixel(canvas, 4,12, 255,255,255,255, 1);
checkCanvasPixel(canvas, 12,12, 255,255,255,255, 1);
expectCanvasCtxToBeTainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// feDisplacementMap with untainted map input from feFlood should work even on tainted canvas
ctx.filter = 'url(#use-feFlood-as-map-on-SourceGraphic)';
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 16, 16);
checkCanvasPixel(canvas, 4,4, 0,0,255,255, 1);
checkCanvasPixel(canvas, 12,4, 255,255,255,255, 1);
checkCanvasPixel(canvas, 4,12, 255,255,255,255, 1);
checkCanvasPixel(canvas, 12,12, 255,255,255,255, 1);
expectCanvasCtxToBeTainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// Create new untainted canvas.
canvas = newCanvas();
ctx = canvas.getContext('2d');
// cross-origin feImage should render correctly and taint the canvas
ctx.filter = 'url(#render-cross-origin-red-feImage)';
ctx.rect(0, 0, 16, 16); ctx.rect(0, 0, 16, 16);
ctx.fill(); ctx.fill();
checkCanvasPixel(canvas, 8,8, 255,0,0,255, 5);
expectCanvasCtxToBeTainted(ctx);
// Create new untainted canvas.
canvas = newCanvas();
ctx = canvas.getContext('2d');
var canvas2 = document.createElement('canvas'); // cross-origin feImage should be ignored as map input to a displacement map, and taint the canvas.
var ctx2 = SpecialPowers.wrap(canvas2.getContext('2d')); ctx.filter = 'url(#use-cross-origin-green-feImage-as-map-on-same-origin-red)';
ctx2.drawWindow(window, 0, 0, 16, 16, 'rgb(255,255,255)', 0); ctx.rect(0, 0, 16, 16);
isPixel(ctx2, 8,8, 255,0,0,255, '8,8', "255,0,0,255", 5); ctx.fill();
checkCanvasPixel(canvas, 4,4, 255,0,0,255, 1);
var expected_error = 'SecurityError'; checkCanvasPixel(canvas, 12,4, 255,0,0,255, 1);
var data; checkCanvasPixel(canvas, 4,12, 255,0,0,255, 1);
try { checkCanvasPixel(canvas, 12,12, 255,0,0,255, 1);
data = ctx.getImageData(0, 0, 16, 16); expectCanvasCtxToBeTainted(ctx);
actual_error = "";
} catch (e) { // Create new untainted canvas.
actual_error = e.name; canvas = newCanvas();
} ctx = canvas.getContext('2d');
is(actual_error, expected_error, 'canvas should have been tainted and throw a SecurityError');
expectCanvasCtxToBeUntainted(ctx);
// cross-origin SourceGraphic should be ignored as map input to a displacement map, and taint the canvas.
// SourceGraphic: cross-origin image_green-16x16.png,
// gets used as map to shift same-origin image_red-16x16.png,
// but should get ignored so that image_red-16x16.png gets drawn unshifted.
ctx.filter = 'url(#use-SourceGraphic-as-map-on-red)';
ctx.drawImage(crossOriginGreenImage, 0, 0);
// expect to see red because cross-origin image_green-16x16.png should not have displaced the red away
checkCanvasPixel(canvas, 4,4, 255,0,0,255, 1);
checkCanvasPixel(canvas, 12,4, 255,0,0,255, 1);
checkCanvasPixel(canvas, 4,12, 255,0,0,255, 1);
checkCanvasPixel(canvas, 12,12, 255,0,0,255, 1);
expectCanvasCtxToBeTainted(ctx);
// cross-origin feImage should be ignored as map to displacement map, and taint the canvas.
// SourceGraphic: cross-origin image_green-16x16.png,
// Cross-origin feImage image_red-16x16.png gets used as map to shift SourceGraphic,
// but should get ignored so that image_green-16x16.png gets drawn unshifted.
ctx.filter = 'url(#use-cross-origin-red-feImage-as-map-on-SourceGraphic)';
ctx.drawImage(crossOriginGreenImage, 0, 0);
// expect to see green because cross-origin image_red-16x16.png should not have displaced the green away
checkCanvasPixel(canvas, 4,4, 0,255,0,255, 1);
checkCanvasPixel(canvas, 12,4, 0,255,0,255, 1);
checkCanvasPixel(canvas, 4,12, 0,255,0,255, 1);
checkCanvasPixel(canvas, 12,12, 0,255,0,255, 1);
expectCanvasCtxToBeTainted(ctx);
// Create new untainted canvas.
canvas = newCanvas();
ctx = canvas.getContext('2d');
// feDisplacementMap with tainted map input from different CSS filter should be ignored
ctx.filter = 'grayscale(100%) url(#use-SourceGraphic-as-map-on-red)';
is(ctx.filter, 'grayscale(100%) url(#use-SourceGraphic-as-map-on-red)', 'filter chain should parse correctly');
ctx.drawImage(crossOriginGreenImage, 0, 0);
checkCanvasPixel(canvas, 4,4, 255,0,0,255, 1);
checkCanvasPixel(canvas, 12,4, 255,0,0,255, 1);
checkCanvasPixel(canvas, 4,12, 255,0,0,255, 1);
checkCanvasPixel(canvas, 12,12, 255,0,0,255, 1);
expectCanvasCtxToBeTainted(ctx);
ctx.clearRect(0, 0, 16, 16);
// Create new untainted canvas.
canvas = newCanvas();
ctx = canvas.getContext('2d');
// feDisplacementMap with tainted feImage map input from different SVG filter should be ignored
ctx.filter = 'url(#render-cross-origin-red-feImage) url(#use-SourceGraphic-as-map-on-red)';
is(ctx.filter, 'url(#render-cross-origin-red-feImage) url(#use-SourceGraphic-as-map-on-red)', 'filter chain should parse correctly');
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 16, 16);
checkCanvasPixel(canvas, 4,4, 255,0,0,255, 1);
checkCanvasPixel(canvas, 12,4, 255,0,0,255, 1);
checkCanvasPixel(canvas, 4,12, 255,0,0,255, 1);
checkCanvasPixel(canvas, 12,12, 255,0,0,255, 1);
expectCanvasCtxToBeTainted(ctx);
// Paint 'canvas' (which is red and tainted) into a different canvas and makes sure it taints that other canvas.
var secondCanvas = newCanvas();
var secondCtx = secondCanvas.getContext('2d');
secondCtx.filter = 'grayscale(100%)';
secondCtx.drawImage(canvas, 0, 0);
checkCanvasPixel(secondCanvas, 4,4, 53,53,53,255, 1);
checkCanvasPixel(secondCanvas, 12,4, 53,53,53,255, 1);
checkCanvasPixel(secondCanvas, 4,12, 53,53,53,255, 1);
checkCanvasPixel(secondCanvas, 12,12, 53,53,53,255, 1);
expectCanvasCtxToBeTainted(secondCtx);
// Fill the left half with blue (i.e. an untainted SourceGraphic) and make sure the canvas is still tainted.
secondCtx.fillStyle = "blue";
secondCtx.fillRect(0, 0, 8, 16);
checkCanvasPixel(secondCanvas, 4,4, 17,17,17,255, 1);
checkCanvasPixel(secondCanvas, 12,4, 53,53,53,255, 1);
checkCanvasPixel(secondCanvas, 4,12, 17,17,17,255, 1);
checkCanvasPixel(secondCanvas, 12,12, 53,53,53,255, 1);
expectCanvasCtxToBeTainted(secondCtx);
SimpleTest.finish(); SimpleTest.finish();
}); });

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

@ -1,64 +0,0 @@
<!DOCTYPE HTML>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<body onload="runTest()" style="margin: 0; padding: 0">
<svg style="display: block; width: 0; height: 0">
<defs>
<filter id="tainted">
<feImage xlink:href='image_red-16x16.png' result='img'/>
<feImage xlink:href='http://example.com/tests/dom/canvas/test/image_green-16x16.png' result='map'/>
<feDisplacementMap in="img" in2="map" scale="20"/>
</filter>
</defs>
</svg>
<canvas id="c" width="16" height="16"></canvas>
<script>
function isPixel(ctx, x,y, r,g,b,a, pos, color, d) {
var pixel = ctx.getImageData(x, y, 1, 1);
var pr = pixel.data[0],
pg = pixel.data[1],
pb = pixel.data[2],
pa = pixel.data[3];
ok(r - d <= pr && pr <= r + d &&
g - d <= pg && pg <= g + d &&
b - d <= pb && pb <= b + d &&
a - d <= pa && pa <= a + d,
'pixel ' + pos + ' is ' + pr + ',' + pg + ',' + pb + ',' + pa + '; expected ' + color + ' +/- ' + d);
}
function runTest() {
SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () {
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
ctx.filter = 'url(#tainted)';
ctx.rect(0, 0, 16, 16);
ctx.fill();
var canvas2 = document.createElement('canvas');
var ctx2 = SpecialPowers.wrap(canvas2.getContext('2d'));
ctx2.drawWindow(window, 0, 0, 16, 16, 'rgb(255,255,255)', 0);
isPixel(ctx2, 8,8, 255,0,0,255, '8,8', "255,0,0,255", 5);
var expected_error = 'SecurityError';
var data;
try {
data = ctx.getImageData(0, 0, 16, 16);
actual_error = "";
} catch (e) {
actual_error = e.name;
}
is(actual_error, expected_error, 'canvas should have been tainted and throw a SecurityError');
SimpleTest.finish();
});
}
SimpleTest.waitForExplicitFinish();
</script>

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

@ -1,73 +0,0 @@
<!DOCTYPE HTML>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<body onload="runTest()" style="margin: 0; padding: 0">
<svg style="display: block; width: 0; height: 0">
<defs>
<filter id="tainted1">
<feImage xlink:href='image_red-16x16.png' result='img'/>
<feDisplacementMap in="img" in2="SourceGraphic" scale="20"/>
</filter>
<filter id="tainted2">
<feImage xlink:href='http://example.com/tests/dom/canvas/test/image_red-16x16.png' result='img'/>
<feDisplacementMap in="SourceGraphic" in2="img" scale="20"/>
</filter>
</defs>
</svg>
<canvas id="c" width="16" height="16"></canvas>
<img id="i" src="http://example.com/tests/dom/canvas/test/image_green-16x16.png"/>
<script>
function isPixel(ctx, x,y, r,g,b,a, pos, color, d) {
var pixel = ctx.getImageData(x, y, 1, 1);
var pr = pixel.data[0],
pg = pixel.data[1],
pb = pixel.data[2],
pa = pixel.data[3];
ok(r - d <= pr && pr <= r + d &&
g - d <= pg && pg <= g + d &&
b - d <= pb && pb <= b + d &&
a - d <= pa && pa <= a + d,
'pixel ' + pos + ' is ' + pr + ',' + pg + ',' + pb + ',' + pa + '; expected ' + color + ' +/- ' + d);
}
function runTest() {
SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () {
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var img = document.getElementById('i');
ctx.filter = 'url(#tainted1)';
ctx.drawImage(img, 0, 0);
var canvas2 = document.createElement('canvas');
var ctx2 = SpecialPowers.wrap(canvas2.getContext('2d'));
ctx2.drawWindow(window, 0, 0, 16, 16, 'rgb(255, 255, 255)', 0);
isPixel(ctx2, 8,8, 255,0,0,255, '8,8', "255,0,0,255", 5);
ctx.filter = 'url(#tainted2)';
ctx.drawImage(img, 0, 0);
ctx2.drawWindow(window, 0, 0, 16, 16, 'rgb(255, 255, 255)', 0);
isPixel(ctx2, 8,8, 0,255,0,255, '8,8', "0,255,0,255", 5);
var expected_error = 'SecurityError';
var data;
try {
data = ctx.getImageData(0, 0, 16, 16);
actual_error = "";
} catch (e) {
actual_error = e.name;
}
is(actual_error, expected_error, 'canvas should have been tainted and throw a SecurityError');
SimpleTest.finish();
});
}
SimpleTest.waitForExplicitFinish();
</script>

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

@ -1,65 +0,0 @@
<!DOCTYPE HTML>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<body onload="runTest()" style="margin: 0; padding: 0">
<svg style="display: block; width: 0; height: 0">
<defs>
<filter id="tainted">
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
0 0 0 1 0"/>
</filter>
</defs>
</svg>
<canvas id="c" width="16" height="16"></canvas>
<img id="i" src="http://example.com/tests/dom/canvas/test/image_red-16x16.png"/>
<script>
function isPixel(ctx, x,y, r,g,b,a, pos, color, d) {
var pixel = ctx.getImageData(x, y, 1, 1);
var pr = pixel.data[0],
pg = pixel.data[1],
pb = pixel.data[2],
pa = pixel.data[3];
ok(r - d <= pr && pr <= r + d &&
g - d <= pg && pg <= g + d &&
b - d <= pb && pb <= b + d &&
a - d <= pa && pa <= a + d,
'pixel ' + pos + ' is ' + pr + ',' + pg + ',' + pb + ',' + pa + '; expected ' + color + ' +/- ' + d);
}
function runTest() {
SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () {
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var img = document.getElementById('i');
ctx.filter = 'url(#tainted)';
ctx.drawImage(img, 0, 0);
var canvas2 = document.createElement('canvas');
var ctx2 = SpecialPowers.wrap(canvas2.getContext('2d'));
ctx2.drawWindow(window, 0, 0, 16, 16, 'rgb(255,255,255)', 0);
isPixel(ctx2, 8,8, 0,255,0,255, '8,8', "0,255,0,255", 5);
var expected_error = 'SecurityError';
var data;
try {
data = ctx.getImageData(0, 0, 16, 16);
actual_error = "";
} catch (e) {
actual_error = e.name;
}
is(actual_error, expected_error, 'canvas should have been tainted and throw a SecurityError');
SimpleTest.finish();
});
}
SimpleTest.waitForExplicitFinish();
</script>

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

@ -263,7 +263,11 @@ public:
, mFinished(false) , mFinished(false)
, mRemoved(false) , mRemoved(false)
, mAudioStopped(false) , mAudioStopped(false)
, mVideoStopped(false) {} , mAudioStopPending(false)
, mVideoStopped(false)
, mVideoStopPending(false)
, mChromeNotificationTaskPosted(false)
{}
~GetUserMediaCallbackMediaStreamListener() ~GetUserMediaCallbackMediaStreamListener()
{ {
@ -302,6 +306,8 @@ public:
void StopTrack(TrackID aID); void StopTrack(TrackID aID);
void NotifyChromeOfTrackStops();
typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid; typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
already_AddRefed<PledgeVoid> already_AddRefed<PledgeVoid>
@ -494,10 +500,22 @@ private:
// MainThread only. // MainThread only.
bool mAudioStopped; bool mAudioStopped;
// true if we have scheduled MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
// MainThread only.
bool mAudioStopPending;
// true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice. // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice.
// MainThread only. // MainThread only.
bool mVideoStopped; bool mVideoStopped;
// true if we have scheduled MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice.
// MainThread only.
bool mVideoStopPending;
// true if we have scheduled a task to notify chrome in the next stable state.
// The task will reset this to false. MainThread only.
bool mChromeNotificationTaskPosted;
// Set at Activate on MainThread // Set at Activate on MainThread
// Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
@ -743,7 +761,8 @@ protected:
NS_IMPL_ISUPPORTS(MediaDevice, nsIMediaDevice) NS_IMPL_ISUPPORTS(MediaDevice, nsIMediaDevice)
MediaDevice::MediaDevice(MediaEngineSource* aSource, bool aIsVideo) MediaDevice::MediaDevice(MediaEngineSource* aSource, bool aIsVideo)
: mMediaSource(aSource->GetMediaSource()) : mScary(aSource->GetScary())
, mMediaSource(aSource->GetMediaSource())
, mSource(aSource) , mSource(aSource)
, mIsVideo(aIsVideo) , mIsVideo(aIsVideo)
{ {
@ -881,6 +900,13 @@ MediaDevice::GetRawId(nsAString& aID)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
MediaDevice::GetScary(bool* aScary)
{
*aScary = mScary;
return NS_OK;
}
void void
MediaDevice::SetId(const nsAString& aID) MediaDevice::SetId(const nsAString& aID)
{ {
@ -3546,9 +3572,9 @@ GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID)
{ {
LOG(("Can't stop gUM track %d (%s), exists=%d, stopped=%d", LOG(("Can't stop gUM track %d (%s), exists=%d, stopped=%d",
aTrackID, aTrackID,
aTrackID == kAudioTrack ? "audio" : "video", stopAudio ? "audio" : "video",
aTrackID == kAudioTrack ? !!mAudioDevice : !!mVideoDevice, stopAudio ? !!mAudioDevice : !!mVideoDevice,
aTrackID == kAudioTrack ? mAudioStopped : mVideoStopped)); stopAudio ? mAudioStopped : mVideoStopped));
return; return;
} }
@ -3558,6 +3584,59 @@ GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID)
return; return;
} }
// We wait until stable state before notifying chrome so chrome only does one
// update if more tracks are stopped in this event loop.
mAudioStopPending |= stopAudio;
mVideoStopPending |= stopVideo;
if (mChromeNotificationTaskPosted) {
return;
}
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyChromeOfTrackStops);
nsContentUtils::RunInStableState(runnable.forget());
mChromeNotificationTaskPosted = true;
}
void
GetUserMediaCallbackMediaStreamListener::NotifyChromeOfTrackStops()
{
MOZ_ASSERT(mChromeNotificationTaskPosted);
mChromeNotificationTaskPosted = false;
// We make sure these are always reset.
bool stopAudio = mAudioStopPending;
bool stopVideo = mVideoStopPending;
mAudioStopPending = false;
mVideoStopPending = false;
if (mStopped) {
// The entire capture was stopped while we were waiting for stable state.
return;
}
MOZ_ASSERT(stopAudio || stopVideo);
MOZ_ASSERT(!stopAudio || !mAudioStopped,
"If there's a pending stop for audio, audio must not have been stopped");
MOZ_ASSERT(!stopAudio || mAudioDevice,
"If there's a pending stop for audio, there must be an audio device");
MOZ_ASSERT(!stopVideo || !mVideoStopped,
"If there's a pending stop for video, video must not have been stopped");
MOZ_ASSERT(!stopVideo || mVideoDevice,
"If there's a pending stop for video, there must be a video device");
if ((stopAudio || mAudioStopped || !mAudioDevice) &&
(stopAudio || mVideoStopped || !mVideoDevice)) {
// All tracks stopped.
Stop();
return;
}
mAudioStopped |= stopAudio;
mVideoStopped |= stopVideo;
RefPtr<MediaOperationTask> mediaOperation = RefPtr<MediaOperationTask> mediaOperation =
new MediaOperationTask(MEDIA_STOP_TRACK, new MediaOperationTask(MEDIA_STOP_TRACK,
this, nullptr, nullptr, this, nullptr, nullptr,
@ -3565,8 +3644,6 @@ GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID)
stopVideo ? mVideoDevice.get() : nullptr, stopVideo ? mVideoDevice.get() : nullptr,
false , mWindowID, nullptr); false , mWindowID, nullptr);
MediaManager::PostTask(mediaOperation.forget()); MediaManager::PostTask(mediaOperation.forget());
mAudioStopped |= stopAudio;
mVideoStopped |= stopVideo;
} }
void void

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

@ -94,6 +94,7 @@ protected:
nsString mName; nsString mName;
nsString mID; nsString mID;
nsString mRawID; nsString mRawID;
bool mScary;
dom::MediaSourceEnum mMediaSource; dom::MediaSourceEnum mMediaSource;
RefPtr<MediaEngineSource> mSource; RefPtr<MediaEngineSource> mSource;
RefPtr<MediaEngineSource::AllocationHandle> mAllocationHandle; RefPtr<MediaEngineSource::AllocationHandle> mAllocationHandle;

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

@ -13,6 +13,7 @@ interface nsIMediaDevice : nsISupports
readonly attribute DOMString id; readonly attribute DOMString id;
readonly attribute DOMString mediaSource; readonly attribute DOMString mediaSource;
readonly attribute DOMString rawId; readonly attribute DOMString rawId;
readonly attribute boolean scary;
}; };
[scriptable, function, uuid(24544878-d35e-4962-8c5f-fb84e97bdfee)] [scriptable, function, uuid(24544878-d35e-4962-8c5f-fb84e97bdfee)]

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

@ -214,6 +214,9 @@ public:
/* Populate the UUID of this device in the nsACString */ /* Populate the UUID of this device in the nsACString */
virtual void GetUUID(nsACString&) const = 0; virtual void GetUUID(nsACString&) const = 0;
/* Override w/true if source does end-run around cross origin restrictions. */
virtual bool GetScary() const { return false; };
class AllocationHandle class AllocationHandle
{ {
public: public:
@ -249,9 +252,14 @@ public:
return a.get() == b.get(); return a.get() == b.get();
} }
}; };
MOZ_ASSERT(mRegisteredHandles.Contains(handle, Comparator()));
mRegisteredHandles.RemoveElementAt(mRegisteredHandles.IndexOf(handle, 0, auto ix = mRegisteredHandles.IndexOf(handle, 0, Comparator());
Comparator())); if (ix == mRegisteredHandles.NoIndex) {
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
mRegisteredHandles.RemoveElementAt(ix);
if (mRegisteredHandles.Length() && !mInShutdown) { if (mRegisteredHandles.Length() && !mInShutdown) {
// Whenever constraints are removed, other parties may get closer to ideal. // Whenever constraints are removed, other parties may get closer to ideal.
auto& first = mRegisteredHandles[0]; auto& first = mRegisteredHandles[0];

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

@ -29,10 +29,11 @@ NS_IMPL_ISUPPORTS0(MediaEngineRemoteVideoSource)
MediaEngineRemoteVideoSource::MediaEngineRemoteVideoSource( MediaEngineRemoteVideoSource::MediaEngineRemoteVideoSource(
int aIndex, mozilla::camera::CaptureEngine aCapEngine, int aIndex, mozilla::camera::CaptureEngine aCapEngine,
dom::MediaSourceEnum aMediaSource, const char* aMonitorName) dom::MediaSourceEnum aMediaSource, bool aScary, const char* aMonitorName)
: MediaEngineCameraVideoSource(aIndex, aMonitorName), : MediaEngineCameraVideoSource(aIndex, aMonitorName),
mMediaSource(aMediaSource), mMediaSource(aMediaSource),
mCapEngine(aCapEngine) mCapEngine(aCapEngine),
mScary(aScary)
{ {
MOZ_ASSERT(aMediaSource != dom::MediaSourceEnum::Other); MOZ_ASSERT(aMediaSource != dom::MediaSourceEnum::Other);
mSettings.mWidth.Construct(0); mSettings.mWidth.Construct(0);

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

@ -70,6 +70,7 @@ public:
// MediaEngineCameraVideoSource // MediaEngineCameraVideoSource
MediaEngineRemoteVideoSource(int aIndex, mozilla::camera::CaptureEngine aCapEngine, MediaEngineRemoteVideoSource(int aIndex, mozilla::camera::CaptureEngine aCapEngine,
dom::MediaSourceEnum aMediaSource, dom::MediaSourceEnum aMediaSource,
bool aScary = false,
const char* aMonitorName = "RemoteVideo.Monitor"); const char* aMonitorName = "RemoteVideo.Monitor");
nsresult Allocate(const dom::MediaTrackConstraints& aConstraints, nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
@ -103,6 +104,8 @@ public:
void Shutdown() override; void Shutdown() override;
bool GetScary() const override { return mScary; }
protected: protected:
~MediaEngineRemoteVideoSource() { } ~MediaEngineRemoteVideoSource() { }
@ -125,6 +128,7 @@ private:
// To only restart camera when needed, we keep track previous settings. // To only restart camera when needed, we keep track previous settings.
webrtc::CaptureCapability mLastCapability; webrtc::CaptureCapability mLastCapability;
bool mScary;
}; };
} }

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

@ -21,6 +21,11 @@ class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventList
void GetName(nsAString_internal&) const override; void GetName(nsAString_internal&) const override;
void GetUUID(nsACString_internal&) const override; void GetUUID(nsACString_internal&) const override;
bool GetScary() const override {
return true;
}
nsresult Allocate(const dom::MediaTrackConstraints &, nsresult Allocate(const dom::MediaTrackConstraints &,
const mozilla::MediaEnginePrefs&, const mozilla::MediaEnginePrefs&,
const nsString& aDeviceId, const nsString& aDeviceId,

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

@ -157,6 +157,7 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
return; return;
} }
#endif #endif
bool scaryKind = false; // flag sources with cross-origin exploit potential
switch (aMediaSource) { switch (aMediaSource) {
case dom::MediaSourceEnum::Window: case dom::MediaSourceEnum::Window:
@ -167,9 +168,11 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
break; break;
case dom::MediaSourceEnum::Screen: case dom::MediaSourceEnum::Screen:
capEngine = mozilla::camera::ScreenEngine; capEngine = mozilla::camera::ScreenEngine;
scaryKind = true;
break; break;
case dom::MediaSourceEnum::Browser: case dom::MediaSourceEnum::Browser:
capEngine = mozilla::camera::BrowserEngine; capEngine = mozilla::camera::BrowserEngine;
scaryKind = true;
break; break;
case dom::MediaSourceEnum::Camera: case dom::MediaSourceEnum::Camera:
capEngine = mozilla::camera::CameraEngine; capEngine = mozilla::camera::CameraEngine;
@ -196,6 +199,7 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
for (int i = 0; i < num; i++) { for (int i = 0; i < num; i++) {
char deviceName[MediaEngineSource::kMaxDeviceNameLength]; char deviceName[MediaEngineSource::kMaxDeviceNameLength];
char uniqueId[MediaEngineSource::kMaxUniqueIdLength]; char uniqueId[MediaEngineSource::kMaxUniqueIdLength];
bool scaryWindow = false;
// paranoia // paranoia
deviceName[0] = '\0'; deviceName[0] = '\0';
@ -215,6 +219,16 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
#ifdef DEBUG #ifdef DEBUG
LOG((" Capture Device Index %d, Name %s", i, deviceName)); LOG((" Capture Device Index %d, Name %s", i, deviceName));
if (aMediaSource == dom::MediaSourceEnum::Window) {
// TODO: Detect firefox windows
//scaryWindow = true;
}
if (aMediaSource == dom::MediaSourceEnum::Application) {
// TODO: Detect firefox application windows
//scaryWindow = true;
}
webrtc::CaptureCapability cap; webrtc::CaptureCapability cap;
int numCaps = mozilla::camera::GetChildAndCall( int numCaps = mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::NumberOfCapabilities, &mozilla::camera::CamerasChild::NumberOfCapabilities,
@ -247,7 +261,8 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
static_cast<MediaEngineRemoteVideoSource*>(vSource.get())->Refresh(i); static_cast<MediaEngineRemoteVideoSource*>(vSource.get())->Refresh(i);
aVSources->AppendElement(vSource.get()); aVSources->AppendElement(vSource.get());
} else { } else {
vSource = new MediaEngineRemoteVideoSource(i, capEngine, aMediaSource); vSource = new MediaEngineRemoteVideoSource(i, capEngine, aMediaSource,
scaryKind || scaryWindow);
mVideoSources.Put(uuid, vSource); // Hashtable takes ownership. mVideoSources.Put(uuid, vSource); // Hashtable takes ownership.
aVSources->AppendElement(vSource); aVSources->AppendElement(vSource);
} }

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

@ -165,8 +165,9 @@ BufferTextureData::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
return nullptr; return nullptr;
} }
bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV, bool hasIntermediateBuffer = aAllocator ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
aAllocator->GetCompositorBackendType()); aAllocator->GetCompositorBackendType())
: true;
// Initialize the metadata with something, even if it will have to be rewritten // Initialize the metadata with something, even if it will have to be rewritten
// afterwards since we don't know the dimensions of the texture at this point. // afterwards since we don't know the dimensions of the texture at this point.
@ -175,8 +176,8 @@ BufferTextureData::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
aYUVColorSpace, aYUVColorSpace,
hasIntermediateBuffer); hasIntermediateBuffer);
return CreateInternal(aAllocator->GetTextureForwarder(), desc, gfx::BackendType::NONE, aBufferSize, return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder() : nullptr,
aTextureFlags); desc, gfx::BackendType::NONE, aBufferSize, aTextureFlags);
} }
BufferTextureData* BufferTextureData*

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

@ -32,8 +32,8 @@ function* test(testDriver) {
testDriver(); testDriver();
}; };
yield synthesizeNativeTouch(v, 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT); yield synthesizeNativeTouch(v, 25, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT);
yield synthesizeNativeTouch(v, 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE); yield synthesizeNativeTouch(v, 25, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE);
ok(v._gotTouchend, 'Touchend was received on video element'); ok(v._gotTouchend, 'Touchend was received on video element');
yield synthesizeNativeTouch(a, 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT); yield synthesizeNativeTouch(a, 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT);

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

@ -28,20 +28,21 @@ window.addEventListener("load", () => {
target.style.animation = "Opacity0 100s 100s"; target.style.animation = "Opacity0 100s 100s";
// We need to wait for MozAfterPaint instead of requestAnimationFrame to // We need to wait for MozAfterPaint instead of requestAnimationFrame to
// ensure the stacking context has been updated (removed) on the compositor // ensure the stacking context has been updated on the compositor.
// before we snapshot.
window.addEventListener("MozAfterPaint", function firstPaint() { window.addEventListener("MozAfterPaint", function firstPaint() {
window.removeEventListener("MozAfterPaint", firstPaint, false); window.removeEventListener("MozAfterPaint", firstPaint, false);
// Here we have CSS animation on 100% opacity style element, so // Here we have CSS animation on 100% opacity style element, so
// there should be a stacking context. // there should be a stacking context.
target.style.animation = ""; target.style.animation = "";
window.addEventListener("MozAfterPaint", function secondPaint() {
window.removeEventListener("MozAfterPaint", secondPaint, false); // This time we don't need to wait for MozAfterPaint because reftest tool
// will be received MozAferPaint event.
requestAnimationFrame(() => {
// Now we have only 100% opacity style, so we should not create any // Now we have only 100% opacity style, so we should not create any
// stacking context. // stacking context.
document.documentElement.classList.remove("reftest-wait"); document.documentElement.classList.remove("reftest-wait");
}, false); });
}, false); }, false);
}); });
</script> </script>

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

@ -28,20 +28,20 @@ window.addEventListener("load", () => {
target.style.animation = "TransformNone 100s 100s"; target.style.animation = "TransformNone 100s 100s";
// We need to wait for MozAfterPaint instead of requestAnimationFrame to // We need to wait for MozAfterPaint instead of requestAnimationFrame to
// ensure the stacking context has been updated (removed) on the compositor // ensure the stacking context has been updated on the compositor.
// before we snapshot.
window.addEventListener("MozAfterPaint", function firstPaint() { window.addEventListener("MozAfterPaint", function firstPaint() {
window.removeEventListener("MozAfterPaint", firstPaint, false); window.removeEventListener("MozAfterPaint", firstPaint, false);
// Here we have CSS animation on transform:none style element, so // Here we have CSS animation on transform:none style element, so
// there should be a stacking context. // there should be a stacking context.
target.style.animation = ""; target.style.animation = "";
window.addEventListener("MozAfterPaint", function secondPaint() { // This time we don't need to wait for MozAfterPaint because reftest tool
window.removeEventListener("MozAfterPaint", secondPaint, false); // will be received MozAferPaint event.
requestAnimationFrame(() => {
// Now we have only transform:none style, so we should not create any // Now we have only transform:none style, so we should not create any
// stacking context. // stacking context.
document.documentElement.classList.remove("reftest-wait"); document.documentElement.classList.remove("reftest-wait");
}, false); });
}, false); }, false);
}); });
</script> </script>

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

@ -40,50 +40,71 @@ nsCSSFilterInstance::nsCSSFilterInstance(const nsStyleFilter& aFilter,
} }
nsresult nsresult
nsCSSFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) nsCSSFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
bool aInputIsTainted)
{ {
FilterPrimitiveDescription descr; FilterPrimitiveDescription descr;
nsresult result; nsresult result;
switch(mFilter.GetType()) { switch(mFilter.GetType()) {
case NS_STYLE_FILTER_BLUR: case NS_STYLE_FILTER_BLUR:
descr = CreatePrimitiveDescription(PrimitiveType::GaussianBlur, aPrimitiveDescrs); descr = CreatePrimitiveDescription(PrimitiveType::GaussianBlur,
aPrimitiveDescrs,
aInputIsTainted);
result = SetAttributesForBlur(descr); result = SetAttributesForBlur(descr);
break; break;
case NS_STYLE_FILTER_BRIGHTNESS: case NS_STYLE_FILTER_BRIGHTNESS:
descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs); descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
aPrimitiveDescrs,
aInputIsTainted);
result = SetAttributesForBrightness(descr); result = SetAttributesForBrightness(descr);
break; break;
case NS_STYLE_FILTER_CONTRAST: case NS_STYLE_FILTER_CONTRAST:
descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs); descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
aPrimitiveDescrs,
aInputIsTainted);
result = SetAttributesForContrast(descr); result = SetAttributesForContrast(descr);
break; break;
case NS_STYLE_FILTER_DROP_SHADOW: case NS_STYLE_FILTER_DROP_SHADOW:
descr = CreatePrimitiveDescription(PrimitiveType::DropShadow, aPrimitiveDescrs); descr = CreatePrimitiveDescription(PrimitiveType::DropShadow,
aPrimitiveDescrs,
aInputIsTainted);
result = SetAttributesForDropShadow(descr); result = SetAttributesForDropShadow(descr);
break; break;
case NS_STYLE_FILTER_GRAYSCALE: case NS_STYLE_FILTER_GRAYSCALE:
descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs); descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
aPrimitiveDescrs,
aInputIsTainted);
result = SetAttributesForGrayscale(descr); result = SetAttributesForGrayscale(descr);
break; break;
case NS_STYLE_FILTER_HUE_ROTATE: case NS_STYLE_FILTER_HUE_ROTATE:
descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs); descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
aPrimitiveDescrs,
aInputIsTainted);
result = SetAttributesForHueRotate(descr); result = SetAttributesForHueRotate(descr);
break; break;
case NS_STYLE_FILTER_INVERT: case NS_STYLE_FILTER_INVERT:
descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs); descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
aPrimitiveDescrs,
aInputIsTainted);
result = SetAttributesForInvert(descr); result = SetAttributesForInvert(descr);
break; break;
case NS_STYLE_FILTER_OPACITY: case NS_STYLE_FILTER_OPACITY:
descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs); descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
aPrimitiveDescrs,
aInputIsTainted);
result = SetAttributesForOpacity(descr); result = SetAttributesForOpacity(descr);
break; break;
case NS_STYLE_FILTER_SATURATE: case NS_STYLE_FILTER_SATURATE:
descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs); descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
aPrimitiveDescrs,
aInputIsTainted);
result = SetAttributesForSaturate(descr); result = SetAttributesForSaturate(descr);
break; break;
case NS_STYLE_FILTER_SEPIA: case NS_STYLE_FILTER_SEPIA:
descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs); descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
aPrimitiveDescrs,
aInputIsTainted);
result = SetAttributesForSepia(descr); result = SetAttributesForSepia(descr);
break; break;
default: default:
@ -106,11 +127,12 @@ nsCSSFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrim
FilterPrimitiveDescription FilterPrimitiveDescription
nsCSSFilterInstance::CreatePrimitiveDescription(PrimitiveType aType, nsCSSFilterInstance::CreatePrimitiveDescription(PrimitiveType aType,
const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) { const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
bool aInputIsTainted) {
FilterPrimitiveDescription descr(aType); FilterPrimitiveDescription descr(aType);
int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs); int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs);
descr.SetInputPrimitive(0, inputIndex); descr.SetInputPrimitive(0, inputIndex);
descr.SetIsTainted(inputIndex < 0 ? true : aPrimitiveDescrs[inputIndex].IsTainted()); descr.SetIsTainted(inputIndex < 0 ? aInputIsTainted : aPrimitiveDescrs[inputIndex].IsTainted());
descr.SetInputColorSpace(0, ColorSpace::SRGB); descr.SetInputColorSpace(0, ColorSpace::SRGB);
descr.SetOutputColorSpace(ColorSpace::SRGB); descr.SetOutputColorSpace(ColorSpace::SRGB);
return descr; return descr;

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

@ -50,15 +50,25 @@ public:
* Creates at least one new FilterPrimitiveDescription based on the filter * Creates at least one new FilterPrimitiveDescription based on the filter
* from the style system. Appends the new FilterPrimitiveDescription(s) to the * from the style system. Appends the new FilterPrimitiveDescription(s) to the
* aPrimitiveDescrs list. * aPrimitiveDescrs list.
* aInputIsTainted describes whether the input to this filter is tainted, i.e.
* whether it contains security-sensitive content. This is needed to propagate
* taintedness to the FilterPrimitive that take tainted inputs. Something being
* tainted means that it contains security sensitive content.
* The input to this filter is the previous filter's output, i.e. the last
* element in aPrimitiveDescrs, or the SourceGraphic input if this is the first
* filter in the filter chain.
*/ */
nsresult BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs); nsresult BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
bool aInputIsTainted);
private: private:
/** /**
* Returns a new FilterPrimitiveDescription with its basic properties set up. * Returns a new FilterPrimitiveDescription with its basic properties set up.
* See the comment above BuildPrimitives for the meaning of aInputIsTainted.
*/ */
FilterPrimitiveDescription CreatePrimitiveDescription(PrimitiveType aType, FilterPrimitiveDescription CreatePrimitiveDescription(PrimitiveType aType,
const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs); const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
bool aInputIsTainted);
/** /**
* Sets aDescr's attributes using the style info in mFilter. * Sets aDescr's attributes using the style info in mFilter.

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

@ -32,14 +32,15 @@ using namespace mozilla::gfx;
FilterDescription FilterDescription
nsFilterInstance::GetFilterDescription(nsIContent* aFilteredElement, nsFilterInstance::GetFilterDescription(nsIContent* aFilteredElement,
const nsTArray<nsStyleFilter>& aFilterChain, const nsTArray<nsStyleFilter>& aFilterChain,
bool aFilterInputIsTainted,
const UserSpaceMetrics& aMetrics, const UserSpaceMetrics& aMetrics,
const gfxRect& aBBox, const gfxRect& aBBox,
nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages) nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages)
{ {
gfxMatrix unused; // aPaintTransform arg not used since we're not painting gfxMatrix unused; // aPaintTransform arg not used since we're not painting
nsFilterInstance instance(nullptr, aFilteredElement, aMetrics, nsFilterInstance instance(nullptr, aFilteredElement, aMetrics,
aFilterChain, nullptr, unused, aFilterChain, aFilterInputIsTainted, nullptr,
nullptr, nullptr, nullptr, &aBBox); unused, nullptr, nullptr, nullptr, &aBBox);
if (!instance.IsInitialized()) { if (!instance.IsInitialized()) {
return FilterDescription(); return FilterDescription();
} }
@ -65,9 +66,11 @@ nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame,
{ {
auto& filterChain = aFilteredFrame->StyleEffects()->mFilters; auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame); UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
// Hardcode InputIsTainted to true because we don't want JS to be able to
// read the rendered contents of aFilteredFrame.
nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics, nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
filterChain, aPaintCallback, aTransform, filterChain, /* InputIsTainted */ true, aPaintCallback,
aDirtyArea, nullptr, nullptr, nullptr); aTransform, aDirtyArea, nullptr, nullptr, nullptr);
if (!instance.IsInitialized()) { if (!instance.IsInitialized()) {
return NS_OK; return NS_OK;
} }
@ -85,9 +88,11 @@ nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
gfxMatrix unused; // aPaintTransform arg not used since we're not painting gfxMatrix unused; // aPaintTransform arg not used since we're not painting
auto& filterChain = aFilteredFrame->StyleEffects()->mFilters; auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame); UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
// Hardcode InputIsTainted to true because we don't want JS to be able to
// read the rendered contents of aFilteredFrame.
nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics, nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
filterChain, nullptr, unused, nullptr, filterChain, /* InputIsTainted */ true, nullptr, unused,
&aPreFilterDirtyRegion); nullptr, &aPreFilterDirtyRegion);
if (!instance.IsInitialized()) { if (!instance.IsInitialized()) {
return nsRegion(); return nsRegion();
} }
@ -105,8 +110,10 @@ nsFilterInstance::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
gfxMatrix unused; // aPaintTransform arg not used since we're not painting gfxMatrix unused; // aPaintTransform arg not used since we're not painting
auto& filterChain = aFilteredFrame->StyleEffects()->mFilters; auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame); UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
// Hardcode InputIsTainted to true because we don't want JS to be able to
// read the rendered contents of aFilteredFrame.
nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics, nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
filterChain, nullptr, unused, filterChain, /* InputIsTainted */ true, nullptr, unused,
&aPostFilterDirtyRegion); &aPostFilterDirtyRegion);
if (!instance.IsInitialized()) { if (!instance.IsInitialized()) {
return nsRect(); return nsRect();
@ -136,9 +143,11 @@ nsFilterInstance::GetPostFilterBounds(nsIFrame *aFilteredFrame,
gfxMatrix unused; // aPaintTransform arg not used since we're not painting gfxMatrix unused; // aPaintTransform arg not used since we're not painting
auto& filterChain = aFilteredFrame->StyleEffects()->mFilters; auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame); UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
// Hardcode InputIsTainted to true because we don't want JS to be able to
// read the rendered contents of aFilteredFrame.
nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics, nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
filterChain, nullptr, unused, nullptr, filterChain, /* InputIsTainted */ true, nullptr, unused,
preFilterRegionPtr, aPreFilterBounds, nullptr, preFilterRegionPtr, aPreFilterBounds,
aOverrideBBox); aOverrideBBox);
if (!instance.IsInitialized()) { if (!instance.IsInitialized()) {
return nsRect(); return nsRect();
@ -151,6 +160,7 @@ nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
nsIContent* aTargetContent, nsIContent* aTargetContent,
const UserSpaceMetrics& aMetrics, const UserSpaceMetrics& aMetrics,
const nsTArray<nsStyleFilter>& aFilterChain, const nsTArray<nsStyleFilter>& aFilterChain,
bool aFilterInputIsTainted,
nsSVGFilterPaintCallback *aPaintCallback, nsSVGFilterPaintCallback *aPaintCallback,
const gfxMatrix& aPaintTransform, const gfxMatrix& aPaintTransform,
const nsRegion *aPostFilterDirtyRegion, const nsRegion *aPostFilterDirtyRegion,
@ -213,7 +223,7 @@ nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
mTargetBounds.UnionRect(mTargetBBoxInFilterSpace, targetBounds); mTargetBounds.UnionRect(mTargetBBoxInFilterSpace, targetBounds);
// Build the filter graph. // Build the filter graph.
rv = BuildPrimitives(aFilterChain, aTargetFrame); rv = BuildPrimitives(aFilterChain, aTargetFrame, aFilterInputIsTainted);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return; return;
} }
@ -274,13 +284,17 @@ nsFilterInstance::FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const
nsresult nsresult
nsFilterInstance::BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain, nsFilterInstance::BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain,
nsIFrame* aTargetFrame) nsIFrame* aTargetFrame,
bool aFilterInputIsTainted)
{ {
NS_ASSERTION(!mPrimitiveDescriptions.Length(), NS_ASSERTION(!mPrimitiveDescriptions.Length(),
"expected to start building primitives from scratch"); "expected to start building primitives from scratch");
for (uint32_t i = 0; i < aFilterChain.Length(); i++) { for (uint32_t i = 0; i < aFilterChain.Length(); i++) {
nsresult rv = BuildPrimitivesForFilter(aFilterChain[i], aTargetFrame); bool inputIsTainted =
mPrimitiveDescriptions.IsEmpty() ? aFilterInputIsTainted :
mPrimitiveDescriptions.LastElement().IsTainted();
nsresult rv = BuildPrimitivesForFilter(aFilterChain[i], aTargetFrame, inputIsTainted);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -293,7 +307,8 @@ nsFilterInstance::BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain,
nsresult nsresult
nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter, nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter,
nsIFrame* aTargetFrame) nsIFrame* aTargetFrame,
bool aInputIsTainted)
{ {
NS_ASSERTION(mUserSpaceToFilterSpaceScale.width > 0.0f && NS_ASSERTION(mUserSpaceToFilterSpaceScale.width > 0.0f &&
mFilterSpaceToUserSpaceScale.height > 0.0f, mFilterSpaceToUserSpaceScale.height > 0.0f,
@ -310,7 +325,8 @@ nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages); return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages,
aInputIsTainted);
} }
// Build primitives for a CSS filter. // Build primitives for a CSS filter.
@ -323,7 +339,7 @@ nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter,
nsCSSFilterInstance cssFilterInstance(aFilter, shadowFallbackColor, nsCSSFilterInstance cssFilterInstance(aFilter, shadowFallbackColor,
mTargetBounds, mTargetBounds,
mFrameSpaceInCSSPxToFilterSpaceTransform); mFrameSpaceInCSSPxToFilterSpaceTransform);
return cssFilterInstance.BuildPrimitives(mPrimitiveDescriptions); return cssFilterInstance.BuildPrimitives(mPrimitiveDescriptions, aInputIsTainted);
} }
void void

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

@ -60,12 +60,19 @@ public:
/** /**
* Create a FilterDescription for the supplied filter. All coordinates in * Create a FilterDescription for the supplied filter. All coordinates in
* the description are in filter space. * the description are in filter space.
* @param aFilterInputIsTainted Describes whether the SourceImage / SourceAlpha
* input is tainted. This affects whether feDisplacementMap will respect
* the filter input as its map input, and it affects the IsTainted() state
* on the filter primitives in the FilterDescription. "Tainted" is a term
* from the filters spec and means security-sensitive content, i.e. pixels
* that JS should not be able to read in any way.
* @param aOutAdditionalImages Will contain additional images needed to * @param aOutAdditionalImages Will contain additional images needed to
* render the filter (from feImage primitives). * render the filter (from feImage primitives).
* @return A FilterDescription describing the filter. * @return A FilterDescription describing the filter.
*/ */
static FilterDescription GetFilterDescription(nsIContent* aFilteredElement, static FilterDescription GetFilterDescription(nsIContent* aFilteredElement,
const nsTArray<nsStyleFilter>& aFilterChain, const nsTArray<nsStyleFilter>& aFilterChain,
bool aFilterInputIsTainted,
const UserSpaceMetrics& aMetrics, const UserSpaceMetrics& aMetrics,
const gfxRect& aBBox, const gfxRect& aBBox,
nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages); nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages);
@ -118,6 +125,9 @@ public:
* @param aTargetContent The filtered element itself. * @param aTargetContent The filtered element itself.
* @param aMetrics The metrics to resolve SVG lengths against. * @param aMetrics The metrics to resolve SVG lengths against.
* @param aFilterChain The list of filters to apply. * @param aFilterChain The list of filters to apply.
* @param aFilterInputIsTainted Describes whether the SourceImage / SourceAlpha
* input is tainted. This affects whether feDisplacementMap will respect
* the filter input as its map input.
* @param aPaintCallback [optional] The callback that Render() should use to * @param aPaintCallback [optional] The callback that Render() should use to
* paint. Only required if you will call Render(). * paint. Only required if you will call Render().
* @param aPaintTransform The transform to apply to convert to * @param aPaintTransform The transform to apply to convert to
@ -137,6 +147,7 @@ public:
nsIContent* aTargetContent, nsIContent* aTargetContent,
const UserSpaceMetrics& aMetrics, const UserSpaceMetrics& aMetrics,
const nsTArray<nsStyleFilter>& aFilterChain, const nsTArray<nsStyleFilter>& aFilterChain,
bool aFilterInputIsTainted,
nsSVGFilterPaintCallback *aPaintCallback, nsSVGFilterPaintCallback *aPaintCallback,
const gfxMatrix& aPaintTransform, const gfxMatrix& aPaintTransform,
const nsRegion *aPostFilterDirtyRegion = nullptr, const nsRegion *aPostFilterDirtyRegion = nullptr,
@ -236,18 +247,22 @@ private:
/** /**
* Build the list of FilterPrimitiveDescriptions that describes the filter's * Build the list of FilterPrimitiveDescriptions that describes the filter's
* filter primitives and their connections. This populates * filter primitives and their connections. This populates
* mPrimitiveDescriptions and mInputImages. * mPrimitiveDescriptions and mInputImages. aFilterInputIsTainted describes
* whether the SourceGraphic is tainted.
*/ */
nsresult BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain, nsresult BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain,
nsIFrame* aTargetFrame); nsIFrame* aTargetFrame,
bool aFilterInputIsTainted);
/** /**
* Add to the list of FilterPrimitiveDescriptions for a particular SVG * Add to the list of FilterPrimitiveDescriptions for a particular SVG
* reference filter or CSS filter. This populates mPrimitiveDescrs and * reference filter or CSS filter. This populates mPrimitiveDescriptions and
* mInputImages. * mInputImages. aInputIsTainted describes whether the input to aFilter is
* tainted.
*/ */
nsresult BuildPrimitivesForFilter(const nsStyleFilter& aFilter, nsresult BuildPrimitivesForFilter(const nsStyleFilter& aFilter,
nsIFrame* aTargetFrame); nsIFrame* aTargetFrame,
bool aInputIsTainted);
/** /**
* Computes the filter space bounds of the areas that we actually *need* from * Computes the filter space bounds of the areas that we actually *need* from

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

@ -366,23 +366,10 @@ nsSVGFilterInstance::GetSourceIndices(nsSVGFE* aPrimitiveElement,
return NS_OK; return NS_OK;
} }
static bool
IsFilterInputTainted(nsIContent* aElement)
{
// When the filter is applied during canvas drawing, we might be allowed to
// read from the canvas.
if (HTMLCanvasElement* canvas =
HTMLCanvasElement::FromContentOrNull(aElement)) {
return canvas->IsWriteOnly();
}
// Always treat normal filtered elements as tainted.
return true;
}
nsresult nsresult
nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs, nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
nsTArray<RefPtr<SourceSurface>>& aInputImages) nsTArray<RefPtr<SourceSurface>>& aInputImages,
bool aInputIsTainted)
{ {
mSourceGraphicIndex = GetLastResultIndex(aPrimitiveDescrs); mSourceGraphicIndex = GetLastResultIndex(aPrimitiveDescrs);
@ -410,8 +397,6 @@ nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrim
// The principal that we check principals of any loaded images against. // The principal that we check principals of any loaded images against.
nsCOMPtr<nsIPrincipal> principal = mTargetContent->NodePrincipal(); nsCOMPtr<nsIPrincipal> principal = mTargetContent->NodePrincipal();
bool filterInputIsTainted = IsFilterInputTainted(mTargetContent);
for (uint32_t primitiveElementIndex = 0; for (uint32_t primitiveElementIndex = 0;
primitiveElementIndex < primitives.Length(); primitiveElementIndex < primitives.Length();
++primitiveElementIndex) { ++primitiveElementIndex) {
@ -427,7 +412,7 @@ nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrim
ComputeFilterPrimitiveSubregion(filter, aPrimitiveDescrs, sourceIndices); ComputeFilterPrimitiveSubregion(filter, aPrimitiveDescrs, sourceIndices);
nsTArray<bool> sourcesAreTainted; nsTArray<bool> sourcesAreTainted;
GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, filterInputIsTainted, sourcesAreTainted); GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, aInputIsTainted, sourcesAreTainted);
FilterPrimitiveDescription descr = FilterPrimitiveDescription descr =
filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, aInputImages); filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, aInputImages);

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

@ -98,9 +98,17 @@ public:
* FilterPrimitiveDescription for each one. Appends the new * FilterPrimitiveDescription for each one. Appends the new
* FilterPrimitiveDescription(s) to the aPrimitiveDescrs list. Also, appends * FilterPrimitiveDescription(s) to the aPrimitiveDescrs list. Also, appends
* new images from feImage filter primitive elements to the aInputImages list. * new images from feImage filter primitive elements to the aInputImages list.
* aInputIsTainted describes whether the input to this filter is tainted, i.e.
* whether it contains security-sensitive content. This is needed to propagate
* taintedness to the FilterPrimitive that take tainted inputs. Something being
* tainted means that it contains security sensitive content.
* The input to this filter is the previous filter's output, i.e. the last
* element in aPrimitiveDescrs, or the SourceGraphic input if this is the first
* filter in the filter chain.
*/ */
nsresult BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs, nsresult BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
nsTArray<RefPtr<SourceSurface>>& aInputImages); nsTArray<RefPtr<SourceSurface>>& aInputImages,
bool aInputIsTainted);
/** /**
* Returns the user specified "filter region", in the filtered element's user * Returns the user specified "filter region", in the filtered element's user

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

@ -51,7 +51,7 @@ win32/pgo:
worker-type: aws-provisioner-v1/gecko-{level}-b-win2012 worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
worker: worker:
implementation: generic-worker implementation: generic-worker
max-run-time: 7200 max-run-time: 9000
run: run:
using: mozharness using: mozharness
options: [enable-pgo] options: [enable-pgo]
@ -112,7 +112,7 @@ win64/pgo:
worker-type: aws-provisioner-v1/gecko-{level}-b-win2012 worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
worker: worker:
implementation: generic-worker implementation: generic-worker
max-run-time: 7200 max-run-time: 9000
run: run:
using: mozharness using: mozharness
options: [enable-pgo] options: [enable-pgo]

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

@ -211,10 +211,17 @@ def mozharness_on_windows(config, job, taskdesc):
mh_command.append(r'--skip-buildbot-actions --work-dir %cd:Z:=z:%\build') mh_command.append(r'--skip-buildbot-actions --work-dir %cd:Z:=z:%\build')
for option in run.get('options', []): for option in run.get('options', []):
mh_command.append('--' + option) mh_command.append('--' + option)
hg = r'c:\Program Files\Mercurial\hg.exe'
hg_command = ['"c:\\Program Files\\Mercurial\\hg.exe"']
hg_command.append('robustcheckout')
hg_command.extend(['--sharebase', 'y:\\hg-shared'])
hg_command.append('--purge')
hg_command.extend(['--upstream', 'https://hg.mozilla.org/mozilla-unified'])
hg_command.extend(['--revision', env['GECKO_HEAD_REV']])
hg_command.append(env['GECKO_HEAD_REPOSITORY'])
hg_command.append('.\\build\\src')
worker['command'] = [ worker['command'] = [
r'mkdir .\build\src', ' '.join(hg_command),
r'"{}" share c:\builds\hg-shared\mozilla-central .\build\src'.format(hg), ' '.join(mh_command)
r'"{}" pull -u -R .\build\src --rev %GECKO_HEAD_REV% %GECKO_HEAD_REPOSITORY%'.format(hg),
' '.join(mh_command),
] ]

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

@ -36,12 +36,4 @@ def setup_task(config, tasks):
'mount-point': "/home/worker/.tc-vcs", 'mount-point': "/home/worker/.tc-vcs",
}] }]
if int(config.params['level']) > 1:
task['worker'].setdefault('caches', []).append({
'type': 'persistent',
'name': 'level-{}-{}-test-workspace'.format(
config.params['level'], config.params['project']),
'mount-point': "/home/worker/workspace",
})
yield task yield task

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

@ -30,6 +30,10 @@ import urllib2
FINGERPRINT_URL = 'http://taskcluster/secrets/v1/secret/project/taskcluster/gecko/hgfingerprint' FINGERPRINT_URL = 'http://taskcluster/secrets/v1/secret/project/taskcluster/gecko/hgfingerprint'
FALLBACK_FINGERPRINT = {
'fingerprints':
"sha256:8e:ad:f7:6a:eb:44:06:15:ed:f3:e4:69:a6:64:60:37:2d:ff:98:88:37"
":bf:d7:b8:40:84:01:48:9c:26:ce:d9"}
def print_line(prefix, m): def print_line(prefix, m):
@ -95,15 +99,17 @@ def vcs_checkout(source_repo, dest, base_repo=None, revision=None, branch=None):
FINGERPRINT_URL) FINGERPRINT_URL)
res = urllib2.urlopen(FINGERPRINT_URL, timeout=10) res = urllib2.urlopen(FINGERPRINT_URL, timeout=10)
secret = res.read() secret = res.read()
except urllib2.URLError as e: try:
print('error retrieving hg fingerprint: %s' % e) secret = json.loads(secret, encoding='utf-8')
sys.exit(1) except ValueError:
print_line(b'vcs', 'invalid JSON in hg fingerprint secret')
try: sys.exit(1)
secret = json.loads(secret, encoding='utf-8') except urllib2.URLError:
except ValueError: print_line(b'vcs', 'Unable to retrieve current hg.mozilla.org fingerprint'
print('invalid JSON in hg fingerprint secret') 'using the secret service, using fallback instead.')
sys.exit(1) # XXX This fingerprint will not be accurate if running on an old
# revision after the server fingerprint has changed.
secret = {'secret': FALLBACK_FINGERPRINT}
hgmo_fingerprint = secret['secret']['fingerprints'].encode('ascii') hgmo_fingerprint = secret['secret']['fingerprints'].encode('ascii')
@ -193,6 +199,7 @@ def main(args):
gids = [g.gr_gid for g in grp.getgrall() if args.group in g.gr_mem] gids = [g.gr_gid for g in grp.getgrall() if args.group in g.gr_mem]
wanted_dir_mode = stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR wanted_dir_mode = stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR
def set_dir_permissions(path, uid, gid): def set_dir_permissions(path, uid, gid):
st = os.lstat(path) st = os.lstat(path)

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

@ -3,7 +3,7 @@ import os
config = { config = {
"locales_file": "src/browser/locales/all-locales", "locales_file": "src/browser/locales/all-locales",
"tools_repo": "https://hg.mozilla.org/build/tools", "tools_repo": "https://hg.mozilla.org/build/tools",
"mozconfig": "src/browser/config/mozconfigs/linux64/l10n-mozconfig", "mozconfig": "src/browser/config/mozconfigs/linux32/l10n-mozconfig",
"bootstrap_env": { "bootstrap_env": {
"NO_MERCURIAL_SETUP_CHECK": "1", "NO_MERCURIAL_SETUP_CHECK": "1",
"MOZ_OBJDIR": "obj-l10n", "MOZ_OBJDIR": "obj-l10n",

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

@ -2188,16 +2188,10 @@ TextInputHandler::InsertText(NSAttributedString* aAttrString,
return; return;
} }
if (str.Length() != 1 || IsIMEComposing()) { // If this is not caused by pressing a key or there is a composition, let's
// insert the text as committing a composition.
if (!currentKeyEvent || IsIMEComposing()) {
InsertTextAsCommittingComposition(aAttrString, aReplacementRange); InsertTextAsCommittingComposition(aAttrString, aReplacementRange);
// For now, consume keypress events when we dispatch the string with a
// composition for preventing to dispatch keypress events later.
// TODO: When there is a currentKeyEvent, we should dispatch keypress
// events even if the length of the string is over 1.
if (currentKeyEvent) {
currentKeyEvent->mKeyPressHandled = true;
currentKeyEvent->mKeyPressDispatched = true;
}
return; return;
} }

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

@ -1906,6 +1906,21 @@ function* runKeyEventTests()
yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_I, yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_I,
modifiers:{metaKey:1, altKey:1, shiftKey:1}, chars:"\u02C6", unmodifiedChars:"C"}, modifiers:{metaKey:1, altKey:1, shiftKey:1}, chars:"\u02C6", unmodifiedChars:"C"},
"\u02C6", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "\u02C6", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); "\u02C6", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "\u02C6", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
// Arabic - PC keyboard layout inputs 2 or more characters with some key.
yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_G,
modifiers:{shiftKey:1}, chars:"\u0644\u0623", unmodifiedChars:"\u0644\u0623"},
["\u0644\u0623", "\u0644", "\u0623", "\u0644\u0623"], "KeyG", nsIDOMKeyEvent.DOM_VK_G, "\u0644\u0623", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_T,
modifiers:{shiftKey:1}, chars:"\u0644\u0625", unmodifiedChars:"\u0644\u0625"},
["\u0644\u0625", "\u0644", "\u0625", "\u0644\u0625"], "KeyT", nsIDOMKeyEvent.DOM_VK_T, "\u0644\u0625", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_B,
modifiers:{shiftKey:1}, chars:"\u0644\u0622", unmodifiedChars:"\u0644\u0622"},
["\u0644\u0622", "\u0644", "\u0622", "\u0644\u0622"], "KeyB", nsIDOMKeyEvent.DOM_VK_B, "\u0644\u0622", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_B,
modifiers:{}, chars:"\u0644\u0627", unmodifiedChars:"\u0644\u0627"},
["\u0644\u0627", "\u0644", "\u0627", "\u0644\u0627"], "KeyB", nsIDOMKeyEvent.DOM_VK_B, "\u0644\u0627", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
cleanup(); cleanup();
} }
@ -4197,11 +4212,37 @@ function* runTextInputTests()
textbox.value = ""; textbox.value = "";
textbox.focus(); textbox.focus();
var name = eventToString(aEvent);
// Check if the text comes with keypress events rather than composition events.
var keypress = 0;
function onKeypress(aEvent) {
keypress++;
if (keypress == 1 && aExpectText == "") {
if (!aEvent.ctrlKey && !aEvent.altKey) {
is(aEvent.charCode, 0, name + ", the charCode value should be 0 when it shouldn't cause inputting text");
}
return;
}
if (keypress > aExpectText.length) {
ok(false, name + " causes too many keypress events");
return;
}
is(aEvent.key, aExpectText[keypress - 1],
name + ", " + keypress + "th keypress event's key value should be '" + aExpectText[keypress - 1] + "'");
is(aEvent.charCode, aExpectText.charCodeAt(keypress - 1),
name + ", " + keypress + "th keypress event's charCode value should be 0x" + parseInt(aExpectText.charCodeAt(keypress - 1), 16));
}
textbox.addEventListener("keypress", onKeypress, true);
return synthesizeKey(aEvent, "textbox", function() { return synthesizeKey(aEvent, "textbox", function() {
textbox.removeEventListener("keypress", onKeypress, true);
var name = eventToString(aEvent); if (aExpectText == "") {
is(keypress, 1, name + " should cause one keypress event because it doesn't cause inputting text");
is(textbox.value, aExpectText, name + " does not input correct text."); } else {
is(keypress, aExpectText.length, name + " should cause " + aExpectText.length + " keypress events");
is(textbox.value, aExpectText, name + " does not input correct text.");
}
continueTest(); continueTest();
}); });