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();
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 expectNoObserverCalled();
yield checkNotSharing();

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

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

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

@ -110,9 +110,6 @@ var gTests = [
yield promiseReloadFrame("frame1");
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 expectNoObserverCalled();
yield checkNotSharing();
@ -188,9 +185,6 @@ var gTests = [
yield expectObserverCalled("recording-window-ended");
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 closeStream(false, "frame1");
@ -225,9 +219,6 @@ var gTests = [
content.location.reload();
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 expectNoObserverCalled();
yield checkNotSharing();

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

@ -51,11 +51,8 @@ var gTests = [
gBrowser.selectedBrowser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
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-device-events");
yield expectNoObserverCalled();
yield checkNotSharing();
}

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

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

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

@ -34,13 +34,6 @@ addMessageListener("Test:ExpectObserverCalled", ({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 => {
sendAsyncMessage("Test:ExpectNoObserverCalled:Reply", 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) {
let promise = new Promise((resolve, reject) => {
let mm = _mm();
@ -357,11 +345,6 @@ function* stopSharing(aType = "camera") {
yield promiseRecordingEvent;
yield expectObserverCalled("getUserMedia:revoke");
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* checkNotSharing();
}
@ -398,12 +381,6 @@ function* closeStream(aAlreadyClosed, aFrameId) {
if (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);
}

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

@ -93,13 +93,15 @@ exports.getHighlighterUtils = function (toolbox) {
/**
* 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
*/
let togglePicker = exported.togglePicker = function () {
let togglePicker = exported.togglePicker = function (doFocus) {
if (isPicking) {
return stopPicker();
return cancelPicker();
} else {
return startPicker();
return startPicker(doFocus);
}
};
@ -109,10 +111,12 @@ exports.getHighlighterUtils = function (toolbox) {
* on the target page to highlight the hovered/picked element.
* Depending on the server-side capabilities, this may fire events when nodes
* 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
* if it is already started
*/
let startPicker = exported.startPicker = requireInspector(function* () {
let startPicker = exported.startPicker = requireInspector(function* (doFocus = false) {
if (isPicking) {
return;
}
@ -120,14 +124,14 @@ exports.getHighlighterUtils = function (toolbox) {
toolbox.pickerButtonChecked = true;
yield toolbox.selectTool("inspector");
toolbox.on("select", stopPicker);
toolbox.on("select", cancelPicker);
if (isRemoteHighlightable()) {
toolbox.walker.on("picker-node-hovered", onPickerNodeHovered);
toolbox.walker.on("picker-node-picked", onPickerNodePicked);
toolbox.walker.on("picker-node-canceled", onPickerNodeCanceled);
yield toolbox.highlighter.pick();
yield toolbox.highlighter.pick(doFocus);
toolbox.emit("picker-started");
} else {
// 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();
}
toolbox.off("select", stopPicker);
toolbox.off("select", cancelPicker);
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
* @param {Object} data Information about the node being hovered
@ -190,7 +202,7 @@ exports.getHighlighterUtils = function (toolbox) {
* gets the focus.
*/
function onPickerNodeCanceled() {
stopPicker();
cancelPicker();
toolbox.win.focus();
}

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

@ -123,6 +123,10 @@ function Toolbox(target, selectedTool, hostType, hostOptions) {
this._toggleMinimizeMode = this._toggleMinimizeMode.bind(this);
this._onTabbarFocus = this._onTabbarFocus.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);
@ -149,6 +153,9 @@ function Toolbox(target, selectedTool, hostType, hostOptions) {
gDevTools.on("tool-registered", this._toolRegistered);
gDevTools.on("tool-unregistered", this._toolUnregistered);
this.on("picker-started", this._onPickerStarted);
this.on("picker-stopped", this._onPickerStopped);
}
exports.Toolbox = Toolbox;
@ -984,8 +991,39 @@ Toolbox.prototype = {
let container = this.doc.querySelector("#toolbox-picker-container");
container.appendChild(this._pickerButton);
this._togglePicker = this.highlighterUtils.togglePicker.bind(this.highlighterUtils);
this._pickerButton.addEventListener("click", this._togglePicker, false);
this._pickerButton.addEventListener("click", this._onPickerClick, 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/tabs/tabs.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/shared/components/splitter/split-box.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._onMouseMove = this._onMouseMove.bind(this);
this._onMouseOut = this._onMouseOut.bind(this);
this._onToolboxPickerCanceled = this._onToolboxPickerCanceled.bind(this);
this._onToolboxPickerHover = this._onToolboxPickerHover.bind(this);
this._onCollapseAttributesPrefChange =
this._onCollapseAttributesPrefChange.bind(this);
@ -127,6 +128,7 @@ function MarkupView(inspector, frame, controllerWindow) {
this.walker.on("mutations", this._mutationObserver);
this.walker.on("display-change", this._onDisplayChange);
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._onNewSelection();
@ -189,6 +191,16 @@ MarkupView.prototype = {
}, 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,
_onMouseMove: function (event) {
@ -375,6 +387,9 @@ MarkupView.prototype = {
this.getContainer(nodeFront).hovered = true;
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) {

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

@ -26,6 +26,7 @@ support-files =
doc_inspector_infobar_02.html
doc_inspector_infobar_03.html
doc_inspector_infobar_textnode.html
doc_inspector_long-divs.html
doc_inspector_menu.html
doc_inspector_outerhtml.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-04.js]
[browser_inspector_highlighter-by-type.js]
[browser_inspector_highlighter-cancel.js]
[browser_inspector_highlighter-comments.js]
[browser_inspector_highlighter-cssgrid_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.
* @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");
toolbox.win.focus();
yield toolbox.highlighterUtils.startPicker();
// Make sure the content window is focused since the picker does not focus
// the content window by default.
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
content.focus();
});
if (!skipFocus) {
// By default make sure the content window is focused since the picker may not focus
// the content window by default.
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.
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
# 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

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

@ -76,6 +76,11 @@ const ResponsiveUIManager = exports.ResponsiveUIManager = {
this.showRemoteOnlyNotification(window, tab, options);
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)) {
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
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)) {
// Notification already displayed
return;
}
nbox.appendNotification(
getStr("responsive.remoteOnly"),
msg,
value,
null,
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-map.js',
'grip.js',
'infinity.js',
'nan.js',
'null.js',
'number.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) {
return type == "boolean" || type == "number" ||
(type == "object" && object.type == "-0");
return ["boolean", "number", "-0"].includes(type);
}
// Exports from this module

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

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

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

@ -16,6 +16,8 @@ support-files =
[test_reps_grip.html]
[test_reps_grip-array.html]
[test_reps_grip-map.html]
[test_reps_infinity.html]
[test_reps_nan.html]
[test_reps_null.html]
[test_reps_number.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");
useXulWrapper = true;
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);
}
yield runTests(doc);
});
function* runTests(doc) {

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

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

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

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

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

@ -353,6 +353,16 @@ var HighlighterActor = exports.HighlighterActor = protocol.ActorClassWithSpec(hi
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) {
// originalTarget allows access to the "real" element before any retargeting
// 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/. */
"use strict";
const { FrontClassWithSpec } = require("devtools/shared/protocol");
const { FrontClassWithSpec, custom } = require("devtools/shared/protocol");
const {
customHighlighterSpec,
highlighterSpec
@ -15,7 +15,16 @@ const HighlighterFront = FrontClassWithSpec(highlighterSpec, {
this.actorID = json.actor;
// FF42+ HighlighterActors starts exposing custom form, with traits object
this.traits = json.traits || {};
}
},
pick: custom(function (doFocus) {
if (doFocus && this.pickAndFocus) {
return this.pickAndFocus();
}
return this._pick();
}, {
impl: "_pick"
})
});
exports.HighlighterFront = HighlighterFront;

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

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

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

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

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

@ -264,12 +264,6 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
[test_filter.html]
skip-if = (e10s && debug && os == 'win')
[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]
subsuite = gpu
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">
<svg style="display: block; width: 0; height: 0">
<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"/>
</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>
</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>
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 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);
var checkSucceeded = r - d <= pr && pr <= r + d &&
g - d <= pg && pg <= g + d &&
b - d <= pb && pb <= b + d &&
a - d <= pa && pa <= a + 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() {
SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () {
var canvas = document.getElementById('c');
var sameOriginGreenImage = document.getElementById('same-origin-green');
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');
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.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');
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');
// cross-origin feImage should be ignored as map input to a displacement map, and taint the canvas.
ctx.filter = 'url(#use-cross-origin-green-feImage-as-map-on-same-origin-red)';
ctx.rect(0, 0, 16, 16);
ctx.fill();
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);
// Create new untainted canvas.
canvas = newCanvas();
ctx = canvas.getContext('2d');
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();
});

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

@ -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)
, mRemoved(false)
, mAudioStopped(false)
, mVideoStopped(false) {}
, mAudioStopPending(false)
, mVideoStopped(false)
, mVideoStopPending(false)
, mChromeNotificationTaskPosted(false)
{}
~GetUserMediaCallbackMediaStreamListener()
{
@ -302,6 +306,8 @@ public:
void StopTrack(TrackID aID);
void NotifyChromeOfTrackStops();
typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
already_AddRefed<PledgeVoid>
@ -494,10 +500,22 @@ private:
// MainThread only.
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.
// MainThread only.
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
// Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
@ -743,7 +761,8 @@ protected:
NS_IMPL_ISUPPORTS(MediaDevice, nsIMediaDevice)
MediaDevice::MediaDevice(MediaEngineSource* aSource, bool aIsVideo)
: mMediaSource(aSource->GetMediaSource())
: mScary(aSource->GetScary())
, mMediaSource(aSource->GetMediaSource())
, mSource(aSource)
, mIsVideo(aIsVideo)
{
@ -881,6 +900,13 @@ MediaDevice::GetRawId(nsAString& aID)
return NS_OK;
}
NS_IMETHODIMP
MediaDevice::GetScary(bool* aScary)
{
*aScary = mScary;
return NS_OK;
}
void
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",
aTrackID,
aTrackID == kAudioTrack ? "audio" : "video",
aTrackID == kAudioTrack ? !!mAudioDevice : !!mVideoDevice,
aTrackID == kAudioTrack ? mAudioStopped : mVideoStopped));
stopAudio ? "audio" : "video",
stopAudio ? !!mAudioDevice : !!mVideoDevice,
stopAudio ? mAudioStopped : mVideoStopped));
return;
}
@ -3558,6 +3584,59 @@ GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID)
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 =
new MediaOperationTask(MEDIA_STOP_TRACK,
this, nullptr, nullptr,
@ -3565,8 +3644,6 @@ GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID)
stopVideo ? mVideoDevice.get() : nullptr,
false , mWindowID, nullptr);
MediaManager::PostTask(mediaOperation.forget());
mAudioStopped |= stopAudio;
mVideoStopped |= stopVideo;
}
void

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

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

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

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

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

@ -214,6 +214,9 @@ public:
/* Populate the UUID of this device in the nsACString */
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
{
public:
@ -249,9 +252,14 @@ public:
return a.get() == b.get();
}
};
MOZ_ASSERT(mRegisteredHandles.Contains(handle, Comparator()));
mRegisteredHandles.RemoveElementAt(mRegisteredHandles.IndexOf(handle, 0,
Comparator()));
auto ix = mRegisteredHandles.IndexOf(handle, 0, Comparator());
if (ix == mRegisteredHandles.NoIndex) {
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
mRegisteredHandles.RemoveElementAt(ix);
if (mRegisteredHandles.Length() && !mInShutdown) {
// Whenever constraints are removed, other parties may get closer to ideal.
auto& first = mRegisteredHandles[0];

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

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

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

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

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

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

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

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

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

@ -165,8 +165,9 @@ BufferTextureData::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
return nullptr;
}
bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
aAllocator->GetCompositorBackendType());
bool hasIntermediateBuffer = aAllocator ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
aAllocator->GetCompositorBackendType())
: true;
// 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.
@ -175,8 +176,8 @@ BufferTextureData::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
aYUVColorSpace,
hasIntermediateBuffer);
return CreateInternal(aAllocator->GetTextureForwarder(), desc, gfx::BackendType::NONE, aBufferSize,
aTextureFlags);
return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder() : nullptr,
desc, gfx::BackendType::NONE, aBufferSize, aTextureFlags);
}
BufferTextureData*

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

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

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

@ -28,20 +28,21 @@ window.addEventListener("load", () => {
target.style.animation = "Opacity0 100s 100s";
// We need to wait for MozAfterPaint instead of requestAnimationFrame to
// ensure the stacking context has been updated (removed) on the compositor
// before we snapshot.
// ensure the stacking context has been updated on the compositor.
window.addEventListener("MozAfterPaint", function firstPaint() {
window.removeEventListener("MozAfterPaint", firstPaint, false);
// Here we have CSS animation on 100% opacity style element, so
// there should be a stacking context.
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
// stacking context.
document.documentElement.classList.remove("reftest-wait");
}, false);
});
}, false);
});
</script>

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

