Merge m-c to b2ginbound, a=merge

This commit is contained in:
Wes Kocher 2015-05-26 16:25:40 -07:00
commit 9b191c89ba
220 changed files with 3942 additions and 1234 deletions

View File

@ -417,14 +417,6 @@ this.AccessFu = { // jshint ignore:line
let dpr = win.devicePixelRatio;
let offset = { left: -win.mozInnerScreenX, top: -win.mozInnerScreenY };
if (!aBrowser.contentWindow) {
// OOP browser, add offset of browser.
// The offset of the browser element in relation to its parent window.
let clientRect = aBrowser.getBoundingClientRect();
let win = aBrowser.ownerDocument.defaultView;
offset.left += clientRect.left + win.mozInnerScreenX;
offset.top += clientRect.top + win.mozInnerScreenY;
}
// Add the offset; the offset is in CSS pixels, so multiply the
// devicePixelRatio back in before adding to preserve unit consistency.
bounds = bounds.translate(offset.left * dpr, offset.top * dpr);

View File

@ -357,8 +357,8 @@ function runTest(testNum) {
// Context menu for textarea before spell check initialization finishes
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", false,
"context-copy", false,
"context-cut", true,
"context-copy", true,
"context-paste", null,
"context-delete", false,
"---", null,
@ -377,8 +377,8 @@ function runTest(testNum) {
"---", null,
"context-undo", false,
"---", null,
"context-cut", false,
"context-copy", false,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
@ -409,8 +409,8 @@ function runTest(testNum) {
"---", null,
"context-undo", false,
"---", null,
"context-cut", false,
"context-copy", false,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
@ -434,8 +434,8 @@ function runTest(testNum) {
"---", null,
"context-undo", false,
"---", null,
"context-cut", false,
"context-copy", false,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
@ -650,8 +650,8 @@ function runTest(testNum) {
// Context menu for selected text in input[type="password"]
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", false,
"context-copy", false,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", true,
"---", null,
@ -743,8 +743,8 @@ function runTest(testNum) {
// Context menu for text input field with spellcheck=false
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", false,
"context-copy", false,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,

View File

@ -40,8 +40,8 @@ function runTest(testNum) {
// Context menu for text input field.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", false,
"context-copy", false,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
@ -61,8 +61,8 @@ function runTest(testNum) {
// Context menu for spell-check input.
checkContextMenu(["context-undo", value,
"---", null,
"context-cut", value,
"context-copy", value,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", value,
"---", null,
@ -90,8 +90,8 @@ function runTest(testNum) {
"---", null,
"context-undo", value,
"---", null,
"context-cut", value,
"context-copy", value,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", value,
"---", null,
@ -115,8 +115,8 @@ function runTest(testNum) {
// Context menu for spell-check input with a known word.
checkContextMenu(["context-undo", value,
"---", null,
"context-cut", value,
"context-copy", value,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", value,
"---", null,
@ -139,8 +139,8 @@ function runTest(testNum) {
// Context menu for disabled input.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", false,
"context-copy", false,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
@ -157,15 +157,11 @@ function runTest(testNum) {
break;
case 7: // password
case 8: // email
case 9: // url
case 10: // tel
case 11: // type='number'
// Context menu for tel, password, email, url and number input fields.
// Context menu for password input fields.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", false,
"context-copy", false,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
@ -175,9 +171,30 @@ function runTest(testNum) {
closeContextMenu();
if (testNum == 7) {
input.type = 'email';
} else if (testNum == 8) {
input.type = 'email';
openContextMenuFor(input); // Invoke context menu for next test.
break;
case 8: // email
case 9: // url
case 10: // tel
case 11: // type='number'
// Context menu for tel, email, url and number input fields.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", false,
"---", null,
"context-inspect", true]);
closeContextMenu();
if (testNum == 8) {
input.type = 'url';
} else if (testNum == 9) {
input.type = 'tel';
@ -228,8 +245,8 @@ function runTest(testNum) {
// Context menu for search input fields.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", false,
"context-copy", false,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
@ -257,8 +274,8 @@ function runTest(testNum) {
// Context menu for a read-only input.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", false,
"context-copy", false,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,

View File

@ -19,7 +19,7 @@ add_task(function() {
let copyButton = document.getElementById("copy-button");
ok(copyButton, "Copy button exists in Panel Menu");
is(copyButton.getAttribute("disabled"), "true", "Copy button is initially disabled");
ok(!copyButton.getAttribute("disabled"), "Copy button is initially enabled");
// copy text from URL bar
gURLBar.value = testText;
@ -28,7 +28,7 @@ add_task(function() {
yield PanelUI.show();
info("Menu panel was opened");
ok(!copyButton.hasAttribute("disabled"), "Copy button gets enabled");
ok(!copyButton.hasAttribute("disabled"), "Copy button is enabled when selecting");
copyButton.click();
is(gURLBar.value, testText, "Selected text is unaltered when clicking copy");

View File

@ -18,7 +18,7 @@ add_task(function() {
let cutButton = document.getElementById("cut-button");
ok(cutButton, "Cut button exists in Panel Menu");
ok(cutButton.getAttribute("disabled"), "Cut button is disabled");
ok(!cutButton.hasAttribute("disabled"), "Cut button is enabled");
// cut text from URL bar
gURLBar.value = testText;
@ -27,7 +27,7 @@ add_task(function() {
yield PanelUI.show();
info("Menu panel was opened");
ok(!cutButton.hasAttribute("disabled"), "Cut button gets enabled");
ok(!cutButton.hasAttribute("disabled"), "Cut button is enabled when selecting");
cutButton.click();
is(gURLBar.value, "", "Selected text is removed from source when clicking on cut");

View File

@ -330,6 +330,7 @@ loop.conversationViews = (function(mozL10n) {
var errorString;
switch (this.props.failureReason) {
case FAILURE_DETAILS.NO_MEDIA:
case FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA:
errorString = mozL10n.get("no_media_failure_message");
break;

View File

@ -330,6 +330,7 @@ loop.conversationViews = (function(mozL10n) {
var errorString;
switch (this.props.failureReason) {
case FAILURE_DETAILS.NO_MEDIA:
case FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA:
errorString = mozL10n.get("no_media_failure_message");
break;

View File

@ -978,7 +978,7 @@ loop.OTSdkDriver = (function() {
var bucket = this.mozLoop.SHARING_STATE_CHANGE[type.toUpperCase() + "_" +
(enabled ? "ENABLED" : "DISABLED")];
if (!bucket) {
if (typeof bucket === "undefined") {
console.error("No sharing state bucket found for '" + type + "'");
return;
}

View File

@ -241,7 +241,7 @@ let LoopRoomsInternal = {
* @param {String} roomToken The token for the room that needs encrypting.
*/
queueForEncryption: function(roomToken) {
if (!this.encryptionQueue.queue.includes(roomToken)) {
if (this.encryptionQueue.queue.indexOf(roomToken) == -1) {
this.encryptionQueue.queue.push(roomToken);
}

View File

@ -939,6 +939,15 @@ describe("loop.conversationViews", function () {
expect(view.getDOMNode().querySelector("h2").textContent).eql("no_media_failure_message");
});
it("should show 'no media' for FAILURE_DETAILS.NO_MEDIA reason", function() {
view = mountTestComponent({
cancelCall: function() {},
failureReason: FAILURE_DETAILS.NO_MEDIA
});
expect(view.getDOMNode().querySelector("h2").textContent).eql("no_media_failure_message");
});
it("should show 'generic_failure_title' when no reason is specified", function() {
view = mountTestComponent({cancelCall: function() {}});

View File

@ -78,16 +78,16 @@ describe("loop.OTSdkDriver", function () {
mozLoop = {
telemetryAddValue: sinon.stub(),
TWO_WAY_MEDIA_CONN_LENGTH: {
SHORTER_THAN_10S: "SHORTER_THAN_10S",
BETWEEN_10S_AND_30S: "BETWEEN_10S_AND_30S",
BETWEEN_30S_AND_5M: "BETWEEN_30S_AND_5M",
MORE_THAN_5M: "MORE_THAN_5M"
SHORTER_THAN_10S: 0,
BETWEEN_10S_AND_30S: 1,
BETWEEN_30S_AND_5M: 2,
MORE_THAN_5M: 3
},
SHARING_STATE_CHANGE: {
WINDOW_ENABLED: "WINDOW_ENABLED",
WINDOW_DISABLED: "WINDOW_DISABLED",
BROWSER_ENABLED: "BROWSER_ENABLED",
BROWSER_DISABLED: "BROWSER_DISABLED"
WINDOW_ENABLED: 0,
WINDOW_DISABLED: 1,
BROWSER_ENABLED: 2,
BROWSER_DISABLED: 3
}
};

View File

@ -190,8 +190,8 @@ EventEmitter.decorate(this);
/**
* DOM query helpers.
*/
function $(selector, target = document) target.querySelector(selector);
function $all(selector, target = document) target.querySelectorAll(selector);
let $ = (selector, target = document) => target.querySelector(selector);
let $all = (selector, target = document) => target.querySelectorAll(selector);
/**
* Helper for getting an nsIURL instance out of a string.

View File

@ -57,7 +57,9 @@ CanvasDebuggerPanel.prototype = {
// DevToolPanel API
get target() this._toolbox.target,
get target() {
return this._toolbox.target;
},
destroy: function() {
// Make sure this panel is not already destroyed.

View File

@ -6,7 +6,7 @@
* You can also use this initialization format as a template for other tests.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCallWatcherBackend(SIMPLE_CANVAS_URL);
ok(target, "Should have a target available.");

View File

@ -6,7 +6,7 @@
* and that their stack is successfully retrieved.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCallWatcherBackend(SIMPLE_CANVAS_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* for a canvas context.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* the correct thumbnails.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* the correct "end result" screenshot.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
let navigated = once(target, "navigate");

View File

@ -5,7 +5,7 @@
* Tests if screenshots for arbitrary draw calls are generated properly.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_TRANSPARENT_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* by deferring the the most recent previous draw-call.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* forms if the method's signature does not expect an enum. Bug 999687.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_BITMASKS_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* forms if the method's signature does not expect an enum. Bug 999687.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(WEBGL_ENUM_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* after generating screenshots using the actor.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(WEBGL_BINDINGS_URL);
loadFrameScripts();

View File

@ -6,7 +6,7 @@
* for a canvas context, and that the generated screenshots are correct.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(SET_TIMEOUT_URL);
let navigated = once(target, "navigate");

View File

@ -6,7 +6,7 @@
* in the event no rAF loop is found.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, front } = yield initCanvasDebuggerBackend(NO_CANVAS_URL);
loadFrameScripts();

View File

@ -5,7 +5,7 @@
* Tests if certain function calls are properly highlighted in the UI.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* are properly displayed in the UI.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if filtering the items in the call list works properly.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
let searchbox = $("#calls-searchbox");

View File

@ -5,7 +5,7 @@
* Tests if the a function call's stack is properly displayed in the UI.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* and jumping to source in the debugger for the topmost call item works.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* on a function call item.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if clearing the snapshots list works as expected.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, EVENTS, SnapshotsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if screenshots are properly displayed in the UI.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if thumbnails are properly displayed in the UI.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, $all, EVENTS, SnapshotsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* function call items and their respective screenshots.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests that the frontend UI is properly configured when opening the tool.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { $ } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests whether the frontend behaves correctly while reording a snapshot.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests whether the frontend displays a placeholder snapshot while recording.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, EVENTS, L10N, $, SnapshotsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* after finishing recording.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;

View File

@ -8,7 +8,7 @@
* of its loop, when the recording starts before the rAFs start.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(RAF_BEGIN_URL);
let { window, EVENTS, gFront, SnapshotsListView } = panel.panelWin;
loadFrameScripts();

View File

@ -5,7 +5,7 @@
* Tests that the frontend UI is properly reconfigured after reloading.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests that the frontend UI is properly reconfigured after reloading.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if the slider in the calls list view works as advertised.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if the slider in the calls list view works as advertised.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, gFront, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* respective to their recorded animation frame.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* respective to their recorded animation frame.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests if the stepping buttons in the call list toolbar work as advertised.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests that you can stop a recording that does not have a rAF cycle.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(NO_CANVAS_URL);
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;

View File

@ -5,7 +5,7 @@
* Tests that a recording that does not have a rAF cycle fails after timeout.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(NO_CANVAS_URL);
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;

View File

@ -6,7 +6,7 @@
* after timeout.
*/
function ifTestingSupported() {
function* ifTestingSupported() {
let { target, panel } = yield initCanvasDebuggerFrontend(RAF_NO_CANVAS_URL);
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;

View File

@ -119,7 +119,7 @@ function runTests()
};
let firstShow = function() {
ok(cutItem.hasAttribute("disabled"), "cut menuitem is disabled");
ok(!cutItem.hasAttribute("disabled"), "cut menuitem is enabled");
closeMenu(firstHide);
};
@ -149,7 +149,7 @@ function runTests()
};
let showAfterCut = function() {
ok(cutItem.hasAttribute("disabled"), "cut menuitem is disabled after cut");
ok(!cutItem.hasAttribute("disabled"), "cut menuitem is enabled after cut");
ok(!pasteItem.hasAttribute("disabled"), "paste menuitem is enabled after cut");
closeMenu(hideAfterCut);
};
@ -167,7 +167,7 @@ function runTests()
};
let showAfterPaste = function() {
ok(cutItem.hasAttribute("disabled"), "cut menuitem is disabled after paste");
ok(!cutItem.hasAttribute("disabled"), "cut menuitem is enabled after paste");
ok(!pasteItem.hasAttribute("disabled"), "paste menuitem is enabled after paste");
closeMenu(hideAfterPaste);
};

View File

@ -66,6 +66,7 @@ support-files =
[browser_graphs-09f.js]
[browser_graphs-10a.js]
[browser_graphs-10b.js]
[browser_graphs-10c.js]
[browser_graphs-11a.js]
[browser_graphs-11b.js]
[browser_graphs-12.js]

View File

@ -0,0 +1,86 @@
// Tests that graphs properly handle resizing.
const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
let {LineGraphWidget} = Cu.import("resource:///modules/devtools/Graphs.jsm", {});
let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
add_task(function*() {
yield promiseTab("about:blank");
yield performTest();
gBrowser.removeCurrentTab();
});
function* performTest() {
let [host, win, doc] = yield createHost("window");
doc.body.setAttribute("style", "position: fixed; width: 100%; height: 100%; margin: 0;");
let graph = new LineGraphWidget(doc.body, "fps");
yield graph.once("ready");
let refreshCount = 0;
graph.on("refresh", () => refreshCount++);
yield testGraph(host, graph);
is(refreshCount, 2, "The graph should've been refreshed 2 times.");
yield graph.destroy();
host.destroy();
}
function* testGraph(host, graph) {
graph.setData(TEST_DATA);
host._window.resizeTo(500, 500);
yield graph.once("refresh");
let oldBounds = host.frame.getBoundingClientRect();
is (graph._width, oldBounds.width * window.devicePixelRatio,
"The window was properly resized (1).");
is (graph._height, oldBounds.height * window.devicePixelRatio,
"The window was properly resized (1).");
dragStart(graph, 100);
dragStop(graph, 400);
is(graph.getSelection().start, 100,
"The current selection start value is correct (1).");
is(graph.getSelection().end, 400,
"The current selection end value is correct (1).");
info("Making sure the selection updates when the window is resized");
host._window.resizeTo(250, 250);
yield graph.once("refresh");
let newBounds = host.frame.getBoundingClientRect();
is (graph._width, newBounds.width * window.devicePixelRatio,
"The window was properly resized (2).");
is (graph._height, newBounds.height * window.devicePixelRatio,
"The window was properly resized (2).");
let ratio = oldBounds.width / newBounds.width;
info("The window resize ratio is: " + ratio);
is(graph.getSelection().start, Math.round(100 / ratio),
"The current selection start value is correct (2).");
is(graph.getSelection().end, Math.round(400 / ratio),
"The current selection end value is correct (2).");
}
// EventUtils just doesn't work!
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function dragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}

View File

@ -684,6 +684,13 @@ AbstractCanvasGraph.prototype = {
return;
}
// Handle a changed size by mapping the old selection to the new width
if (this._width && newWidth && this.hasSelection()) {
let ratio = this._width / (newWidth * this._pixelRatio);
this._selection.start = Math.round(this._selection.start / ratio);
this._selection.end = Math.round(this._selection.end / ratio);
}
bounds.width = newWidth;
bounds.height = newHeight;
this._iframe.setAttribute("width", bounds.width);

View File

@ -151,6 +151,7 @@ function CssHtmlTree(aStyleInspector, aPageStyle)
this._onClick = this._onClick.bind(this);
this._onCopy = this._onCopy.bind(this);
this._onCopyColor = this._onCopyColor.bind(this);
this._onCopyImageDataUrl = this._onCopyImageDataUrl.bind(this);
this._onFilterStyles = this._onFilterStyles.bind(this);
this._onFilterKeyPress = this._onFilterKeyPress.bind(this);
this._onClearSearch = this._onClearSearch.bind(this);
@ -328,7 +329,7 @@ CssHtmlTree.prototype = {
* - type {String} One of the VIEW_NODE_XXX_TYPE const in
* style-inspector-overlays
* - value {Object} Depends on the type of the node
* returns null of the node isn't anything we care about
* returns null if the node isn't anything we care about
*/
getNodeInfo: function(node) {
if (!node) {
@ -351,7 +352,7 @@ CssHtmlTree.prototype = {
return {
type: overlays.VIEW_NODE_SELECTOR_TYPE,
value: selectorText.trim()
}
};
}
// Walk up the nodes to find out where node is
@ -715,6 +716,13 @@ CssHtmlTree.prototype = {
command: this._onCopyColor
});
// Copy data URI
this.menuitemCopyImageDataUrl = createMenuItem(this._contextmenu, {
label: "styleinspector.contextmenu.copyImageDataUrl",
accesskey: "styleinspector.contextmenu.copyImageDataUrl.accessKey",
command: this._onCopyImageDataUrl
});
// Show Original Sources
this.menuitemSources= createMenuItem(this._contextmenu, {
label: "ruleView.contextmenu.showOrigSources",
@ -745,6 +753,7 @@ CssHtmlTree.prototype = {
this.menuitemSources.setAttribute("checked", showOrig);
this.menuitemCopyColor.hidden = !this._isColorPopup();
this.menuitemCopyImageDataUrl.hidden = !this._isImageUrlPopup();
},
/**
@ -757,14 +766,12 @@ CssHtmlTree.prototype = {
_isColorPopup: function () {
this._colorToCopy = "";
let trigger = this.popupNode;
if (!trigger) {
let container = this._getPopupNodeContainer();
if (!container) {
return false;
}
let container = (trigger.nodeType == trigger.TEXT_NODE) ?
trigger.parentElement : trigger;
let isColorNode = el => el.dataset && "color" in el.dataset;
while (!isColorNode(container)) {
@ -778,6 +785,52 @@ CssHtmlTree.prototype = {
return true;
},
/**
* Check if the context menu popup was opened with a click on an image link
* If true, save the image url to this._imageUrlToCopy
*/
_isImageUrlPopup: function () {
this._imageUrlToCopy = "";
let container = this._getPopupNodeContainer();
let isImageUrlNode = this._isImageUrlNode(container);
if (isImageUrlNode) {
this._imageUrlToCopy = container.href;
}
return isImageUrlNode;
},
/**
* Check if a node is an image url
* @param {DOMNode} node The node which we want information about
* @return {Boolean} true if the node is an image url
*/
_isImageUrlNode: function (node) {
let nodeInfo = this.getNodeInfo(node);
if (!nodeInfo) {
return false
}
return nodeInfo.type == overlays.VIEW_NODE_IMAGE_URL_TYPE;
},
/**
* Get the DOM Node container for the current popupNode.
* If popupNode is a textNode, return the parent node, otherwise return popupNode itself.
* @return {DOMNode}
*/
_getPopupNodeContainer: function () {
let container = null;
let node = this.popupNode;
if (node) {
let isTextNode = node.nodeType == node.TEXT_NODE;
container = isTextNode ? node.parentElement : node;
}
return container;
},
/**
* Context menu handler.
*/
@ -821,6 +874,22 @@ CssHtmlTree.prototype = {
clipboardHelper.copyString(this._colorToCopy);
},
/**
* Retrieve the image data for the selected image url and copy it to the clipboard
*/
_onCopyImageDataUrl: Task.async(function*() {
let message;
try {
let inspectorFront = this.inspector.inspector;
let data = yield inspectorFront.getImageDataFromURL(this._imageUrlToCopy);
message = yield data.data.string();
} catch (e) {
message = CssHtmlTree.l10n("styleinspector.copyImageDataUrlError");
}
clipboardHelper.copyString(message);
}),
/**
* Copy selected text.
*
@ -910,6 +979,10 @@ CssHtmlTree.prototype = {
this.menuitemCopyColor.removeEventListener("command", this._onCopyColor);
this.menuitemCopyColor = null;
// Destroy Copy Data URI menuitem.
this.menuitemCopyImageDataUrl.removeEventListener("command", this._onCopyImageDataUrl);
this.menuitemCopyImageDataUrl = null;
// Destroy the context menu.
this._contextmenu.removeEventListener("popupshowing", this._contextMenuUpdate);
this._contextmenu.parentNode.removeChild(this._contextmenu);

View File

@ -1123,6 +1123,7 @@ function CssRuleView(aInspector, aDoc, aStore, aPageStyle) {
this._onSelectAll = this._onSelectAll.bind(this);
this._onCopy = this._onCopy.bind(this);
this._onCopyColor = this._onCopyColor.bind(this);
this._onCopyImageDataUrl = this._onCopyImageDataUrl.bind(this);
this._onToggleOrigSources = this._onToggleOrigSources.bind(this);
this._onShowMdnDocs = this._onShowMdnDocs.bind(this);
this._onFilterStyles = this._onFilterStyles.bind(this);
@ -1214,6 +1215,11 @@ CssRuleView.prototype = {
accesskey: "ruleView.contextmenu.copyColor.accessKey",
command: this._onCopyColor
});
this.menuitemCopyImageDataUrl = createMenuItem(this._contextmenu, {
label: "styleinspector.contextmenu.copyImageDataUrl",
accesskey: "styleinspector.contextmenu.copyImageDataUrl.accessKey",
command: this._onCopyImageDataUrl
});
this.menuitemSources = createMenuItem(this._contextmenu, {
label: "ruleView.contextmenu.showOrigSources",
accesskey: "ruleView.contextmenu.showOrigSources.accessKey",
@ -1348,6 +1354,7 @@ CssRuleView.prototype = {
}
this.menuitemCopyColor.hidden = !this._isColorPopup();
this.menuitemCopyImageDataUrl.hidden = !this._isImageUrlPopup();
this.menuitemCopy.disabled = !copy;
var showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
@ -1398,7 +1405,7 @@ CssRuleView.prototype = {
pseudoElement: prop.rule.pseudoElement,
sheetHref: prop.rule.domRule.href
};
} else if (classes.contains("theme-link") && prop) {
} else if (classes.contains("theme-link") && !classes.contains("ruleview-rule-source") && prop) {
type = overlays.VIEW_NODE_IMAGE_URL_TYPE;
value = {
property: getPropertyNameAndValue(node).name,
@ -1430,14 +1437,11 @@ CssRuleView.prototype = {
_isColorPopup: function () {
this._colorToCopy = "";
let trigger = this.doc.popupNode;
if (!trigger) {
let container = this._getPopupNodeContainer();
if (!container) {
return false;
}
let container = (trigger.nodeType == trigger.TEXT_NODE) ?
trigger.parentElement : trigger;
let isColorNode = el => el.dataset && "color" in el.dataset;
while (!isColorNode(container)) {
@ -1451,6 +1455,52 @@ CssRuleView.prototype = {
return true;
},
/**
* Check if the context menu popup was opened with a click on an image link
* If true, save the image url to this._imageUrlToCopy
*/
_isImageUrlPopup: function () {
this._imageUrlToCopy = "";
let container = this._getPopupNodeContainer();
let isImageUrlNode = this._isImageUrlNode(container);
if (isImageUrlNode) {
this._imageUrlToCopy = container.href;
}
return isImageUrlNode;
},
/**
* Check if a node is an image url
* @param {DOMNode} node The node which we want information about
* @return {Boolean} true if the node is an image url
*/
_isImageUrlNode: function (node) {
let nodeInfo = this.getNodeInfo(node);
if (!nodeInfo) {
return false
}
return nodeInfo.type == overlays.VIEW_NODE_IMAGE_URL_TYPE;
},
/**
* Get the DOM Node container for the current popupNode.
* If popupNode is a textNode, return the parent node, otherwise return popupNode itself.
* @return {DOMNode}
*/
_getPopupNodeContainer: function () {
let container = null;
let node = this.doc.popupNode;
if (node) {
let isTextNode = node.nodeType == node.TEXT_NODE;
container = isTextNode ? node.parentElement : node;
}
return container;
},
/**
* Context menu handler.
*/
@ -1525,6 +1575,22 @@ CssRuleView.prototype = {
clipboardHelper.copyString(this._colorToCopy);
},
/**
* Retrieve the image data for the selected image url and copy it to the clipboard
*/
_onCopyImageDataUrl: Task.async(function*() {
let message;
try {
let inspectorFront = this.inspector.inspector;
let data = yield inspectorFront.getImageDataFromURL(this._imageUrlToCopy);
message = yield data.data.string();
} catch (e) {
message = _strings.GetStringFromName("styleinspector.copyImageDataUrlError");
}
clipboardHelper.copyString(message);
}),
/**
* Toggle the original sources pref.
*/
@ -1729,6 +1795,10 @@ CssRuleView.prototype = {
this.menuitemCopyColor.removeEventListener("command", this._onCopyColor);
this.menuitemCopyColor = null;
// Destroy Copy Data URI menuitem.
this.menuitemCopyImageDataUrl.removeEventListener("command", this._onCopyImageDataUrl);
this.menuitemCopyImageDataUrl = null;
this.menuitemSources.removeEventListener("command", this._onToggleOrigSources);
this.menuitemSources = null;

View File

@ -21,6 +21,7 @@ const {
SwatchFilterTooltip
} = require("devtools/shared/widgets/Tooltip");
const {CssLogic} = require("devtools/styleinspector/css-logic");
const EventEmitter = require("devtools/toolkit/event-emitter");
const {Promise:promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -59,6 +60,8 @@ function HighlightersOverlay(view) {
// Only initialize the overlay if at least one of the highlighter types is
// supported
this.supportsHighlighters = this.highlighterUtils.supportsCustomHighlighters();
EventEmitter.decorate(this);
}
exports.HighlightersOverlay = HighlightersOverlay;
@ -124,9 +127,13 @@ HighlightersOverlay.prototype = {
if (type) {
this.highlighterShown = type;
let node = this.view.inspector.selection.nodeFront;
this._getHighlighter(type).then(highlighter => {
highlighter.show(node);
});
this._getHighlighter(type)
.then(highlighter => highlighter.show(node))
.then(shown => {
if (shown) {
this.emit("highlighter-shown");
}
});
}
},
@ -176,6 +183,7 @@ HighlightersOverlay.prototype = {
promise.then(null, Cu.reportError);
}
this.highlighterShown = null;
this.emit("highlighter-hidden");
});
}
},

View File

@ -151,6 +151,7 @@ skip-if = e10s # bug 1040670 Cannot open inline styles in viewSourceUtils
[browser_ruleview_user-property-reset.js]
[browser_styleinspector_context-menu-copy-color_01.js]
[browser_styleinspector_context-menu-copy-color_02.js]
[browser_styleinspector_context-menu-copy-data-uri.js]
[browser_styleinspector_csslogic-content-stylesheets.js]
[browser_styleinspector_output-parser.js]
[browser_styleinspector_refresh_when_active.js]

View File

@ -0,0 +1,99 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const PROPERTIES_URL = "chrome://global/locale/devtools/styleinspector.properties";
const TEST_DATA_URI = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=";
// invalid URL still needs to be reachable otherwise getImageDataUrl will timeout.
// Reusing the properties bundle URL as a workaround
const INVALID_IMAGE_URI = PROPERTIES_URL;
const ERROR_MESSAGE = Services.strings
.createBundle(PROPERTIES_URL)
.GetStringFromName("styleinspector.copyImageDataUrlError");
add_task(function*() {
const PAGE_CONTENT = [
"<style type=\"text/css\">",
" .valid-background {",
" background-image: url(" + TEST_DATA_URI + ");",
" }",
" .invalid-background {",
" background-image: url(" + INVALID_IMAGE_URI + ");",
" }",
"</style>",
"<div class=\"valid-background\">Valid background image</div>",
"<div class=\"invalid-background\">Invalid background image</div>"
].join("\n");
yield addTab("data:text/html;charset=utf8," + encodeURIComponent(PAGE_CONTENT));
yield startTest();
});
function* startTest() {
info("Opening rule view");
let ruleViewData = yield openRuleView();
info("Test valid background image URL in rule view");
yield testCopyImageDataUrlToClipboard(ruleViewData, ".valid-background", TEST_DATA_URI);
info("Test invalid background image URL in rue view");
yield testCopyImageDataUrlToClipboard(ruleViewData, ".invalid-background", ERROR_MESSAGE);
info("Opening computed view");
let computedViewData = yield openComputedView();
info("Test valid background image URL in computed view");
yield testCopyImageDataUrlToClipboard(computedViewData, ".valid-background", TEST_DATA_URI);
info("Test invalid background image URL in computed view");
yield testCopyImageDataUrlToClipboard(computedViewData, ".invalid-background", ERROR_MESSAGE);
}
function* testCopyImageDataUrlToClipboard({view, inspector}, selector, expected) {
info("Select node in inspector panel");
yield selectNode(selector, inspector);
info("Retrieve background-image link for selected node in current styleinspector view");
let property = getBackgroundImageProperty(view, selector);
let imageLink = property.valueSpan.querySelector(".theme-link");
ok(imageLink, "Background-image link element found");
info("Simulate right click on the background-image URL");
let popup = once(view._contextmenu, "popupshown");
// Cannot rely on synthesizeMouseAtCenter here. The image URL can be displayed on several lines.
// A click simulated at the exact center may click between the lines and miss the target
// Instead, using the top-left corner of first client rect, with an offset of 2 pixels.
let rect = imageLink.getClientRects()[0];
let x = rect.left + 2;
let y = rect.top + 2;
EventUtils.synthesizeMouseAtPoint(x, y, {button: 2, type: "contextmenu"}, getViewWindow(view));
yield popup;
info("Context menu is displayed");
ok(!view.menuitemCopyImageDataUrl.hidden, "\"Copy Image Data-URL\" menu entry is displayed");
info("Click Copy Data URI and wait for clipboard");
yield waitForClipboard(() => view.menuitemCopyImageDataUrl.click(), expected);
info("Hide context menu");
view._contextmenu.hidePopup();
}
function getBackgroundImageProperty(view, selector) {
let isRuleView = view instanceof CssRuleView;
if (isRuleView) {
return getRuleViewProperty(view, selector, "background-image");
} else {
return getComputedViewProperty(view, "background-image");
}
}
/**
* Function that returns the window for a given view.
*/
function getViewWindow(view) {
let viewDocument = view.styleDocument ? view.styleDocument : view.doc;
return viewDocument.defaultView;
}

View File

@ -36,7 +36,9 @@ add_task(function*() {
info("Faking a mousemove on a transform property");
({valueSpan} = getRuleViewProperty(rView, "body", "transform"));
let onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
ok(hs.promises[TYPE], "The highlighter is being initialized");
let h = yield hs.promises[TYPE];
is(h, hs.highlighters[TYPE], "The initialized highlighter is the right one");
@ -57,7 +59,9 @@ add_task(function*() {
info("Faking a mousemove on a transform property");
({valueSpan} = getComputedViewProperty(cView, "transform"));
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
ok(hs.promises[TYPE], "The highlighter is being initialized");
h = yield hs.promises[TYPE];
is(h, hs.highlighters[TYPE], "The initialized highlighter is the right one");

View File

@ -39,53 +39,64 @@ add_task(function*() {
this.nodeFront = nodeFront;
this.isShown = true;
this.nbOfTimesShown ++;
return promise.resolve(true);
},
hide: function() {
this.nodeFront = null;
this.isShown = false;
return promise.resolve();
}
};
// Inject the mock highlighter in the rule-view
rView.highlighters.promises[TYPE] = {
then: function(cb) {
cb(HighlighterFront);
}
};
let hs = rView.highlighters;
hs.promises[TYPE] = promise.resolve(HighlighterFront);
let {valueSpan} = getRuleViewProperty(rView, "body", "transform");
info("Checking that the HighlighterFront's show/hide methods are called");
rView.highlighters._onMouseMove({target: valueSpan});
let onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
ok(HighlighterFront.isShown, "The highlighter is shown");
rView.highlighters._onMouseLeave();
let onHighlighterHidden = hs.once("highlighter-hidden");
hs._onMouseLeave();
yield onHighlighterHidden;
ok(!HighlighterFront.isShown, "The highlighter is hidden");
info("Checking that hovering several times over the same property doesn't" +
" show the highlighter several times");
let nb = HighlighterFront.nbOfTimesShown;
rView.highlighters._onMouseMove({target: valueSpan});
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
is(HighlighterFront.nbOfTimesShown, nb + 1, "The highlighter was shown once");
rView.highlighters._onMouseMove({target: valueSpan});
rView.highlighters._onMouseMove({target: valueSpan});
hs._onMouseMove({target: valueSpan});
hs._onMouseMove({target: valueSpan});
is(HighlighterFront.nbOfTimesShown, nb + 1,
"The highlighter was shown once, after several mousemove");
info("Checking that the right NodeFront reference is passed");
yield selectNode("html", inspector);
({valueSpan} = getRuleViewProperty(rView, "html", "transform"));
rView.highlighters._onMouseMove({target: valueSpan});
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
is(HighlighterFront.nodeFront.tagName, "HTML",
"The right NodeFront is passed to the highlighter (1)");
yield selectNode("body", inspector);
({valueSpan} = getRuleViewProperty(rView, "body", "transform"));
rView.highlighters._onMouseMove({target: valueSpan});
onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
is(HighlighterFront.nodeFront.tagName, "BODY",
"The right NodeFront is passed to the highlighter (2)");
info("Checking that the highlighter gets hidden when hovering a non-transform property");
({valueSpan} = getRuleViewProperty(rView, "body", "color"));
rView.highlighters._onMouseMove({target: valueSpan});
onHighlighterHidden = hs.once("highlighter-hidden");
hs._onMouseMove({target: valueSpan});
yield onHighlighterHidden;
ok(!HighlighterFront.isShown, "The highlighter is hidden");
});

View File

@ -55,7 +55,9 @@ add_task(function*() {
info("Faking a mousemove on the now unoverriden property");
({valueSpan} = getRuleViewProperty(rView, "div", "transform"));
let onHighlighterShown = hs.once("highlighter-shown");
hs._onMouseMove({target: valueSpan});
yield onHighlighterShown;
ok(hs.promises[TYPE], "The highlighter is being initialized now");
let h = yield hs.promises[TYPE];
is(h, hs.highlighters[TYPE], "The initialized highlighter is the right one");

View File

@ -17,8 +17,6 @@ GENERATED_SOURCES += [
"%s.c" % cpu,
]
DEFINES['ELFHACK_BUILD'] = True
NO_PGO = True
NO_VISIBILITY_FLAGS = True

View File

@ -25,6 +25,4 @@ HOST_SOURCES += [
HostProgram('elfhack')
DEFINES['ELFHACK_BUILD'] = True
NO_PGO = True

View File

@ -431,7 +431,7 @@ if test -z "$RUSTC" -a -n "$MOZ_RUST"; then
To compile rust language sources, you must have 'rustc' in your path.
See http://www.rust-lang.org/ for more information.])
fi
if test -n "$MOZ_RUST" -a -z "$_RUSTC_MAJOR_VERSION" -o \
if test -n "$MOZ_RUST" && test -z "$_RUSTC_MAJOR_VERSION" -o \
"$_RUSTC_MAJOR_VERSION" -lt 1; then
AC_MSG_ERROR([Rust compiler ${RUSTC_VERSION} is too old.
To compile rust language sources please install at least

View File

@ -6628,6 +6628,14 @@ nsContentUtils::IsRequestFullScreenAllowed()
IsCallerChrome();
}
/* static */
bool
nsContentUtils::IsCutCopyAllowed()
{
return EventStateManager::IsHandlingUserInput() ||
IsCallerChrome();
}
/* static */
bool
nsContentUtils::HaveEqualPrincipals(nsIDocument* aDoc1, nsIDocument* aDoc2)

View File

@ -1863,6 +1863,13 @@ public:
*/
static bool IsRequestFullScreenAllowed();
/**
* Returns true if calling execCommand with 'cut' or 'copy' arguments is
* allowed in the current context. These are only allowed if the user initiated
* them (like with a mouse-click or key press).
*/
static bool IsCutCopyAllowed();
/*
* Returns true if the performance timing APIs are enabled.
*/

View File

@ -1152,10 +1152,12 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * aProto)
mData, nullptr, nameSpaceManager, proto,
&desc);
NS_ENSURE_SUCCESS(rv, rv);
if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined() &&
!JS_DefineUCProperty(cx, global, mData->mNameUTF16,
NS_strlen(mData->mNameUTF16), desc)) {
return NS_ERROR_UNEXPECTED;
if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined()) {
desc.attributesRef() |= JSPROP_RESOLVING;
if (!JS_DefineUCProperty(cx, global, mData->mNameUTF16,
NS_strlen(mData->mNameUTF16), desc)) {
return NS_ERROR_UNEXPECTED;
}
}
return NS_OK;

View File

@ -2121,6 +2121,9 @@ GK_ATOM(el, "el")
GK_ATOM(ga, "ga")
GK_ATOM(nl, "nl")
// mathematical language, used for MathML
GK_ATOM(x_math, "x-math")
// Names for editor transactions
GK_ATOM(TypingTxnName, "Typing")
GK_ATOM(IMETxnName, "IME")

View File

@ -423,7 +423,7 @@ private:
return this;
}
void InsertUserEntry(PerformanceEntry* aEntry);
void InsertUserEntry(PerformanceEntry* aEntry) override;
bool IsPerformanceTimingAttribute(const nsAString& aName) override;

View File

@ -550,7 +550,7 @@ DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name,
// This is Enumerable: False per spec.
return alreadyDefined ||
JS_DefineProperty(cx, global, name, constructor, 0);
JS_DefineProperty(cx, global, name, constructor, JSPROP_RESOLVING);
}
static JSObject*

View File

@ -7803,9 +7803,11 @@ class CGResolveHook(CGAbstractClassHook):
// If desc.value() is undefined, then the DoResolve call
// has already defined it on the object. Don't try to also
// define it.
if (!desc.value().isUndefined() &&
!JS_DefinePropertyById(cx, obj, id, desc)) {
return false;
if (!desc.value().isUndefined()) {
desc.attributesRef() |= JSPROP_RESOLVING;
if (!JS_DefinePropertyById(cx, obj, id, desc)) {
return false;
}
}
*resolvedp = true;
return true;

View File

@ -63,6 +63,29 @@ StringToUsage(const nsString& aUsage, CryptoKey::KeyUsage& aUsageOut)
return NS_OK;
}
SECKEYPrivateKey*
PrivateKeyFromPrivateKeyTemplate(SECItem* aObjID,
CK_ATTRIBUTE* aTemplate,
CK_ULONG aTemplateSize)
{
// Create a generic object with the contents of the key
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
return nullptr;
}
ScopedPK11GenericObject obj(PK11_CreateGenericObject(slot,
aTemplate,
aTemplateSize,
PR_FALSE));
if (!obj) {
return nullptr;
}
// Have NSS translate the object to a private key.
return PK11_FindKeyByKeyID(slot, aObjID, nullptr);
}
CryptoKey::CryptoKey(nsIGlobalObject* aGlobal)
: mGlobal(aGlobal)
, mAttributes(0)
@ -223,6 +246,73 @@ CryptoKey::SetExtractable(bool aExtractable)
}
}
// NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the
// public value. To properly export the private key to JWK or PKCS #8 we need
// the public key data though and so we use this method to augment a private
// key with data from the given public key.
nsresult
CryptoKey::AddPublicKeyData(SECKEYPublicKey* aPublicKey)
{
// This should be a private key.
MOZ_ASSERT(GetKeyType() == PRIVATE);
// There should be a private NSS key with type 'EC'.
MOZ_ASSERT(mPrivateKey && mPrivateKey->keyType == ecKey);
// The given public key should have the same key type.
MOZ_ASSERT(aPublicKey->keyType == mPrivateKey->keyType);
nsNSSShutDownPreventionLock locker;
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
return NS_ERROR_DOM_OPERATION_ERR;
}
// Generate a random 160-bit object ID.
ScopedSECItem objID(::SECITEM_AllocItem(nullptr, nullptr, 20));
SECStatus rv = PK11_GenerateRandomOnSlot(slot, objID->data, objID->len);
if (rv != SECSuccess) {
return NS_ERROR_DOM_OPERATION_ERR;
}
// Read EC params.
ScopedSECItem params(::SECITEM_AllocItem(nullptr, nullptr, 0));
rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey, CKA_EC_PARAMS,
params);
if (rv != SECSuccess) {
return NS_ERROR_DOM_OPERATION_ERR;
}
// Read private value.
ScopedSECItem value(::SECITEM_AllocItem(nullptr, nullptr, 0));
rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey, CKA_VALUE, value);
if (rv != SECSuccess) {
return NS_ERROR_DOM_OPERATION_ERR;
}
SECItem* point = &aPublicKey->u.ec.publicValue;
CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
CK_BBOOL falseValue = CK_FALSE;
CK_KEY_TYPE ecValue = CKK_EC;
CK_ATTRIBUTE keyTemplate[9] = {
{ CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) },
{ CKA_KEY_TYPE, &ecValue, sizeof(ecValue) },
{ CKA_TOKEN, &falseValue, sizeof(falseValue) },
{ CKA_SENSITIVE, &falseValue, sizeof(falseValue) },
{ CKA_PRIVATE, &falseValue, sizeof(falseValue) },
{ CKA_ID, objID->data, objID->len },
{ CKA_EC_PARAMS, params->data, params->len },
{ CKA_EC_POINT, point->data, point->len },
{ CKA_VALUE, value->data, value->len },
};
mPrivateKey = PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate,
PR_ARRAY_SIZE(keyTemplate));
NS_ENSURE_TRUE(mPrivateKey, NS_ERROR_DOM_OPERATION_ERR);
return NS_OK;
}
void
CryptoKey::ClearUsages()
{
@ -367,6 +457,9 @@ CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData,
{
SECKEYPrivateKey* privKey;
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
return nullptr;
}
ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
if (!arena) {
@ -582,36 +675,6 @@ CreateECPointForCoordinates(const CryptoBuffer& aX,
return point;
}
SECKEYPrivateKey*
PrivateKeyFromPrivateKeyTemplate(SECItem* aObjID,
CK_ATTRIBUTE* aTemplate,
CK_ULONG aTemplateSize)
{
// Create a generic object with the contents of the key
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot.get()) {
return nullptr;
}
ScopedPK11GenericObject obj(PK11_CreateGenericObject(slot.get(),
aTemplate,
aTemplateSize,
PR_FALSE));
if (!obj.get()) {
return nullptr;
}
// Have NSS translate the object to a private key by inspection
// and make a copy we can own
ScopedSECKEYPrivateKey privKey(PK11_FindKeyByKeyID(slot.get(), aObjID,
nullptr));
if (!privKey.get()) {
return nullptr;
}
return SECKEY_CopyPrivateKey(privKey.get());
}
SECKEYPrivateKey*
CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
@ -1089,11 +1152,15 @@ CryptoKey::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
CryptoBuffer priv, pub;
if (mPrivateKey) {
CryptoKey::PrivateKeyToPkcs8(mPrivateKey, priv, locker);
if (NS_FAILED(CryptoKey::PrivateKeyToPkcs8(mPrivateKey, priv, locker))) {
return false;
}
}
if (mPublicKey) {
CryptoKey::PublicKeyToSpki(mPublicKey, pub, locker);
if (NS_FAILED(CryptoKey::PublicKeyToSpki(mPublicKey, pub, locker))) {
return false;
}
}
return JS_WriteUint32Pair(aWriter, mAttributes, CRYPTOKEY_SC_VERSION) &&

View File

@ -116,6 +116,7 @@ public:
nsresult SetType(const nsString& aType);
void SetType(KeyType aType);
void SetExtractable(bool aExtractable);
nsresult AddPublicKeyData(SECKEYPublicKey* point);
void ClearUsages();
nsresult AddUsage(const nsString& aUsage);
nsresult AddUsageIntersecting(const nsString& aUsage, uint32_t aUsageMask);
@ -155,7 +156,7 @@ public:
static SECKEYPublicKey* PublicKeyFromSpki(CryptoBuffer& aKeyData,
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
static nsresult PublicKeyToSpki(SECKEYPublicKey* aPrivKey,
static nsresult PublicKeyToSpki(SECKEYPublicKey* aPubKey,
CryptoBuffer& aRetVal,
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
@ -167,7 +168,7 @@ public:
static SECKEYPublicKey* PublicKeyFromJwk(const JsonWebKey& aKeyData,
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
static nsresult PublicKeyToJwk(SECKEYPublicKey* aPrivKey,
static nsresult PublicKeyToJwk(SECKEYPublicKey* aPubKey,
JsonWebKey& aRetVal,
const nsNSSShutDownPreventionLock& /*proofOfLock*/);

View File

@ -1913,9 +1913,13 @@ private:
}
switch (mPrivateKey->keyType) {
case rsaKey:
CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), mResult, locker);
case rsaKey: {
nsresult rv = CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), mResult, locker);
if (NS_FAILED(rv)) {
return NS_ERROR_DOM_OPERATION_ERR;
}
return NS_OK;
}
default:
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
@ -2327,6 +2331,14 @@ private:
mKeyPair.mPrivateKey.get()->SetPrivateKey(mPrivateKey);
mKeyPair.mPublicKey.get()->SetPublicKey(mPublicKey);
// PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
// private key, we need this later when exporting to PKCS8 and JWK though.
if (mMechanism == CKM_EC_KEY_PAIR_GEN) {
nsresult rv = mKeyPair.mPrivateKey->AddPublicKeyData(mPublicKey);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
}
return NS_OK;
}

View File

@ -298,6 +298,37 @@ TestArray.addTest(
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"JWK export of a newly generated ECDH private key",
function () {
var that = this;
var alg = { name: "ECDH", namedCurve: "P-256" };
var reBase64URL = /^[a-zA-Z0-9_-]+$/;
function doExportToJWK(x) {
return crypto.subtle.exportKey("jwk", x.privateKey)
}
crypto.subtle.generateKey(alg, true, ["deriveKey", "deriveBits"])
.then(doExportToJWK)
.then(
complete(that, function(x) {
return x.ext &&
x.kty == 'EC' &&
x.crv == 'P-256' &&
reBase64URL.test(x.x) &&
reBase64URL.test(x.y) &&
reBase64URL.test(x.d) &&
x.x.length == 43 && // 32 octets, base64-encoded
x.y.length == 43 && // 32 octets, base64-encoded
shallowArrayEquals(x.key_ops, ['deriveKey', 'deriveBits']);
}),
error(that)
);
}
);
// -----------------------------------------------------------------------------
TestArray.addTest(
"Derive an HMAC key from two ECDH keys and test sign/verify",

View File

@ -22,6 +22,9 @@
<script>/*<![CDATA[*/
"use strict";
// Generating 2048-bit keys takes some time.
SimpleTest.requestLongerTimeout(2);
// -----------------------------------------------------------------------------
TestArray.addTest(
"RSA-OAEP encrypt/decrypt round-trip",

View File

@ -22,6 +22,9 @@
<script>/*<![CDATA[*/
"use strict";
// Generating 2048-bit keys takes some time.
SimpleTest.requestLongerTimeout(2);
TestArray.addTest(
"Test that we reject generating keys without any usage",
function() {

View File

@ -43,7 +43,8 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
nsAutoScriptBlocker scriptBlocker;
tmp->UnregisterObservers(true);
tmp->NotifyIMEOfBlur();
tmp->UnregisterObservers();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWidget)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection)
@ -86,6 +87,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
IMEContentObserver::IMEContentObserver()
: mESM(nullptr)
, mPreCharacterDataChangeLength(-1)
, mIsObserving(false)
, mIsSelectionChangeEventPending(false)
, mSelectionChangeCausedOnlyByComposition(false)
, mIsPositionChangeEventPending(false)
@ -104,17 +106,35 @@ IMEContentObserver::Init(nsIWidget* aWidget,
{
MOZ_ASSERT(aEditor, "aEditor must not be null");
State state = GetState();
if (NS_WARN_IF(state == eState_Observing)) {
return; // Nothing to do.
}
bool firstInitialization = state != eState_StoppedObserving;
if (!firstInitialization) {
// If this is now trying to initialize with new contents, all observers
// should be registered again for simpler implementation.
UnregisterObservers();
// Clear members which may not be initialized again.
mRootContent = nullptr;
mEditor = nullptr;
mSelection = nullptr;
mDocShell = nullptr;
}
mESM = aPresContext->EventStateManager();
mESM->OnStartToObserveContent(this);
mWidget = aWidget;
mEditableNode = IMEStateManager::GetRootEditableNode(aPresContext, aContent);
mEditableNode =
IMEStateManager::GetRootEditableNode(aPresContext, aContent);
if (!mEditableNode) {
return;
}
mEditor = aEditor;
mEditor->AddEditorObserver(this);
nsIPresShell* presShell = aPresContext->PresShell();
@ -154,19 +174,23 @@ IMEContentObserver::Init(nsIWidget* aWidget,
}
NS_ENSURE_TRUE_VOID(mRootContent);
if (IMEStateManager::IsTestingIME()) {
nsIDocument* doc = aPresContext->Document();
(new AsyncEventDispatcher(doc, NS_LITERAL_STRING("MozIMEFocusIn"),
false, false))->RunDOMEventWhenSafe();
}
if (firstInitialization) {
aWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS));
aWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS));
// While Init() notifies IME of focus, pending layout may be flushed
// because the notification may cause querying content. Then, recursive
// call of Init() with the latest content may be occur. In such case, we
// shouldn't keep first initialization.
if (GetState() != eState_Initializing) {
return;
}
// NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
// instance via IMEStateManager::UpdateIMEState(). So, this
// instance might already have been destroyed, check it.
if (!mRootContent) {
return;
// NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
// instance via IMEStateManager::UpdateIMEState(). So, this
// instance might already have been destroyed, check it.
if (!mRootContent) {
return;
}
}
mDocShell = aPresContext->GetDocShell();
@ -177,8 +201,13 @@ IMEContentObserver::Init(nsIWidget* aWidget,
void
IMEContentObserver::ObserveEditableNode()
{
MOZ_ASSERT(mSelection);
MOZ_ASSERT(mRootContent);
MOZ_RELEASE_ASSERT(mEditor);
MOZ_RELEASE_ASSERT(mSelection);
MOZ_RELEASE_ASSERT(mRootContent);
MOZ_RELEASE_ASSERT(GetState() != eState_Observing);
mIsObserving = true;
mEditor->AddEditorObserver(this);
mUpdatePreference = mWidget->GetIMEUpdatePreference();
if (mUpdatePreference.WantSelectionChange()) {
@ -203,32 +232,30 @@ IMEContentObserver::ObserveEditableNode()
}
void
IMEContentObserver::UnregisterObservers(bool aPostEvent)
IMEContentObserver::NotifyIMEOfBlur()
{
if (mEditor) {
mEditor->RemoveEditorObserver(this);
// If this failed to initialize, mRootContent may be null, then, we
// should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR))
if (!mRootContent || !mWidget) {
return;
}
// If CreateTextStateManager failed, mRootContent will be null, then, we
// should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR))
if (mRootContent && mWidget) {
if (IMEStateManager::IsTestingIME() && mEditableNode) {
nsIDocument* doc = mEditableNode->OwnerDoc();
if (doc) {
nsRefPtr<AsyncEventDispatcher> dispatcher =
new AsyncEventDispatcher(doc, NS_LITERAL_STRING("MozIMEFocusOut"),
false, false);
if (aPostEvent) {
dispatcher->PostDOMEvent();
} else {
dispatcher->RunDOMEventWhenSafe();
}
}
}
// A test event handler might destroy the widget.
if (mWidget) {
mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
}
// A test event handler might destroy the widget.
if (mWidget) {
mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
}
}
void
IMEContentObserver::UnregisterObservers()
{
if (!mIsObserving) {
return;
}
mIsObserving = false;
if (mEditor) {
mEditor->RemoveEditorObserver(this);
}
if (mUpdatePreference.WantSelectionChange() && mSelection) {
@ -259,7 +286,8 @@ IMEContentObserver::Destroy()
{
// WARNING: When you change this method, you have to check Unlink() too.
UnregisterObservers(false);
NotifyIMEOfBlur();
UnregisterObservers();
mEditor = nullptr;
// Even if there are some pending notification, it'll never notify the widget.
@ -282,16 +310,47 @@ IMEContentObserver::DisconnectFromEventStateManager()
mESM = nullptr;
}
bool
IMEContentObserver::MaybeReinitialize(nsIWidget* aWidget,
nsPresContext* aPresContext,
nsIContent* aContent,
nsIEditor* aEditor)
{
if (!IsObservingContent(aPresContext, aContent)) {
return false;
}
if (GetState() == eState_StoppedObserving) {
Init(aWidget, aPresContext, aContent, aEditor);
}
return IsManaging(aPresContext, aContent);
}
bool
IMEContentObserver::IsManaging(nsPresContext* aPresContext,
nsIContent* aContent)
{
return GetState() == eState_Observing &&
IsObservingContent(aPresContext, aContent);
}
IMEContentObserver::State
IMEContentObserver::GetState() const
{
if (!mSelection || !mRootContent || !mEditableNode) {
return false; // failed to initialize.
return eState_NotObserving; // failed to initialize or finalized.
}
if (!mRootContent->IsInComposedDoc()) {
return false; // the focused editor has already been reframed.
// the focused editor has already been reframed.
return eState_StoppedObserving;
}
return mIsObserving ? eState_Observing : eState_Initializing;
}
bool
IMEContentObserver::IsObservingContent(nsPresContext* aPresContext,
nsIContent* aContent) const
{
return mEditableNode == IMEStateManager::GetRootEditableNode(aPresContext,
aContent);
}

View File

@ -72,6 +72,17 @@ public:
* storing the instance.
*/
void DisconnectFromEventStateManager();
/**
* MaybeReinitialize() tries to restart to observe the editor's root node.
* This is useful when the editor is reframed and all children are replaced
* with new node instances.
* @return Returns true if the instance is managing the content.
* Otherwise, false.
*/
bool MaybeReinitialize(nsIWidget* aWidget,
nsPresContext* aPresContext,
nsIContent* aContent,
nsIEditor* aEditor);
bool IsManaging(nsPresContext* aPresContext, nsIContent* aContent);
bool IsEditorHandlingEventForComposition() const;
bool KeepAliveDuringDeactive() const
@ -133,6 +144,15 @@ public:
private:
~IMEContentObserver() {}
enum State {
eState_NotObserving,
eState_Initializing,
eState_StoppedObserving,
eState_Observing
};
State GetState() const;
bool IsObservingContent(nsPresContext* aPresContext,
nsIContent* aContent) const;
void MaybeNotifyIMEOfTextChange(const TextChangeData& aTextChangeData);
void MaybeNotifyIMEOfSelectionChange(bool aCausedByComposition);
void MaybeNotifyIMEOfPositionChange();
@ -140,11 +160,13 @@ private:
void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
void ObserveEditableNode();
/**
* UnregisterObservers() unresiters all listeners and observers.
* @param aPostEvent When true, DOM event will be posted to the thread.
* Otherwise, dispatched when safe.
* NotifyIMEOfBlur() notifies IME of blur.
*/
void UnregisterObservers(bool aPostEvent);
void NotifyIMEOfBlur();
/**
* UnregisterObservers() unregisters all listeners and observers.
*/
void UnregisterObservers();
void StoreTextChangeData(const TextChangeData& aTextChangeData);
void FlushMergeableNotifications();
@ -221,6 +243,7 @@ private:
uint32_t mPreAttrChangeLength;
int64_t mPreCharacterDataChangeLength;
bool mIsObserving;
bool mIsSelectionChangeEventPending;
bool mSelectionChangeCausedOnlyByComposition;
bool mIsPositionChangeEventPending;

View File

@ -180,7 +180,6 @@ GetNotifyIMEMessageName(IMEMessage aMessage)
nsIContent* IMEStateManager::sContent = nullptr;
nsPresContext* IMEStateManager::sPresContext = nullptr;
bool IMEStateManager::sInstalledMenuKeyboardListener = false;
bool IMEStateManager::sIsTestingIME = false;
bool IMEStateManager::sIsGettingNewIMEState = false;
// sActiveIMEContentObserver points to the currently active IMEContentObserver.
@ -649,9 +648,23 @@ IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
return;
}
// If the IMEContentObserver instance isn't managing the editor's current
// editable root content, the editor frame might be reframed. We should
// recreate the instance at that time.
// Even if there is active IMEContentObserver, it may not be observing the
// editor with current editable root content due to reframed. In such case,
// We should try to reinitialize the IMEContentObserver.
if (sActiveIMEContentObserver && IsIMEObserverNeeded(aNewIMEState)) {
PR_LOG(sISMLog, PR_LOG_DEBUG,
("ISM: IMEStateManager::UpdateIMEState(), try to reinitialize the "
"active IMEContentObserver"));
if (!sActiveIMEContentObserver->MaybeReinitialize(widget, sPresContext,
aContent, aEditor)) {
PR_LOG(sISMLog, PR_LOG_ERROR,
("ISM: IMEStateManager::UpdateIMEState(), failed to reinitialize the "
"active IMEContentObserver"));
}
}
// If there is no active IMEContentObserver or it isn't observing the
// editor correctly, we should recreate it.
bool createTextStateManager =
(!sActiveIMEContentObserver ||
!sActiveIMEContentObserver->IsManaging(sPresContext, aContent));
@ -1128,18 +1141,9 @@ IMEStateManager::GetRootEditableNode(nsPresContext* aPresContext,
// static
bool
IMEStateManager::IsEditableIMEState(nsIWidget* aWidget)
IMEStateManager::IsIMEObserverNeeded(const IMEState& aState)
{
switch (aWidget->GetInputContext().mIMEState.mEnabled) {
case IMEState::ENABLED:
case IMEState::PASSWORD:
return true;
case IMEState::PLUGIN:
case IMEState::DISABLED:
return false;
default:
MOZ_CRASH("Unknown IME enable state");
}
return aState.IsEditable();
}
// static
@ -1193,20 +1197,14 @@ IMEStateManager::CreateIMEContentObserver(nsIEditor* aEditor)
return; // Sometimes, there are no widgets.
}
// If it's not text ediable, we don't need to create IMEContentObserver.
if (!IsEditableIMEState(widget)) {
// If it's not text editable, we don't need to create IMEContentObserver.
if (!IsIMEObserverNeeded(widget->GetInputContext().mIMEState)) {
MOZ_LOG(sISMLog, PR_LOG_DEBUG,
("ISM: IMEStateManager::CreateIMEContentObserver() doesn't create "
"IMEContentObserver because of non-editable IME state"));
return;
}
static bool sInitializeIsTestingIME = true;
if (sInitializeIsTestingIME) {
Preferences::AddBoolVarCache(&sIsTestingIME, "test.IME", false);
sInitializeIsTestingIME = false;
}
MOZ_LOG(sISMLog, PR_LOG_DEBUG,
("ISM: IMEStateManager::CreateIMEContentObserver() is creating an "
"IMEContentObserver instance..."));

View File

@ -138,7 +138,6 @@ public:
static nsINode* GetRootEditableNode(nsPresContext* aPresContext,
nsIContent* aContent);
static bool IsTestingIME() { return sIsTestingIME; }
protected:
static nsresult OnChangeFocusInternal(nsPresContext* aPresContext,
@ -157,12 +156,11 @@ protected:
static bool IsEditable(nsINode* node);
static bool IsEditableIMEState(nsIWidget* aWidget);
static bool IsIMEObserverNeeded(const IMEState& aState);
static nsIContent* sContent;
static nsPresContext* sPresContext;
static bool sInstalledMenuKeyboardListener;
static bool sIsTestingIME;
static bool sIsGettingNewIMEState;
class MOZ_STACK_CLASS GettingNewIMEStateBlocker final

View File

@ -224,23 +224,34 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
if (NS_IsMainThread()) {
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
if (!doc) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
nsCOMPtr<nsIDocument> doc;
nsCOMPtr<nsILoadGroup> loadGroup;
nsIPrincipal* principal;
if (window) {
doc = window->GetExtantDoc();
if (!doc) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
principal = doc->NodePrincipal();
loadGroup = doc->GetDocumentLoadGroup();
} else {
principal = aGlobal->PrincipalOrNull();
if (NS_WARN_IF(!principal)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsresult rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), principal);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
}
Telemetry::Accumulate(Telemetry::FETCH_IS_MAINTHREAD, 1);
nsRefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(p);
nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
nsRefPtr<FetchDriver> fetch =
new FetchDriver(r, doc->NodePrincipal(), loadGroup);
nsRefPtr<FetchDriver> fetch = new FetchDriver(r, principal, loadGroup);
fetch->SetDocument(doc);
aRv = fetch->Fetch(resolver);
if (NS_WARN_IF(aRv.Failed())) {
@ -411,6 +422,23 @@ UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest)
doc->GetReferrer(referrer);
aRequest->SetReferrer(referrer);
}
} else if (NS_IsMainThread()) {
// Pull the principal from the global for non-worker scripts.
nsIPrincipal *principal = aGlobal->PrincipalOrNull();
bool isNull;
// Only set the referrer if the principal is present,
// and the principal is not null or the system principal.
if (principal &&
NS_SUCCEEDED(principal->GetIsNullPrincipal(&isNull)) && !isNull &&
!nsContentUtils::IsSystemPrincipal(principal)) {
nsCOMPtr<nsIURI> uri;
if (NS_SUCCEEDED(principal->GetURI(getter_AddRefs(uri))) && uri) {
nsAutoCString referrer;
if (NS_SUCCEEDED(uri->GetSpec(referrer))) {
aRequest->SetReferrer(NS_ConvertUTF8toUTF16(referrer));
}
}
}
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);

View File

@ -3353,6 +3353,19 @@ nsHTMLDocument::QueryCommandEnabled(const nsAString& commandID, ErrorResult& rv)
return false;
}
// cut & copy are always allowed
bool isCutCopy = commandID.LowerCaseEqualsLiteral("cut") ||
commandID.LowerCaseEqualsLiteral("copy");
if (isCutCopy) {
return nsContentUtils::IsCutCopyAllowed();
}
// Report false for restricted commands
bool restricted = commandID.LowerCaseEqualsLiteral("paste");
if (restricted && !nsContentUtils::IsCallerChrome()) {
return false;
}
// if editing is not on, bail
if (!IsEditingOnAfterFlush()) {
rv.Throw(NS_ERROR_FAILURE);

View File

@ -15,6 +15,7 @@ Implement HTML5 sandbox attribute for IFRAMEs
/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs **/
/** Navigation tests Part 1**/
SimpleTest.requestLongerTimeout(2); // slow on Android
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("untriaged");
// a postMessage handler that is used by sandboxed iframes without

View File

@ -16,6 +16,7 @@ Implement HTML5 sandbox attribute for IFRAMEs
/** Navigation tests Part 2**/
SimpleTest.expectAssertions(0);
SimpleTest.requestLongerTimeout(2); // slow on Android
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("untriaged");
// a postMessage handler that is used by sandboxed iframes without

View File

@ -2246,6 +2246,14 @@ TabParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent)
aEvent.mReply.mRect =
aEvent.mReply.mRect.Union(mIMECompositionRects[i]);
}
if (aEvent.mInput.mOffset < mIMECacheText.Length()) {
aEvent.mReply.mString =
Substring(mIMECacheText, aEvent.mInput.mOffset,
mIMECacheText.Length() >= aEvent.mInput.EndOffset() ?
aEvent.mInput.mLength : UINT32_MAX);
} else {
aEvent.mReply.mString.Truncate();
}
aEvent.mReply.mOffset = aEvent.mInput.mOffset;
aEvent.mReply.mRect = aEvent.mReply.mRect - GetChildProcessOffset();
aEvent.mReply.mWritingMode = mWritingMode;

View File

@ -6,6 +6,9 @@
#ifndef mozilla_dom_CanvasCaptureMediaStream_h_
#define mozilla_dom_CanvasCaptureMediaStream_h_
#include "DOMMediaStream.h"
#include "StreamBuffer.h"
namespace mozilla {
class DOMMediaStream;
class MediaStreamListener;

View File

@ -207,7 +207,7 @@ IdpSandbox.prototype = {
wantGlobalProperties: [
'indexedDB', 'XMLHttpRequest', 'TextEncoder', 'TextDecoder',
'URL', 'URLSearchParams', 'atob', 'btoa', 'Blob', 'crypto',
'rtcIdentityProvider'
'rtcIdentityProvider', 'fetch'
]
});
let registrar = this.sandbox.rtcIdentityProvider;

View File

@ -1078,6 +1078,17 @@ MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
StopPrerollingVideo();
}
// Schedule the state machine to send stream data as soon as possible or
// the VideoQueue() is empty before the Push().
// VideoQueue() is empty implies the state machine thread doesn't have
// precise time information about video frames. Once the first video
// frame pushed in the queue, schedule the state machine as soon as
// possible to render the video frame or delay the state machine thread
// accurately.
if (mAudioCaptured || VideoQueue().GetSize() == 1) {
ScheduleStateMachine();
}
// For non async readers, if the requested video sample was slow to
// arrive, increase the amount of audio we buffer to ensure that we
// don't run out of audio. This is unnecessary for async readers,
@ -1097,11 +1108,6 @@ MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
DECODER_LOG("Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld",
mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs);
}
// Schedule the state machine to send stream data as soon as possible.
if (mAudioCaptured) {
ScheduleStateMachine();
}
return;
}
case DECODER_STATE_SEEKING: {
@ -2959,9 +2965,7 @@ void MediaDecoderStateMachine::AdvanceFrame()
// Current frame has already been presented, wait until it's time to
// present the next frame.
if (frame && !currentFrame) {
int64_t now = IsPlaying() ? clock_time : mStartTime + mPlayDuration;
remainingTime = frame->mTime - now;
remainingTime = frame->mTime - clock_time;
}
}
@ -3043,6 +3047,22 @@ void MediaDecoderStateMachine::AdvanceFrame()
currentFrame = nullptr;
}
// The remainingTime is negative (include zero):
// 1. When the clock_time is larger than the latest video frame's endtime.
// All the video frames should be rendered or dropped, nothing left in
// VideoQueue. And since the VideoQueue is empty, we don't need to wake up
// statemachine thread immediately, so set the remainingTime to default value.
// 2. Current frame's endtime is smaller than clock_time but there still exist
// newer frames in queue. Re-calculate the remainingTime.
if (remainingTime <= 0) {
VideoData* nextFrame = VideoQueue().PeekFront();
if (nextFrame) {
remainingTime = nextFrame->mTime - clock_time;
} else {
remainingTime = AUDIO_DURATION_USECS;
}
}
int64_t delay = remainingTime / mPlaybackRate;
if (delay > 0) {
ScheduleStateMachineIn(delay);

View File

@ -67,33 +67,6 @@ IsValidTimestampUs(int64_t aTimestamp)
return aTimestamp >= INT64_C(0);
}
MediaCodecReader::VideoResourceListener::VideoResourceListener(
MediaCodecReader* aReader)
: mReader(aReader)
{
}
MediaCodecReader::VideoResourceListener::~VideoResourceListener()
{
mReader = nullptr;
}
void
MediaCodecReader::VideoResourceListener::codecReserved()
{
if (mReader) {
mReader->VideoCodecReserved();
}
}
void
MediaCodecReader::VideoResourceListener::codecCanceled()
{
if (mReader) {
mReader->VideoCodecCanceled();
}
}
MediaCodecReader::TrackInputCopier::~TrackInputCopier()
{
}
@ -276,7 +249,6 @@ MediaCodecReader::MediaCodecReader(AbstractMediaDecoder* aDecoder)
, mNextParserPosition(INT64_C(0))
, mParsedDataLength(INT64_C(0))
{
mVideoListener = new VideoResourceListener(this);
}
MediaCodecReader::~MediaCodecReader()
@ -676,14 +648,6 @@ MediaCodecReader::ReadMetadata(MediaInfo* aInfo,
return NS_OK;
}
// Configure video codec after the codecReserved.
if (mVideoTrack.mSource != nullptr) {
if (!ConfigureMediaCodec(mVideoTrack)) {
DestroyMediaCodec(mVideoTrack);
return NS_ERROR_FAILURE;
}
}
// TODO: start streaming
if (!UpdateDuration()) {
@ -1295,8 +1259,8 @@ MediaCodecReader::CreateTaskQueues()
bool
MediaCodecReader::CreateMediaCodecs()
{
if (CreateMediaCodec(mLooper, mAudioTrack, false, nullptr) &&
CreateMediaCodec(mLooper, mVideoTrack, true, mVideoListener)) {
if (CreateMediaCodec(mLooper, mAudioTrack, nullptr) &&
CreateMediaCodec(mLooper, mVideoTrack, nullptr)) {
return true;
}
@ -1306,7 +1270,6 @@ MediaCodecReader::CreateMediaCodecs()
bool
MediaCodecReader::CreateMediaCodec(sp<ALooper>& aLooper,
Track& aTrack,
bool aAsync,
wp<MediaCodecProxy::CodecResourceListener> aListener)
{
if (aTrack.mSource != nullptr && aTrack.mCodec == nullptr) {
@ -1321,6 +1284,10 @@ MediaCodecReader::CreateMediaCodec(sp<ALooper>& aLooper,
NS_WARNING("Couldn't create MediaCodecProxy");
return false;
}
if (!aTrack.mCodec->AskMediaCodecAndWait()) {
NS_WARNING("AskMediaCodecAndWait fail");
return false;
}
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
aTrack.mInputCopier = new VorbisInputCopier;
@ -1343,14 +1310,10 @@ MediaCodecReader::CreateMediaCodec(sp<ALooper>& aLooper,
#endif
}
if (!aAsync) {
// Pending configure() and start() to codecReserved() if the creation
// should be asynchronous.
if (!aTrack.mCodec->allocated() || !ConfigureMediaCodec(aTrack)){
NS_WARNING("Couldn't create and configure MediaCodec synchronously");
DestroyMediaCodec(aTrack);
return false;
}
if (!aTrack.mCodec->allocated() || !ConfigureMediaCodec(aTrack)) {
NS_WARNING("Couldn't create and configure MediaCodec synchronously");
DestroyMediaCodec(aTrack);
return false;
}
}
@ -1869,6 +1832,7 @@ MediaCodecReader::EnsureCodecFormatParsed(Track& aTrack)
size_t size = 0;
int64_t timeUs = INT64_C(0);
uint32_t flags = 0;
FillCodecInputData(aTrack);
while ((status = aTrack.mCodec->dequeueOutputBuffer(&index, &offset, &size,
&timeUs, &flags)) != INFO_FORMAT_CHANGED) {
if (status == OK) {
@ -1879,17 +1843,11 @@ MediaCodecReader::EnsureCodecFormatParsed(Track& aTrack)
NS_WARNING("Couldn't get output buffers from MediaCodec");
return false;
}
} else if (status != -EAGAIN && status != INVALID_OPERATION){
} else if (status != -EAGAIN && status != INVALID_OPERATION) {
// FIXME: let INVALID_OPERATION pass?
return false; // something wrong!!!
}
status = FillCodecInputData(aTrack);
if (status == INFO_FORMAT_CHANGED) {
break;
} else if (status != OK) {
return false;
}
FillCodecInputData(aTrack);
}
return aTrack.mCodec->getOutputFormat(&format) == OK;
}
@ -1917,22 +1875,4 @@ MediaCodecReader::ClearColorConverterBuffer()
mColorConverterBufferSize = 0;
}
// Called on Binder thread.
void
MediaCodecReader::VideoCodecReserved()
{
mDecoder->NotifyWaitingForResourcesStatusChanged();
}
// Called on Binder thread.
void
MediaCodecReader::VideoCodecCanceled()
{
if (mVideoTrack.mTaskQueue) {
RefPtr<nsIRunnable> task =
NS_NewRunnableMethod(this, &MediaCodecReader::ReleaseCriticalResources);
mVideoTrack.mTaskQueue->Dispatch(task.forget());
}
}
} // namespace mozilla

View File

@ -177,11 +177,6 @@ protected:
// Called on MediaCodecReader::mLooper thread.
void onMessageReceived(const android::sp<android::AMessage>& aMessage);
// Receive a notify from ResourceListener.
// Called on Binder thread.
virtual void VideoCodecReserved();
virtual void VideoCodecCanceled();
virtual bool CreateExtractor();
// Check the underlying HW resource is available and store the result in
@ -194,28 +189,6 @@ protected:
bool mIsWaitingResources;
private:
// An intermediary class that can be managed by android::sp<T>.
// Redirect codecReserved() and codecCanceled() to MediaCodecReader.
class VideoResourceListener : public android::MediaCodecProxy::CodecResourceListener
{
public:
VideoResourceListener(MediaCodecReader* aReader);
~VideoResourceListener();
virtual void codecReserved();
virtual void codecCanceled();
private:
// Forbidden
VideoResourceListener() = delete;
VideoResourceListener(const VideoResourceListener& rhs) = delete;
const VideoResourceListener& operator=(const VideoResourceListener& rhs) = delete;
MediaCodecReader* mReader;
};
friend class VideoResourceListener;
class VorbisInputCopier : public TrackInputCopier
{
virtual bool Copy(android::MediaBuffer* aSourceBuffer,
@ -354,7 +327,6 @@ private:
bool CreateMediaCodecs();
static bool CreateMediaCodec(android::sp<android::ALooper>& aLooper,
Track& aTrack,
bool aAsync,
android::wp<android::MediaCodecProxy::CodecResourceListener> aListener);
static bool ConfigureMediaCodec(Track& aTrack);
void DestroyMediaCodecs();
@ -415,8 +387,6 @@ private:
void ReleaseAllTextureClients();
android::sp<VideoResourceListener> mVideoListener;
android::sp<android::ALooper> mLooper;
android::sp<android::MetaData> mMetaData;

View File

@ -21,12 +21,8 @@
IDPJS.prototype = {
getLogin: function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/.well-known/idp-proxy/idp.sjs?' + this.id);
return new Promise(resolve => {
xhr.onload = e => resolve(xhr.status === 200);
xhr.send();
});
return fetch('https://example.com/.well-known/idp-proxy/idp.sjs?' + this.id)
.then(response => response.status === 200);
},
checkLogin: function(result) {
return this.getLogin()

View File

@ -19,6 +19,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=346659
/** Test for Bug 346659 **/
var numTests = 10;
SimpleTest.requestLongerTimeout(2); // test takes a long time on android and b2g emulators
SimpleTest.waitForExplicitFinish();
var wins = [];

View File

@ -39,6 +39,7 @@ function testSameOriginBlobURL() {
var blob = new Blob(["english ", "sentence"], { type: "text/plain" });
var url = URL.createObjectURL(blob);
return fetch(url).then(function(res) {
URL.revokeObjectURL(url);
ok(true, "Blob URL fetch should resolve");
if (res.type == "error") {
ok(false, "Blob URL fetch should not fail.");

View File

@ -307,6 +307,15 @@ const knownFailures = {
"QE-Proposed-UNBOOKMARK_TEXT-1-dM": true,
"QE-Proposed-UNBOOKMARK_TEXT-1-body": true,
"QE-Proposed-UNBOOKMARK_TEXT-1-div": true,
"QE-Proposed-COPY_TEXT-1-dM": true,
"QE-Proposed-COPY_TEXT-1-body": true,
"QE-Proposed-COPY_TEXT-1-div": true,
"QE-Proposed-CUT_TEXT-1-dM": true,
"QE-Proposed-CUT_TEXT-1-body": true,
"QE-Proposed-CUT_TEXT-1-div": true,
"QE-Proposed-PASTE_TEXT-1-dM": true,
"QE-Proposed-PASTE_TEXT-1-body": true,
"QE-Proposed-PASTE_TEXT-1-div": true,
"QS-Proposed-SUB_SPAN.sub-1-SI-dM": true,
"QS-Proposed-SUB_SPAN.sub-1-SI-body": true,
"QS-Proposed-SUB_SPAN.sub-1-SI-div": true,

View File

@ -162,4 +162,5 @@ skip-if = e10s
[test_spellcheck_pref.html]
skip-if = toolkit == 'android'
[test_bug1068979.html]
[test_bug1109465.html]
[test_bug1109465.html]
[test_bug1162952.html]

View File

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1162952
-->
<head>
<title>Test for Bug 1162952</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1162952">Mozilla Bug 1162952</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 1162952 **/
var userCallbackRun = false;
document.addEventListener('keydown', function() {
// During a user callback, the commands should be enabled
userCallbackRun = true;
is(true, document.queryCommandEnabled('cut'));
is(true, document.queryCommandEnabled('copy'));
});
// Otherwise, they should be disabled
is(false, document.queryCommandEnabled('cut'));
is(false, document.queryCommandEnabled('copy'));
// Fire a user callback
synthesizeKey('A', {});
ok(userCallbackRun, "User callback should've been run");
</script>
</pre>
</body>
</html>

View File

@ -45,7 +45,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=408231
["justifyleft", "true"],
["justifyright", "true"],
["outdent", "true"],
//["paste", "true"],
["paste", "false"],
["redo", "false"],
["removeformat", "true"],
["selectall", "true"],

View File

@ -35,6 +35,7 @@ var gBlock1, gBlock2;
var alwaysEnabledCommands = [
"contentReadOnly",
"copy",
"cut",
"enableInlineTableEditing",
"enableObjectResizing",
"insertBrOnReturn",
@ -89,9 +90,12 @@ function runTests() {
IsCommandEnabled(commands[i]);
// These are privileged, and available only to chrome.
commands = ["cut", "paste", "copy"];
commands = ["paste"];
for (i = 0; i < commands.length; i++) {
IsCommandEnabled(commands[i]);
is(document.queryCommandEnabled(commands[i]), false,
"Command should not be enabled for non-privileged code");
is(SpecialPowers.wrap(document).queryCommandEnabled(commands[i]), true,
"Command should be enabled for privileged code");
try {
document.execCommand(commands[i], false, false);
ok(false, "Thould have thrown: " + commands[i]);

Some files were not shown because too many files have changed in this diff Show More