@ -28,20 +28,20 @@ window.addEventListener("load", () => {
target.style.animation = "TransformNone 100s 100s";
// We need to wait for MozAfterPaint instead of requestAnimationFrame to
// ensure the stacking context has been updated (removed) on the compositor
// before we snapshot.
// ensure the stacking context has been updated on the compositor.
window.addEventListener("MozAfterPaint", function firstPaint() {
window.removeEventListener("MozAfterPaint", firstPaint, false);
// Here we have CSS animation on transform:none style element, so
// there should be a stacking context.
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 transform:none style, so we should not create any
// stacking context.
document.documentElement.classList.remove("reftest-wait");
}, false);
});
}, false);
});
</script>

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

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

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

@ -50,15 +50,25 @@ public:
* Creates at least one new FilterPrimitiveDescription based on the filter
* from the style system. Appends the new FilterPrimitiveDescription(s) to the
* 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:
/**
* Returns a new FilterPrimitiveDescription with its basic properties set up.
* See the comment above BuildPrimitives for the meaning of aInputIsTainted.
*/
FilterPrimitiveDescription CreatePrimitiveDescription(PrimitiveType aType,
const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs);
const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
bool aInputIsTainted);
/**
* Sets aDescr's attributes using the style info in mFilter.

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

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

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

@ -60,12 +60,19 @@ public:
/**
* Create a FilterDescription for the supplied filter. All coordinates in
* 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
* render the filter (from feImage primitives).
* @return A FilterDescription describing the filter.
*/
static FilterDescription GetFilterDescription(nsIContent* aFilteredElement,
const nsTArray<nsStyleFilter>& aFilterChain,
bool aFilterInputIsTainted,
const UserSpaceMetrics& aMetrics,
const gfxRect& aBBox,
nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages);
@ -118,6 +125,9 @@ public:
* @param aTargetContent The filtered element itself.
* @param aMetrics The metrics to resolve SVG lengths against.
* @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
* paint. Only required if you will call Render().
* @param aPaintTransform The transform to apply to convert to
@ -137,6 +147,7 @@ public:
nsIContent* aTargetContent,
const UserSpaceMetrics& aMetrics,
const nsTArray<nsStyleFilter>& aFilterChain,
bool aFilterInputIsTainted,
nsSVGFilterPaintCallback *aPaintCallback,
const gfxMatrix& aPaintTransform,
const nsRegion *aPostFilterDirtyRegion = nullptr,
@ -236,18 +247,22 @@ private:
/**
* Build the list of FilterPrimitiveDescriptions that describes the filter's
* 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,
nsIFrame* aTargetFrame);
nsIFrame* aTargetFrame,
bool aFilterInputIsTainted);
/**
* Add to the list of FilterPrimitiveDescriptions for a particular SVG
* reference filter or CSS filter. This populates mPrimitiveDescrs and
* mInputImages.
* reference filter or CSS filter. This populates mPrimitiveDescriptions and
* mInputImages. aInputIsTainted describes whether the input to aFilter is
* tainted.
*/
nsresult BuildPrimitivesForFilter(const nsStyleFilter& aFilter,
nsIFrame* aTargetFrame);
nsIFrame* aTargetFrame,
bool aInputIsTainted);
/**
* Computes the filter space bounds of the areas that we actually *need* from

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

@ -366,23 +366,10 @@ nsSVGFilterInstance::GetSourceIndices(nsSVGFE* aPrimitiveElement,
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
nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
nsTArray<RefPtr<SourceSurface>>& aInputImages)
nsTArray<RefPtr<SourceSurface>>& aInputImages,
bool aInputIsTainted)
{
mSourceGraphicIndex = GetLastResultIndex(aPrimitiveDescrs);
@ -410,8 +397,6 @@ nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrim
// The principal that we check principals of any loaded images against.
nsCOMPtr<nsIPrincipal> principal = mTargetContent->NodePrincipal();
bool filterInputIsTainted = IsFilterInputTainted(mTargetContent);
for (uint32_t primitiveElementIndex = 0;
primitiveElementIndex < primitives.Length();
++primitiveElementIndex) {
@ -427,7 +412,7 @@ nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrim
ComputeFilterPrimitiveSubregion(filter, aPrimitiveDescrs, sourceIndices);
nsTArray<bool> sourcesAreTainted;
GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, filterInputIsTainted, sourcesAreTainted);
GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, aInputIsTainted, sourcesAreTainted);
FilterPrimitiveDescription descr =
filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, aInputImages);

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

@ -98,9 +98,17 @@ public:
* FilterPrimitiveDescription for each one. Appends the new
* FilterPrimitiveDescription(s) to the aPrimitiveDescrs list. Also, appends
* 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,
nsTArray<RefPtr<SourceSurface>>& aInputImages);
nsTArray<RefPtr<SourceSurface>>& aInputImages,
bool aInputIsTainted);
/**
* 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:
implementation: generic-worker
max-run-time: 7200
max-run-time: 9000
run:
using: mozharness
options: [enable-pgo]
@ -112,7 +112,7 @@ win64/pgo:
worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
worker:
implementation: generic-worker
max-run-time: 7200
max-run-time: 9000
run:
using: mozharness
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')
for option in run.get('options', []):
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'] = [
r'mkdir .\build\src',
r'"{}" share c:\builds\hg-shared\mozilla-central .\build\src'.format(hg),
r'"{}" pull -u -R .\build\src --rev %GECKO_HEAD_REV% %GECKO_HEAD_REPOSITORY%'.format(hg),
' '.join(mh_command),
' '.join(hg_command),
' '.join(mh_command)
]

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

@ -36,12 +36,4 @@ def setup_task(config, tasks):
'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

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

@ -30,6 +30,10 @@ import urllib2
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):
@ -95,15 +99,17 @@ def vcs_checkout(source_repo, dest, base_repo=None, revision=None, branch=None):
FINGERPRINT_URL)
res = urllib2.urlopen(FINGERPRINT_URL, timeout=10)
secret = res.read()
except urllib2.URLError as e:
print('error retrieving hg fingerprint: %s' % e)
sys.exit(1)
try:
secret = json.loads(secret, encoding='utf-8')
except ValueError:
print('invalid JSON in hg fingerprint secret')
sys.exit(1)
try:
secret = json.loads(secret, encoding='utf-8')
except ValueError:
print_line(b'vcs', 'invalid JSON in hg fingerprint secret')
sys.exit(1)
except urllib2.URLError:
print_line(b'vcs', 'Unable to retrieve current hg.mozilla.org fingerprint'
'using the secret service, using fallback instead.')
# 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')
@ -193,6 +199,7 @@ def main(args):
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
def set_dir_permissions(path, uid, gid):
st = os.lstat(path)

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

@ -3,7 +3,7 @@ import os
config = {
"locales_file": "src/browser/locales/all-locales",
"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": {
"NO_MERCURIAL_SETUP_CHECK": "1",
"MOZ_OBJDIR": "obj-l10n",

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

@ -2188,16 +2188,10 @@ TextInputHandler::InsertText(NSAttributedString* aAttrString,
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);
// 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;
}

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

@ -1906,6 +1906,21 @@ function* runKeyEventTests()
yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_I,
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);
// 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();
}
@ -4197,11 +4212,37 @@ function* runTextInputTests()
textbox.value = "";
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() {
var name = eventToString(aEvent);
is(textbox.value, aExpectText, name + " does not input correct text.");
textbox.removeEventListener("keypress", onKeypress, true);
if (aExpectText == "") {
is(keypress, 1, name + " should cause one keypress event because it doesn't cause inputting text");
} else {
is(keypress, aExpectText.length, name + " should cause " + aExpectText.length + " keypress events");
is(textbox.value, aExpectText, name + " does not input correct text.");
}
continueTest();
});