merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-12-15 14:17:53 +01:00
commit 877ffa1db2
93 changed files with 1818 additions and 881 deletions

View File

@ -486,7 +486,6 @@ tags = fullscreen
[browser_menuButtonBadgeManager.js]
[browser_newTabDrop.js]
[browser_newWindowDrop.js]
skip-if = true # bug 1323276
[browser_csp_block_all_mixedcontent.js]
tags = mcb
[browser_newwindow_focus.js]

View File

@ -25,7 +25,7 @@ add_task(function* test_setup() {
0);
CustomizableUI.ensureWidgetPlacedInWindow("new-window-button", window);
registerCleanupFunction(function() {
CustomizableUI.reset();
CustomizableUI.removeWidgetFromArea("new-window-button");
});
}
});

View File

@ -1,5 +1,6 @@
"use strict";
const {SessionSaver} = Cu.import("resource:///modules/sessionstore/SessionSaver.jsm", {});
const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
/**
@ -84,6 +85,8 @@ add_task(function* dontTemporarilyShowAboutHome() {
yield BrowserTestUtils.closeWindow(win);
ok(SessionStore.getClosedWindowCount(), "Should have a closed window");
yield SessionSaver.run();
windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
win = SessionStore.undoCloseWindow(0);
yield windowOpenedPromise;

View File

@ -8,4 +8,6 @@ support-files =
skip-if = (os == "linux" && debug) # linux: bug 976544
[browser_devices_get_user_media_anim.js]
[browser_devices_get_user_media_in_frame.js]
[browser_devices_get_user_media_screen.js]
skip-if = (e10s && debug) || (os == "linux" && !debug) # bug 1320754 for e10s debug, and bug 1320994 for linux opt
[browser_devices_get_user_media_tear_off_tab.js]

View File

@ -34,8 +34,8 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "CameraAndMicrophone",
"expected camera and microphone to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true, video: true},
"expected camera and microphone to be shared");
yield indicator;
yield checkSharingUI({audio: true, video: true});
@ -64,8 +64,8 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "Microphone",
"expected microphone to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true},
"expected microphone to be shared");
yield indicator;
yield checkSharingUI({audio: true});
@ -94,7 +94,8 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "Camera", "expected camera to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {video: true},
"expected camera to be shared");
yield indicator;
yield checkSharingUI({video: true});
@ -136,8 +137,8 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "CameraAndMicrophone",
"expected camera and microphone to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true, video: true},
"expected camera and microphone to be shared");
yield indicator;
yield checkSharingUI({video: true, audio: true});
@ -164,20 +165,13 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "CameraAndMicrophone",
"expected camera and microphone to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true, video: true},
"expected camera and microphone to be shared");
yield indicator;
yield checkSharingUI({video: true, audio: true});
info("reloading the web page");
promise = promiseObserverCalled("recording-device-events");
content.location.reload();
yield promise;
yield expectObserverCalled("recording-window-ended");
yield expectNoObserverCalled();
yield checkNotSharing();
yield reloadAndAssertClosedStreams();
}
},
@ -203,23 +197,22 @@ var gTests = [
yield promiseMessage(expectedMessage, () => {
activateSecondaryAction(aNever ? kActionNever : kActionAlways);
});
let expected = [];
let expected = {};
if (expectedMessage == "ok") {
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
if (aRequestVideo)
expected.push("Camera");
expected.video = true;
if (aRequestAudio)
expected.push("Microphone");
expected = expected.join("And");
expected.audio = true;
}
else {
yield expectObserverCalled("getUserMedia:response:deny");
yield expectObserverCalled("recording-window-ended");
expected = "none";
}
is((yield getMediaCaptureState()), expected,
"expected " + expected + " to be shared");
Assert.deepEqual((yield getMediaCaptureState()), expected,
"expected " + Object.keys(expected).join(" and ") +
" to be shared");
function checkDevicePermissions(aDevice, aExpected) {
let Perms = Services.perms;
@ -302,14 +295,14 @@ var gTests = [
yield expectObserverCalled("recording-device-events");
// Check what's actually shared.
let expected = [];
let expected = {};
if (aAllowVideo && aRequestVideo)
expected.push("Camera");
expected.video = true;
if (aAllowAudio && aRequestAudio)
expected.push("Microphone");
expected = expected.join("And");
is((yield getMediaCaptureState()), expected,
"expected " + expected + " to be shared");
expected.audio = true;
Assert.deepEqual((yield getMediaCaptureState()), expected,
"expected " + Object.keys(expected).join(" and ") +
" to be shared");
yield closeStream();
}
@ -447,7 +440,8 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "Camera", "expected camera to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {video: true},
"expected camera to be shared");
yield indicator;
yield checkSharingUI({video: true});

View File

@ -22,13 +22,15 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
let expected = [];
let expected = {};
if (aVideo)
expected.push("Camera");
expected.video = true;
if (aAudio)
expected.push("Microphone");
is((yield getMediaCaptureState()), expected.join("And"),
"expected stream to be shared");
expected.audio = true;
Assert.deepEqual((yield getMediaCaptureState()), expected,
"expected " + Object.keys(expected).join(" and ") +
" to be shared");
// Check the attribute on the tab, and check there's no visible
// sharing icon on the tab

View File

@ -39,8 +39,8 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "CameraAndMicrophone",
"expected camera and microphone to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true, video: true},
"expected camera and microphone to be shared");
yield indicator;
yield checkSharingUI({audio: true, video: true});
@ -63,8 +63,8 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "CameraAndMicrophone",
"expected camera and microphone to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true, video: true},
"expected camera and microphone to be shared");
yield indicator;
yield checkSharingUI({video: true, audio: true});
@ -104,8 +104,8 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "CameraAndMicrophone",
"expected camera and microphone to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true, video: true},
"expected camera and microphone to be shared");
yield indicator;
yield checkSharingUI({video: true, audio: true});
@ -160,7 +160,8 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "Microphone", "microphone to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true},
"expected microphone to be shared");
yield indicator;
yield checkSharingUI({video: false, audio: true});
@ -177,8 +178,8 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "CameraAndMicrophone",
"expected camera and microphone to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true, video: true},
"expected camera and microphone to be shared");
yield checkSharingUI({video: true, audio: true});
yield expectNoObserverCalled();
@ -213,20 +214,13 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "CameraAndMicrophone",
"expected camera and microphone to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true, video: true},
"expected camera and microphone to be shared");
yield indicator;
yield checkSharingUI({video: true, audio: true});
info("reloading the web page");
promise = promiseObserverCalled("recording-device-events");
content.location.reload();
yield promise;
yield expectObserverCalled("recording-window-ended");
yield expectNoObserverCalled();
yield checkNotSharing();
yield reloadAndAssertClosedStreams();
}
}

View File

@ -0,0 +1,611 @@
/* 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/. */
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
const permissionError = "error: NotAllowedError: The request is not allowed " +
"by the user agent or the platform in the current context.";
const notFoundError =
"error: NotFoundError: The object can not be found here.";
var gTests = [
{
desc: "getUserMedia screen only",
run: function* checkScreenOnly() {
let promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(false, true, null, "screen");
yield promise;
yield expectObserverCalled("getUserMedia:request");
is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
"webRTC-shareScreen-notification-icon", "anchored to device icon");
checkDeviceSelectors(false, false, true);
let iconclass =
PopupNotifications.panel.firstChild.getAttribute("iconclass");
ok(iconclass.includes("screen-icon"), "panel using screen icon");
let menulist =
document.getElementById("webRTC-selectWindow-menulist");
let count = menulist.itemCount;
ok(count >= 3,
"There should be the 'No Screen' item, a separator and at least one screen");
let noScreenItem = menulist.getItemAtIndex(0);
ok(noScreenItem.hasAttribute("selected"), "the 'No Screen' item is selected");
is(menulist.value, -1, "no screen is selected by default");
is(menulist.selectedItem, noScreenItem, "'No Screen' is the selected item");
let separator = menulist.getItemAtIndex(1);
is(separator.localName, "menuseparator", "the second item is a separator");
ok(document.getElementById("webRTC-all-windows-shared").hidden,
"the 'all windows will be shared' warning should be hidden while there's no selection");
ok(document.getElementById("webRTC-preview").hidden,
"the preview area is hidden");
for (let i = 2; i < count; ++i) {
let item = menulist.getItemAtIndex(i);
is(parseInt(item.getAttribute("value")), i - 2, "the screen item has the correct index");
is(item.getAttribute("devicetype"), "Screen", "the devicetype attribute is set correctly");
ok(item.scary, "the screen item is marked as scary");
}
// Select a screen, a preview with a scary warning should appear.
menulist.getItemAtIndex(2).doCommand();
ok(!document.getElementById("webRTC-all-windows-shared").hidden,
"the 'all windows will be shared' warning should now be visible");
yield promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden);
ok(!document.getElementById("webRTC-preview").hidden,
"the preview area is visible");
ok(!document.getElementById("webRTC-previewWarning").hidden,
"the scary warning is visible");
// Select the 'No Screen' item again, the preview should be hidden.
menulist.getItemAtIndex(0).doCommand();
ok(document.getElementById("webRTC-all-windows-shared").hidden,
"the 'all windows will be shared' warning should now be hidden");
ok(document.getElementById("webRTC-preview").hidden,
"the preview area is hidden");
// Select the first screen again so that we can have a stream.
menulist.getItemAtIndex(2).doCommand();
let indicator = promiseIndicatorWindow();
yield promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
Assert.deepEqual((yield getMediaCaptureState()), {screen: "Screen"},
"expected screen to be shared");
yield indicator;
yield checkSharingUI({screen: "Screen"});
yield closeStream();
}
},
{
desc: "getUserMedia window only",
run: function* checkWindowOnly() {
let promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(false, true, null, "window");
yield promise;
yield expectObserverCalled("getUserMedia:request");
is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
"webRTC-shareScreen-notification-icon", "anchored to device icon");
checkDeviceSelectors(false, false, true);
let iconclass =
PopupNotifications.panel.firstChild.getAttribute("iconclass");
ok(iconclass.includes("screen-icon"), "panel using screen icon");
let menulist =
document.getElementById("webRTC-selectWindow-menulist");
let count = menulist.itemCount;
ok(count >= 3,
"There should be the 'No Window' item, a separator and at least one window");
let noWindowItem = menulist.getItemAtIndex(0);
ok(noWindowItem.hasAttribute("selected"), "the 'No Window' item is selected");
is(menulist.value, -1, "no window is selected by default");
is(menulist.selectedItem, noWindowItem, "'No Window' is the selected item");
let separator = menulist.getItemAtIndex(1);
is(separator.localName, "menuseparator", "the second item is a separator");
ok(document.getElementById("webRTC-all-windows-shared").hidden,
"the 'all windows will be shared' warning should be hidden");
ok(document.getElementById("webRTC-preview").hidden,
"the preview area is hidden");
let scaryIndex, nonScaryIndex;
for (let i = 2; i < count; ++i) {
let item = menulist.getItemAtIndex(i);
is(parseInt(item.getAttribute("value")), i - 2, "the window item has the correct index");
is(item.getAttribute("devicetype"), "Window", "the devicetype attribute is set correctly");
if (item.scary)
scaryIndex = i;
else
nonScaryIndex = i;
}
ok(typeof scaryIndex == "number", "there's at least one scary window, as Firefox is running");
// Select a scary window, a preview with a scary warning should appear.
menulist.getItemAtIndex(scaryIndex).doCommand();
ok(document.getElementById("webRTC-all-windows-shared").hidden,
"the 'all windows will be shared' warning should still be hidden");
yield promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden);
ok(!document.getElementById("webRTC-preview").hidden,
"the preview area is visible");
ok(!document.getElementById("webRTC-previewWarning").hidden,
"the scary warning is visible");
// Select the 'No Window' item again, the preview should be hidden.
menulist.getItemAtIndex(0).doCommand();
ok(document.getElementById("webRTC-preview").hidden,
"the preview area is hidden");
// If we have a non-scary window, select it and verify the warning isn't displayed.
// A non-scary window may not always exist on test slaves.
if (typeof nonScaryIndex == "number") {
menulist.getItemAtIndex(nonScaryIndex).doCommand();
ok(document.getElementById("webRTC-all-windows-shared").hidden,
"the 'all windows will be shared' warning should still be hidden");
yield promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden);
ok(!document.getElementById("webRTC-preview").hidden,
"the preview area is visible");
ok(document.getElementById("webRTC-previewWarning").hidden,
"the scary warning is hidden");
} else {
info("no non-scary window available on this test slave");
// Select the first window again so that we can have a stream.
menulist.getItemAtIndex(scaryIndex).doCommand();
}
let indicator = promiseIndicatorWindow();
yield promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
Assert.deepEqual((yield getMediaCaptureState()), {screen: "Window"},
"expected window to be shared");
yield indicator;
yield checkSharingUI({screen: "Window"});
yield closeStream();
}
},
{
desc: "getUserMedia application only",
run: function* checkAppOnly() {
if (AppConstants.platform == "linux") {
todo(false, "Bug 1323490 - On Linux 64, this test fails with 'NotFoundError' and causes the next test (screen+audio) to timeout");
return;
}
let promptPromise = promisePopupNotificationShown("webRTC-shareDevices");
let messagePromise = promiseMessageReceived();
yield promiseRequestDevice(false, true, null, "application");
let message = yield Promise.race([promptPromise, messagePromise]);
if (message) {
is(message, notFoundError,
"NotFoundError, likely because there's no other application running.");
return;
}
yield expectObserverCalled("getUserMedia:request");
is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
"webRTC-shareScreen-notification-icon", "anchored to device icon");
checkDeviceSelectors(false, false, true);
let iconclass =
PopupNotifications.panel.firstChild.getAttribute("iconclass");
ok(iconclass.includes("screen-icon"), "panel using screen icon");
let menulist =
document.getElementById("webRTC-selectWindow-menulist");
let count = menulist.itemCount;
ok(count >= 3,
"There should be the 'No Application' item, a separator and at least one application");
let noApplicationItem = menulist.getItemAtIndex(0);
ok(noApplicationItem.hasAttribute("selected"), "the 'No Application' item is selected");
is(menulist.value, -1, "no app is selected by default");
is(menulist.selectedItem, noApplicationItem, "'No Application' is the selected item");
let separator = menulist.getItemAtIndex(1);
is(separator.localName, "menuseparator", "the second item is a separator");
ok(document.getElementById("webRTC-all-windows-shared").hidden,
"the 'all windows will be shared' warning should be hidden");
ok(document.getElementById("webRTC-preview").hidden,
"the preview area is hidden");
let scaryIndex;
for (let i = 2; i < count; ++i) {
let item = menulist.getItemAtIndex(i);
is(parseInt(item.getAttribute("value")), i - 2, "the app item has the correct index");
is(item.getAttribute("devicetype"), "Application", "the devicetype attribute is set correctly");
if (item.scary)
scaryIndex = i;
}
ok(scaryIndex === undefined,
"Firefox is currently excluding itself from the list of applications, " +
"so no scary app should be listed");
menulist.getItemAtIndex(2).doCommand();
ok(document.getElementById("webRTC-all-windows-shared").hidden,
"the 'all windows will be shared' warning should still be hidden");
yield promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden);
ok(!document.getElementById("webRTC-preview").hidden,
"the preview area is visible");
ok(document.getElementById("webRTC-previewWarning").hidden,
"the scary warning is hidden");
let indicator = promiseIndicatorWindow();
yield promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
Assert.deepEqual((yield getMediaCaptureState()), {screen: "Application"},
"expected application to be shared");
yield indicator;
yield checkSharingUI({screen: "Application"});
yield closeStream();
}
},
{
desc: "getUserMedia audio+screen",
run: function* checkAudioVideo() {
if (AppConstants.platform == "macosx") {
todo(false, "Bug 1323481 - On Mac on treeherder, but not locally, requesting microphone + screen never makes the permission prompt appear, and so causes the test to timeout");
return;
}
let promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(true, true, null, "screen");
yield promise;
yield expectObserverCalled("getUserMedia:request");
is(PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
"webRTC-shareScreen-notification-icon", "anchored to device icon");
checkDeviceSelectors(true, false, true);
let iconclass =
PopupNotifications.panel.firstChild.getAttribute("iconclass");
ok(iconclass.includes("screen-icon"), "panel using screen icon");
let menulist =
document.getElementById("webRTC-selectWindow-menulist");
let count = menulist.itemCount;
ok(count >= 3,
"There should be the 'No Screen' item, a separator and at least one screen");
// Select a screen, a preview with a scary warning should appear.
menulist.getItemAtIndex(2).doCommand();
ok(!document.getElementById("webRTC-all-windows-shared").hidden,
"the 'all windows will be shared' warning should now be visible");
yield promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden);
ok(!document.getElementById("webRTC-preview").hidden,
"the preview area is visible");
ok(!document.getElementById("webRTC-previewWarning").hidden,
"the scary warning is visible");
let indicator = promiseIndicatorWindow();
yield promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
Assert.deepEqual((yield getMediaCaptureState()),
{audio: true, screen: "Screen"},
"expected screen and microphone to be shared");
yield indicator;
yield checkSharingUI({audio: true, screen: "Screen"});
yield closeStream();
}
},
{
desc: "getUserMedia screen: clicking through without selecting a screen denies",
run: function* checkReloading() {
let promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(false, true, null, "screen");
yield promise;
yield expectObserverCalled("getUserMedia:request");
checkDeviceSelectors(false, false, true);
yield promiseMessage(permissionError, () => {
PopupNotifications.panel.firstChild.button.click();
});
yield expectObserverCalled("getUserMedia:response:deny");
yield expectObserverCalled("recording-window-ended");
yield checkNotSharing();
}
},
{
desc: "getUserMedia screen, user clicks \"Don't Allow\"",
run: function* checkDontShare() {
let promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(false, true, null, "screen");
yield promise;
yield expectObserverCalled("getUserMedia:request");
checkDeviceSelectors(false, false, true);
yield promiseMessage(permissionError, () => {
activateSecondaryAction(kActionDeny);
});
yield expectObserverCalled("getUserMedia:response:deny");
yield expectObserverCalled("recording-window-ended");
yield checkNotSharing();
}
},
{
desc: "getUserMedia audio+video+screen: stop sharing",
run: function* checkStopSharing() {
if (AppConstants.platform == "macosx") {
todo(false, "Bug 1323481 - On Mac on treeherder, but not locally, requesting microphone + screen never makes the permission prompt appear, and so causes the test to timeout");
return;
}
function* share(audio, video, screen) {
let promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(audio, video || !!screen,
null, screen && "screen");
yield promise;
yield expectObserverCalled("getUserMedia:request");
checkDeviceSelectors(audio, video, screen);
if (screen) {
document.getElementById("webRTC-selectWindow-menulist")
.getItemAtIndex(2).doCommand();
}
yield promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
}
function* check(expected = {}) {
let shared = Object.keys(expected).join(" and ");
if (shared) {
Assert.deepEqual((yield getMediaCaptureState()), expected,
"expected " + shared + " to be shared");
yield checkSharingUI(expected);
} else {
yield checkNotSharing();
}
}
info("Share screen and microphone");
let indicator = promiseIndicatorWindow();
yield share(true, false, true);
yield indicator;
yield check({audio: true, screen: "Screen"});
info("Share camera");
yield share(false, true);
yield check({video: true, audio: true, screen: "Screen"});
info("Stop the screen share, mic+cam should continue");
yield stopSharing("screen", true);
yield check({video: true, audio: true});
info("Stop the camera, everything should stop.");
yield stopSharing("camera", false, true);
info("Now, share only the screen...");
indicator = promiseIndicatorWindow();
yield share(false, false, true);
yield indicator;
yield check({screen: "Screen"});
info("... and add camera and microphone in a second request.");
yield share(true, true);
yield check({video: true, audio: true, screen: "Screen"});
info("Stop the camera, this should stop everything.");
yield stopSharing("camera", false, true);
}
},
{
desc: "getUserMedia screen: reloading the page removes all gUM UI",
run: function* checkReloading() {
let promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(false, true, null, "screen");
yield promise;
yield expectObserverCalled("getUserMedia:request");
checkDeviceSelectors(false, false, true);
document.getElementById("webRTC-selectWindow-menulist")
.getItemAtIndex(2).doCommand();
let indicator = promiseIndicatorWindow();
yield promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
Assert.deepEqual((yield getMediaCaptureState()), {screen: "Screen"},
"expected screen to be shared");
yield indicator;
yield checkSharingUI({screen: "Screen"});
yield reloadAndAssertClosedStreams();
}
},
{
desc: "test showControlCenter from screen icon",
run: function* checkShowControlCenter() {
let promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(false, true, null, "screen");
yield promise;
yield expectObserverCalled("getUserMedia:request");
checkDeviceSelectors(false, false, true);
document.getElementById("webRTC-selectWindow-menulist")
.getItemAtIndex(2).doCommand();
let indicator = promiseIndicatorWindow();
yield promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
Assert.deepEqual((yield getMediaCaptureState()), {screen: "Screen"},
"expected screen to be shared");
yield indicator;
yield checkSharingUI({screen: "Screen"});
ok(gIdentityHandler._identityPopup.hidden, "control center should be hidden");
if ("nsISystemStatusBar" in Ci) {
let activeStreams = webrtcUI.getActiveStreams(false, false, true);
webrtcUI.showSharingDoorhanger(activeStreams[0]);
}
else {
let win =
Services.wm.getMostRecentWindow("Browser:WebRTCGlobalIndicator");
let elt = win.document.getElementById("screenShareButton");
EventUtils.synthesizeMouseAtCenter(elt, {}, win);
yield promiseWaitForCondition(() => !gIdentityHandler._identityPopup.hidden);
}
ok(!gIdentityHandler._identityPopup.hidden, "control center should be open");
gIdentityHandler._identityPopup.hidden = true;
yield expectNoObserverCalled();
yield closeStream();
}
},
{
desc: "Only persistent block is possible for screen sharing",
run: function* checkPersistentPermissions() {
let Perms = Services.perms;
let uri = gBrowser.selectedBrowser.documentURI;
let devicePerms = Perms.testExactPermission(uri, "screen");
is(devicePerms, Perms.UNKNOWN_ACTION,
"starting without screen persistent permissions");
let promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(false, true, null, "screen");
yield promise;
yield expectObserverCalled("getUserMedia:request");
checkDeviceSelectors(false, false, true);
document.getElementById("webRTC-selectWindow-menulist")
.getItemAtIndex(2).doCommand();
// Ensure that checking the 'Remember this decision' checkbox disables
// 'Allow'.
let notification = PopupNotifications.panel.firstChild;
ok(notification.hasAttribute("warninghidden"), "warning message is hidden");
let checkbox = notification.checkbox;
ok(!!checkbox, "checkbox is present");
ok(!checkbox.checked, "checkbox is not checked");
checkbox.click();
ok(checkbox.checked, "checkbox now checked");
ok(notification.button.disabled, "Allow button is disabled");
ok(!notification.hasAttribute("warninghidden"), "warning message is shown");
// Click "Don't Allow" to save a persistent block permission.
yield promiseMessage(permissionError, () => {
activateSecondaryAction(kActionDeny);
});
yield expectObserverCalled("getUserMedia:response:deny");
yield expectObserverCalled("recording-window-ended");
yield checkNotSharing();
is(Perms.testExactPermission(uri, "screen"), Perms.DENY_ACTION,
"screen sharing is persistently blocked");
// Request screensharing again, expect an immediate failure.
promise = promiseMessage(permissionError);
yield promiseRequestDevice(false, true, null, "screen");
yield promise;
yield expectObserverCalled("recording-window-ended");
// Now set the permission to allow and expect a prompt.
Perms.add(uri, "screen", Perms.ALLOW_ACTION);
// Request devices and expect a prompt despite the saved 'Allow' permission.
promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(false, true, null, "screen");
yield promise;
yield expectObserverCalled("getUserMedia:request");
// The 'remember' checkbox shouldn't be checked anymore.
notification = PopupNotifications.panel.firstChild;
ok(notification.hasAttribute("warninghidden"), "warning message is hidden");
checkbox = notification.checkbox;
ok(!!checkbox, "checkbox is present");
ok(!checkbox.checked, "checkbox is not checked");
// Deny the request to cleanup...
yield promiseMessage(permissionError, () => {
activateSecondaryAction(kActionDeny);
});
yield expectObserverCalled("getUserMedia:response:deny");
yield expectObserverCalled("recording-window-ended");
Perms.remove(uri, "screen");
}
}
];
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
browser.addEventListener("load", function onload() {
browser.removeEventListener("load", onload, true);
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
ok(gIdentityHandler._identityPopup.hidden,
"should start the test with the control center hidden");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run();
// Cleanup before the next test
yield expectNoObserverCalled();
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, true);
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
}

View File

@ -23,8 +23,8 @@ var gTests = [
});
yield expectObserverCalled("getUserMedia:response:allow");
yield expectObserverCalled("recording-device-events");
is((yield getMediaCaptureState()), "CameraAndMicrophone",
"expected camera and microphone to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {audio: true, video: true},
"expected camera and microphone to be shared");
yield indicator;
yield checkSharingUI({video: true, audio: true});

View File

@ -17,6 +17,20 @@ const kObservedTopics = [
var gObservedTopics = {};
function observer(aSubject, aTopic, aData) {
// With e10s disabled, our content script receives notifications for the
// preview displayed in our screen sharing permission prompt; ignore them.
const kBrowserURL = "chrome://browser/content/browser.xul";
const nsIPropertyBag = Components.interfaces.nsIPropertyBag;
if (aTopic == "recording-device-events" &&
aSubject.QueryInterface(nsIPropertyBag).getProperty("requestURL") == kBrowserURL) {
return;
}
if (aTopic == "recording-window-ended") {
let win = Services.wm.getOuterWindowWithId(aData).top;
if (win.document.documentURI == kBrowserURL)
return;
}
if (!(aTopic in gObservedTopics))
gObservedTopics[aTopic] = 1;
else
@ -44,19 +58,28 @@ function _getMediaCaptureState() {
let hasAudio = {};
let hasScreenShare = {};
let hasWindowShare = {};
let hasAppShare = {};
let hasBrowserShare = {};
MediaManagerService.mediaCaptureWindowState(content, hasVideo, hasAudio,
hasScreenShare, hasWindowShare);
if (hasVideo.value && hasAudio.value)
return "CameraAndMicrophone";
hasScreenShare, hasWindowShare,
hasAppShare, hasBrowserShare);
let result = {};
if (hasVideo.value)
return "Camera";
result.video = true;
if (hasAudio.value)
return "Microphone";
result.audio = true;
if (hasScreenShare.value)
return "Screen";
if (hasWindowShare.value)
return "Window";
return "none";
result.screen = "Screen";
else if (hasWindowShare.value)
result.screen = "Window";
else if (hasAppShare.value)
result.screen = "Application";
else if (hasBrowserShare.value)
result.screen = "Browser";
return result;
}
addMessageListener("Test:GetMediaCaptureState", data => {

View File

@ -102,7 +102,7 @@ function* assertWebRTCIndicatorStatus(expected) {
if (expected.audio)
expectAudio = true;
if (expected.screen)
expectScreen = true;
expectScreen = expected.screen;
}
is(ui.showCameraIndicator, expectVideo, "camera global indicator as expected");
is(ui.showMicrophoneIndicator, expectAudio, "microphone global indicator as expected");
@ -217,15 +217,25 @@ function expectObserverCalled(aTopic) {
});
}
function expectNoObserverCalled() {
function expectNoObserverCalled(aIgnoreDeviceEvents = false) {
return new Promise(resolve => {
let mm = _mm();
mm.addMessageListener("Test:ExpectNoObserverCalled:Reply",
function listener({data}) {
mm.removeMessageListener("Test:ExpectNoObserverCalled:Reply", listener);
for (let topic in data) {
if (data[topic])
if (!data[topic])
continue;
// If we are stopping tracks that were created from 2 different
// getUserMedia calls, the "recording-device-events" notification is
// fired twice on Windows and Mac, and intermittently twice on Linux.
if (topic == "recording-device-events" && aIgnoreDeviceEvents) {
todo(false, "Got " + data[topic] + " unexpected " + topic +
" notifications, see bug 1320994");
} else {
is(data[topic], 0, topic + " notification unexpected");
}
}
resolve();
});
@ -233,18 +243,26 @@ function expectNoObserverCalled() {
});
}
function promiseMessage(aMessage, aAction) {
let promise = new Promise((resolve, reject) => {
function promiseMessageReceived() {
return new Promise((resolve, reject) => {
let mm = _mm();
mm.addMessageListener("Test:MessageReceived", function listener({data}) {
mm.removeMessageListener("Test:MessageReceived", listener);
resolve(data);
});
mm.sendAsyncMessage("Test:WaitForMessage");
});
}
function promiseMessage(aMessage, aAction) {
let promise = new Promise((resolve, reject) => {
promiseMessageReceived(aAction).then(data => {
is(data, aMessage, "received " + aMessage);
if (data == aMessage)
resolve();
else
reject();
mm.removeMessageListener("Test:MessageReceived", listener);
});
mm.sendAsyncMessage("Test:WaitForMessage");
});
if (aAction)
@ -328,7 +346,8 @@ function getMediaCaptureState() {
});
}
function* stopSharing(aType = "camera") {
function* stopSharing(aType = "camera", aShouldKeepSharing = false,
aExpectDoubleRecordingEvent = false) {
let promiseRecordingEvent = promiseObserverCalled("recording-device-events");
gIdentityHandler._identityBox.click();
let permissions = document.getElementById("identity-popup-permission-list");
@ -339,9 +358,16 @@ function* stopSharing(aType = "camera") {
gIdentityHandler._identityPopup.hidden = true;
yield promiseRecordingEvent;
yield expectObserverCalled("getUserMedia:revoke");
yield expectObserverCalled("recording-window-ended");
yield expectNoObserverCalled();
yield* checkNotSharing();
// If we are stopping screen sharing and expect to still have another stream,
// "recording-window-ended" won't be fired.
if (!aShouldKeepSharing)
yield expectObserverCalled("recording-window-ended");
yield expectNoObserverCalled(aExpectDoubleRecordingEvent);
if (!aShouldKeepSharing)
yield* checkNotSharing();
}
function promiseRequestDevice(aRequestAudio, aRequestVideo, aFrameId, aType) {
@ -379,7 +405,19 @@ function* closeStream(aAlreadyClosed, aFrameId) {
yield* assertWebRTCIndicatorStatus(null);
}
function checkDeviceSelectors(aAudio, aVideo) {
function* reloadAndAssertClosedStreams() {
info("reloading the web page");
let promise = promiseObserverCalled("recording-device-events");
yield ContentTask.spawn(gBrowser.selectedBrowser, null,
"() => content.location.reload()");
yield promise;
yield expectObserverCalled("recording-window-ended");
yield expectNoObserverCalled();
yield checkNotSharing();
}
function checkDeviceSelectors(aAudio, aVideo, aScreen) {
let micSelector = document.getElementById("webRTC-selectMicrophone");
if (aAudio)
ok(!micSelector.hidden, "microphone selector visible");
@ -391,6 +429,12 @@ function checkDeviceSelectors(aAudio, aVideo) {
ok(!cameraSelector.hidden, "camera selector visible");
else
ok(cameraSelector.hidden, "camera selector hidden");
let screenSelector = document.getElementById("webRTC-selectWindowOrScreen");
if (aScreen)
ok(!screenSelector.hidden, "screen selector visible");
else
ok(screenSelector.hidden, "screen selector hidden");
}
function* checkSharingUI(aExpected, aWin = window) {
@ -399,7 +443,9 @@ function* checkSharingUI(aExpected, aWin = window) {
let identityBox = doc.getElementById("identity-box");
ok(identityBox.hasAttribute("sharing"), "sharing attribute is set");
let sharing = identityBox.getAttribute("sharing");
if (aExpected.video)
if (aExpected.screen)
is(sharing, "screen", "showing screen icon on the control center icon");
else if (aExpected.video)
is(sharing, "camera", "showing camera icon on the control center icon");
else if (aExpected.audio)
is(sharing, "microphone", "showing mic icon on the control center icon");
@ -439,7 +485,8 @@ function* checkSharingUI(aExpected, aWin = window) {
}
function* checkNotSharing() {
is((yield getMediaCaptureState()), "none", "expected nothing to be shared");
Assert.deepEqual((yield getMediaCaptureState()), {},
"expected nothing to be shared");
ok(!document.getElementById("identity-box").hasAttribute("sharing"),
"no sharing indicator on the control center icon");

View File

@ -194,9 +194,14 @@ var gSearchPane = {
}
},
onInputBlur: function() {
onInputBlur: function(aEvent) {
let tree = document.getElementById("engineList");
tree.stopEditing(false);
if (!tree.hasAttribute("editing"))
return;
// Accept input unless discarded.
let accept = aEvent.charCode != KeyEvent.DOM_VK_ESCAPE;
tree.stopEditing(accept);
},
onTreeSelect: function() {

View File

@ -186,6 +186,9 @@ var SessionSaverInternal = {
let state = SessionStore.getCurrentState(forceUpdateAllWindows);
PrivacyFilter.filterPrivateWindowsAndTabs(state);
// Make sure we only write worth saving tabs to disk.
SessionStore.keepOnlyWorthSavingTabs(state);
// Make sure that we keep the previous session if we started with a single
// private window and no non-private windows have been opened, yet.
if (state.deferredInitialState) {

View File

@ -375,6 +375,34 @@ this.SessionStore = {
}
return number <= FORMAT_VERSION;
},
/**
* Filters out not worth-saving tabs from a given browser state object.
*
* @param aState (object)
* The browser state for which we remove worth-saving tabs.
* The given object will be modified.
*/
keepOnlyWorthSavingTabs: function (aState) {
for (let i = aState.windows.length - 1; i >= 0; i--) {
let win = aState.windows[i];
for (let j = win.tabs.length - 1; j >= 0; j--) {
let tab = win.tabs[j];
if (!SessionStoreInternal._shouldSaveTabState(tab)) {
win.tabs.splice(j, 1);
if (win.selected > j) {
win.selected--;
}
}
}
if (!win.tabs.length) {
aState.windows.splice(i, 1);
if (aState.selectedWindow > i) {
aState.selectedWindow--;
}
}
}
},
};
// Freeze the SessionStore object. We don't want anyone to modify it.

View File

@ -35,8 +35,8 @@ add_task(function* new_window() {
newWin = null;
let state = JSON.parse((yield promiseRecoveryFileContents()));
is(state.windows.length, 2,
"observe1: 2 windows in data written to disk");
is(state.windows.length, 1,
"observe1: 1 window in data written to disk");
is(state._closedWindows.length, 0,
"observe1: no closed windows in data written to disk");
@ -57,6 +57,8 @@ add_task(function* new_tab() {
let newTab;
try {
newTab = gBrowser.addTab("about:mozilla");
yield promiseBrowserLoaded(newTab.linkedBrowser);
yield TabStateFlusher.flush(newTab.linkedBrowser);
let state = JSON.parse((yield promiseRecoveryFileContents()));
is(state.windows.length, 1,
@ -68,7 +70,9 @@ add_task(function* new_tab() {
is(ss.getClosedWindowCount(), 1,
"observe2: 1 closed window according to API");
} finally {
gBrowser.removeTab(newTab);
if (newTab) {
gBrowser.removeTab(newTab);
}
}
});
@ -77,6 +81,7 @@ add_task(function* done() {
// The API still represents the closed window as closed, so we can clear it
// with the API, but just to make sure...
// is(ss.getClosedWindowCount(), 1, "1 closed window according to API");
yield promiseAllButPrimaryWindowClosed();
forgetClosedWindows();
Services.prefs.clearUserPref("browser.sessionstore.interval");
});

View File

@ -8,16 +8,16 @@ requestLongerTimeout(2);
add_task(function* test_1() {
let win = yield promiseNewWindowLoaded();
win.gBrowser.addTab("http://www.example.com/1");
yield promiseTabLoad(win, "http://www.example.com/#1");
win = yield promiseNewWindowLoaded({private: true});
win.gBrowser.addTab("http://www.example.com/2");
yield promiseTabLoad(win, "http://www.example.com/#2");
win = yield promiseNewWindowLoaded();
win.gBrowser.addTab("http://www.example.com/3");
yield promiseTabLoad(win, "http://www.example.com/#3");
win = yield promiseNewWindowLoaded({private: true});
win.gBrowser.addTab("http://www.example.com/4");
yield promiseTabLoad(win, "http://www.example.com/#4");
let curState = JSON.parse(ss.getBrowserState());
is(curState.windows.length, 5, "Browser has opened 5 windows");
@ -27,9 +27,9 @@ add_task(function* test_1() {
let state = JSON.parse(yield promiseRecoveryFileContents());
is(state.windows.length, 3,
"sessionstore state: 3 windows in data being written to disk");
is(state.selectedWindow, 3,
is(state.windows.length, 2,
"sessionstore state: 2 windows in data being written to disk");
is(state.selectedWindow, 2,
"Selected window is updated to match one of the saved windows");
ok(state.windows.every(win => !win.isPrivate),
"Saved windows are not private");
@ -44,10 +44,10 @@ add_task(function* test_1() {
// Test opening default mochitest window + 2 private windows
add_task(function* test_2() {
let win = yield promiseNewWindowLoaded({private: true});
win.gBrowser.addTab("http://www.example.com/1");
yield promiseTabLoad(win, "http://www.example.com/#1");
win = yield promiseNewWindowLoaded({private: true});
win.gBrowser.addTab("http://www.example.com/2");
yield promiseTabLoad(win, "http://www.example.com/#2");
let curState = JSON.parse(ss.getBrowserState());
is(curState.windows.length, 3, "Browser has opened 3 windows");
@ -57,10 +57,10 @@ add_task(function* test_2() {
let state = JSON.parse(yield promiseRecoveryFileContents());
is(state.windows.length, 1,
"sessionstore state: 1 windows in data being written to disk");
is(state.selectedWindow, 1,
"Selected window is updated to match one of the saved windows");
is(state.windows.length, 0,
"sessionstore state: no window in data being written to disk");
is(state.selectedWindow, 0,
"Selected window updated to 0 given there are no saved windows");
is(state._closedWindows.length, 0,
"sessionstore state: no closed windows in data being written to disk");
@ -72,13 +72,13 @@ add_task(function* test_2() {
// Test opening default-normal-private-normal windows and closing a normal window
add_task(function* test_3() {
let normalWindow = yield promiseNewWindowLoaded();
yield promiseTabLoad(normalWindow, "http://www.example.com/");
yield promiseTabLoad(normalWindow, "http://www.example.com/#1");
let win = yield promiseNewWindowLoaded({private: true});
yield promiseTabLoad(win, "http://www.example.com/");
yield promiseTabLoad(win, "http://www.example.com/#2");
win = yield promiseNewWindowLoaded();
yield promiseTabLoad(win, "http://www.example.com/");
yield promiseTabLoad(win, "http://www.example.com/#3");
let curState = JSON.parse(ss.getBrowserState());
is(curState.windows.length, 4, "Browser has opened 4 windows");
@ -96,9 +96,9 @@ add_task(function* test_3() {
let state = JSON.parse(yield promiseRecoveryFileContents());
is(state.windows.length, 2,
"sessionstore state: 2 windows in data being written to disk");
is(state.selectedWindow, 2,
is(state.windows.length, 1,
"sessionstore state: 1 window in data being written to disk");
is(state.selectedWindow, 1,
"Selected window is updated to match one of the saved windows");
ok(state.windows.every(win => !win.isPrivate),
"Saved windows are not private");
@ -113,8 +113,7 @@ add_task(function* test_3() {
});
function* promiseTabLoad(win, url) {
let browser = win.gBrowser.selectedBrowser;
browser.loadURI(url);
yield promiseBrowserLoaded(browser);
yield TabStateFlusher.flush(browser);
let tab = win.gBrowser.addTab(url);
yield promiseBrowserLoaded(tab.linkedBrowser);
yield TabStateFlusher.flush(tab.linkedBrowser);
}

View File

@ -25,6 +25,8 @@ add_task(function* () {
ok(SessionStore.getClosedWindowCount(), "Should have a closed window");
yield forceSaveState();
win = SessionStore.undoCloseWindow(0);
yield TestUtils.topicObserved("sessionstore-single-window-restored",
subject => subject == win);
@ -50,6 +52,8 @@ add_task(function* () {
ok(SessionStore.getClosedWindowCount(), "Should have a closed window");
yield forceSaveState();
win = SessionStore.undoCloseWindow(0);
yield TestUtils.topicObserved("sessionstore-single-window-restored",
subject => subject == win);

View File

@ -124,6 +124,8 @@ add_task(function test_scroll_background_tabs() {
// Close the window
yield BrowserTestUtils.closeWindow(newWin);
yield forceSaveState();
// Now restore the window
newWin = ss.undoCloseWindow(0);

View File

@ -35,6 +35,8 @@ add_task(function test_scroll_background_about_reader_tabs() {
// Close the window
yield BrowserTestUtils.closeWindow(newWin);
yield forceSaveState();
// Now restore the window
newWin = ss.undoCloseWindow(0);

View File

@ -117,10 +117,6 @@ browser.jar:
skin/classic/browser/sync-horizontalbar@2x.png
skin/classic/browser/sync-mobileIcon.svg (../shared/sync-mobileIcon.svg)
skin/classic/browser/sync-notification-24.png
skin/classic/browser/syncProgress-menuPanel.png
skin/classic/browser/syncProgress-menuPanel@2x.png
skin/classic/browser/syncProgress-toolbar.png
skin/classic/browser/syncProgress-toolbar-inverted.png
skin/classic/browser/syncSetup.css
skin/classic/browser/syncCommon.css
skin/classic/browser/syncQuota.css

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -168,12 +168,6 @@ browser.jar:
skin/classic/browser/syncQuota.css
skin/classic/browser/syncProgress-horizontalbar.png
skin/classic/browser/syncProgress-horizontalbar@2x.png
skin/classic/browser/syncProgress-menuPanel.png
skin/classic/browser/syncProgress-menuPanel@2x.png
skin/classic/browser/syncProgress-toolbar.png
skin/classic/browser/syncProgress-toolbar@2x.png
skin/classic/browser/syncProgress-toolbar-inverted.png
skin/classic/browser/syncProgress-toolbar-inverted@2x.png
skin/classic/browser/Toolbar-background-noise.png (Toolbar-background-noise.png)
skin/classic/browser/lion/toolbarbutton-dropmarker.png (toolbarbutton-dropmarker-lion.png)
skin/classic/browser/toolbarbutton-dropmarker@2x.png (toolbarbutton-dropmarker-lion@2x.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View File

@ -178,14 +178,7 @@ browser.jar:
skin/classic/browser/syncProgress-horizontalbar@2x.png
skin/classic/browser/syncProgress-horizontalbar-XPVista7.png
skin/classic/browser/syncProgress-horizontalbar-XPVista7@2x.png
skin/classic/browser/syncProgress-menuPanel.png
skin/classic/browser/syncProgress-menuPanel@2x.png
skin/classic/browser/syncProgress-toolbar.png
skin/classic/browser/syncProgress-toolbar@2x.png
skin/classic/browser/syncProgress-toolbar-inverted.png
skin/classic/browser/syncProgress-toolbar-inverted@2x.png
skin/classic/browser/syncProgress-toolbar-XPVista7.png
skin/classic/browser/syncProgress-toolbar-XPVista7@2x.png
#ifdef E10S_TESTING_ONLY
skin/classic/browser/e10s-64@2x.png (../shared/e10s-64@2x.png)
#endif
@ -231,8 +224,6 @@ browser.jar:
% override chrome://browser/skin/sync-horizontalbar@2x.png chrome://browser/skin/sync-horizontalbar-XPVista7@2x.png os=WINNT osversion<=6.1
% override chrome://browser/skin/syncProgress-horizontalbar.png chrome://browser/skin/syncProgress-horizontalbar-XPVista7.png os=WINNT osversion<=6.1
% override chrome://browser/skin/syncProgress-horizontalbar@2x.png chrome://browser/skin/syncProgress-horizontalbar-XPVista7@2x.png os=WINNT osversion<=6.1
% override chrome://browser/skin/syncProgress-toolbar.png chrome://browser/skin/syncProgress-toolbar-XPVista7.png os=WINNT osversion<=6.1
% override chrome://browser/skin/syncProgress-toolbar@2x.png chrome://browser/skin/syncProgress-toolbar-XPVista7@2x.png os=WINNT osversion<=6.1
% override chrome://browser/skin/toolbarbutton-dropdown-arrow.png chrome://browser/skin/toolbarbutton-dropdown-arrow-XPVista7.png os=WINNT osversion<=6.1
% override chrome://browser/skin/urlbar-history-dropmarker.png chrome://browser/skin/urlbar-history-dropmarker-XPVista7.png os=WINNT osversion<=6.1
% override chrome://browser/skin/urlbar-history-dropmarker@2x.png chrome://browser/skin/urlbar-history-dropmarker-XPVista7@2x.png os=WINNT osversion<=6.1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -164,13 +164,8 @@ NS_INTERFACE_MAP_END
/* static */ bool
CustomElementRegistry::IsCustomElementEnabled(JSContext* aCx, JSObject* aObject)
{
JS::Rooted<JSObject*> obj(aCx, aObject);
if (Preferences::GetBool("dom.webcomponents.customelements.enabled") ||
Preferences::GetBool("dom.webcomponents.enabled")) {
return true;
}
return false;
return Preferences::GetBool("dom.webcomponents.customelements.enabled") ||
Preferences::GetBool("dom.webcomponents.enabled");
}
/* static */ already_AddRefed<CustomElementRegistry>
@ -183,8 +178,7 @@ CustomElementRegistry::Create(nsPIDOMWindowInner* aWindow)
return nullptr;
}
if (!Preferences::GetBool("dom.webcomponents.customelements.enabled") &&
!Preferences::GetBool("dom.webcomponents.enabled")) {
if (!IsCustomElementEnabled()) {
return nullptr;
}

View File

@ -140,7 +140,8 @@ public:
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementRegistry)
public:
static bool IsCustomElementEnabled(JSContext* aCx, JSObject* aObject);
static bool IsCustomElementEnabled(JSContext* aCx = nullptr,
JSObject* aObject = nullptr);
static already_AddRefed<CustomElementRegistry> Create(nsPIDOMWindowInner* aWindow);
static void ProcessTopElementQueue();

View File

@ -23,6 +23,7 @@
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/EventStates.h"
#include "mozilla/ServoRestyleManager.h"
#include "mozilla/dom/Attr.h"
#include "nsDOMAttributeMap.h"
#include "nsIAtom.h"
@ -1112,6 +1113,12 @@ FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
slots->mXBLInsertionParent = nullptr;
}
}
// We just changed the flattened tree, so any Servo style data is now invalid.
// We rely on nsXBLService::LoadBindings to re-traverse the subtree afterwards.
if (IsStyledByServo() && IsElement() && AsElement()->HasServoData()) {
ServoRestyleManager::ClearServoDataFromSubtree(AsElement());
}
}
CustomElementData*

View File

@ -11,6 +11,7 @@
#include "nsITimeoutHandler.h"
#include "mozilla/dom/TabGroup.h"
using namespace mozilla;
using namespace mozilla::dom;
static int32_t gRunningTimeoutDepth = 0;
@ -227,7 +228,8 @@ TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
}
}
InsertTimeoutIntoList(timeout);
mTimeouts.Insert(timeout, mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
: Timeouts::SortBy::TimeWhen);
timeout->mTimeoutId = GetTimeoutId(aReason);
*aReturn = timeout->mTimeoutId;
@ -239,30 +241,30 @@ void
TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason)
{
uint32_t timerId = (uint32_t)aTimerId;
Timeout* timeout;
for (timeout = mTimeouts.GetFirst(); timeout; timeout = timeout->getNext()) {
if (timeout->mTimeoutId == timerId && timeout->mReason == aReason) {
if (timeout->mRunning) {
/* We're running from inside the timeout. Mark this
timeout for deferred deletion by the code in
ForEachTimeoutAbortable([&](Timeout* aTimeout) {
if (aTimeout->mTimeoutId == timerId && aTimeout->mReason == aReason) {
if (aTimeout->mRunning) {
/* We're running from inside the aTimeout. Mark this
aTimeout for deferred deletion by the code in
RunTimeout() */
timeout->mIsInterval = false;
aTimeout->mIsInterval = false;
}
else {
/* Delete the timeout from the pending timeout list */
timeout->remove();
/* Delete the aTimeout from the pending aTimeout list */
aTimeout->remove();
if (timeout->mTimer) {
timeout->mTimer->Cancel();
timeout->mTimer = nullptr;
timeout->Release();
if (aTimeout->mTimer) {
aTimeout->mTimer->Cancel();
aTimeout->mTimer = nullptr;
aTimeout->Release();
}
timeout->Release();
aTimeout->Release();
}
break;
return true; // abort!
}
}
return false;
});
}
void
@ -420,7 +422,8 @@ TimeoutManager::RunTimeout(Timeout* aTimeout)
if (needsReinsertion) {
// Insert interval timeout onto list sorted in deadline order.
// AddRefs timeout.
InsertTimeoutIntoList(timeout);
mTimeouts.Insert(timeout, mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
: Timeouts::SortBy::TimeWhen);
}
// Release the timeout struct since it's possibly out of the list
@ -629,6 +632,21 @@ TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS
return NS_OK;
}
Timeouts::SortBy sortBy = mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
: Timeouts::SortBy::TimeWhen;
return mTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS,
DOMMinTimeoutValue(),
sortBy,
mWindow.GetThrottledEventQueue());
}
nsresult
TimeoutManager::Timeouts::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS,
int32_t aMinTimeoutValueMS,
SortBy aSortBy,
ThrottledEventQueue* aQueue)
{
TimeStamp now = TimeStamp::Now();
// If insertion point is non-null, we're in the middle of firing timers and
@ -638,8 +656,8 @@ TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS
// point or anything before it, so should start at the timer after insertion
// point, if there is one.
// Otherwise, start at the beginning of the list.
for (Timeout* timeout = mTimeouts.InsertionPoint() ?
mTimeouts.InsertionPoint()->getNext() : mTimeouts.GetFirst();
for (Timeout* timeout = InsertionPoint() ?
InsertionPoint()->getNext() : GetFirst();
timeout; ) {
// It's important that this check be <= so that we guarantee that
// taking std::max with |now| won't make a quantity equal to
@ -662,7 +680,7 @@ TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS
// background window
TimeDuration interval =
TimeDuration::FromMilliseconds(std::max(timeout->mInterval,
uint32_t(DOMMinTimeoutValue())));
uint32_t(aMinTimeoutValueMS)));
uint32_t oldIntervalMillisecs = 0;
timeout->mTimer->GetDelay(&oldIntervalMillisecs);
TimeDuration oldInterval = TimeDuration::FromMilliseconds(oldIntervalMillisecs);
@ -690,15 +708,14 @@ TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS
NS_ASSERTION(!nextTimeout ||
timeout->mWhen < nextTimeout->mWhen, "How did that happen?");
timeout->remove();
// InsertTimeoutIntoList will addref |timeout| and reset
// mFiringDepth. Make sure to undo that after calling it.
// Insert() will addref |timeout| and reset mFiringDepth. Make sure to
// undo that after calling it.
uint32_t firingDepth = timeout->mFiringDepth;
InsertTimeoutIntoList(timeout);
Insert(timeout, aSortBy);
timeout->mFiringDepth = firingDepth;
timeout->Release();
nsresult rv = timeout->InitTimer(mWindow.GetThrottledEventQueue(),
delay.ToMilliseconds());
nsresult rv = timeout->InitTimer(aQueue, delay.ToMilliseconds());
if (NS_FAILED(rv)) {
NS_WARNING("Error resetting non background timer for DOM timeout!");
@ -717,36 +734,37 @@ TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS
void
TimeoutManager::ClearAllTimeouts()
{
Timeout* timeout;
Timeout* nextTimeout;
bool seenRunningTimeout = false;
for (timeout = mTimeouts.GetFirst(); timeout; timeout = nextTimeout) {
ForEachTimeout([&](Timeout* aTimeout) {
/* If RunTimeout() is higher up on the stack for this
window, e.g. as a result of document.write from a timeout,
then we need to reset the list insertion point for
newly-created timeouts in case the user adds a timeout,
before we pop the stack back to RunTimeout. */
if (mRunningTimeout == timeout) {
mTimeouts.SetInsertionPoint(nullptr);
if (mRunningTimeout == aTimeout) {
seenRunningTimeout = true;
}
nextTimeout = timeout->getNext();
if (timeout->mTimer) {
timeout->mTimer->Cancel();
timeout->mTimer = nullptr;
if (aTimeout->mTimer) {
aTimeout->mTimer->Cancel();
aTimeout->mTimer = nullptr;
// Drop the count since the timer isn't going to hold on
// anymore.
timeout->Release();
aTimeout->Release();
}
// Set timeout->mCleared to true to indicate that the timeout was
// cleared and taken out of the list of timeouts
timeout->mCleared = true;
aTimeout->mCleared = true;
// Drop the count since we're removing it from the list.
timeout->Release();
aTimeout->Release();
});
if (seenRunningTimeout) {
mTimeouts.SetInsertionPoint(nullptr);
}
// Clear out our list
@ -754,16 +772,16 @@ TimeoutManager::ClearAllTimeouts()
}
void
TimeoutManager::InsertTimeoutIntoList(Timeout* aTimeout)
TimeoutManager::Timeouts::Insert(Timeout* aTimeout, SortBy aSortBy)
{
// Start at mLastTimeout and go backwards. Don't go further than insertion
// point, though. This optimizes for the common case of insertion at the end.
Timeout* prevSibling;
for (prevSibling = mTimeouts.GetLast();
prevSibling && prevSibling != mTimeouts.InsertionPoint() &&
for (prevSibling = GetLast();
prevSibling && prevSibling != InsertionPoint() &&
// This condition needs to match the one in SetTimeoutOrInterval that
// determines whether to set mWhen or mTimeRemaining.
(mWindow.IsFrozen() ?
(aSortBy == SortBy::TimeRemaining ?
prevSibling->mTimeRemaining > aTimeout->mTimeRemaining :
prevSibling->mWhen > aTimeout->mWhen);
prevSibling = prevSibling->getPrevious()) {
@ -774,7 +792,7 @@ TimeoutManager::InsertTimeoutIntoList(Timeout* aTimeout)
if (prevSibling) {
prevSibling->setNext(aTimeout);
} else {
mTimeouts.InsertFront(aTimeout);
InsertFront(aTimeout);
}
aTimeout->mFiringDepth = 0;
@ -808,33 +826,31 @@ TimeoutManager::EndRunningTimeout(Timeout* aTimeout)
void
TimeoutManager::UnmarkGrayTimers()
{
for (Timeout* timeout = mTimeouts.GetFirst();
timeout;
timeout = timeout->getNext()) {
if (timeout->mScriptHandler) {
timeout->mScriptHandler->MarkForCC();
ForEachTimeout([](Timeout* aTimeout) {
if (aTimeout->mScriptHandler) {
aTimeout->mScriptHandler->MarkForCC();
}
}
});
}
void
TimeoutManager::Suspend()
{
for (Timeout* t = mTimeouts.GetFirst(); t; t = t->getNext()) {
ForEachTimeout([](Timeout* aTimeout) {
// Leave the timers with the current time remaining. This will
// cause the timers to potentially fire when the window is
// Resume()'d. Time effectively passes while suspended.
// Drop the XPCOM timer; we'll reschedule when restoring the state.
if (t->mTimer) {
t->mTimer->Cancel();
t->mTimer = nullptr;
if (aTimeout->mTimer) {
aTimeout->mTimer->Cancel();
aTimeout->mTimer = nullptr;
// Drop the reference that the timer's closure had on this timeout, we'll
// add it back in Resume().
t->Release();
aTimeout->Release();
}
}
});
}
void
@ -843,67 +859,67 @@ TimeoutManager::Resume()
TimeStamp now = TimeStamp::Now();
DebugOnly<bool> _seenDummyTimeout = false;
for (Timeout* t = mTimeouts.GetFirst(); t; t = t->getNext()) {
ForEachTimeout([&](Timeout* aTimeout) {
// There's a chance we're being called with RunTimeout on the stack in which
// case we have a dummy timeout in the list that *must not* be resumed. It
// can be identified by a null mWindow.
if (!t->mWindow) {
if (!aTimeout->mWindow) {
NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
_seenDummyTimeout = true;
continue;
return;
}
MOZ_ASSERT(!t->mTimer);
MOZ_ASSERT(!aTimeout->mTimer);
// The timeout mWhen is set to the absolute time when the timer should
// fire. Recalculate the delay from now until that deadline. If the
// the deadline has already passed or falls within our minimum delay
// deadline, then clamp the resulting value to the minimum delay. The
// mWhen will remain at its absolute time, but we won't fire the OS
// mWhen will remain at its absolute time, but we won'aTimeout fire the OS
// timer until our calculated delay has passed.
int32_t remaining = 0;
if (t->mWhen > now) {
remaining = static_cast<int32_t>((t->mWhen - now).ToMilliseconds());
if (aTimeout->mWhen > now) {
remaining = static_cast<int32_t>((aTimeout->mWhen - now).ToMilliseconds());
}
uint32_t delay = std::max(remaining, DOMMinTimeoutValue());
t->mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!t->mTimer) {
t->remove();
continue;
aTimeout->mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!aTimeout->mTimer) {
aTimeout->remove();
return;
}
nsresult rv = t->InitTimer(mWindow.GetThrottledEventQueue(), delay);
nsresult rv = aTimeout->InitTimer(mWindow.GetThrottledEventQueue(), delay);
if (NS_FAILED(rv)) {
t->mTimer = nullptr;
t->remove();
continue;
aTimeout->mTimer = nullptr;
aTimeout->remove();
return;
}
// Add a reference for the new timer's closure.
t->AddRef();
}
aTimeout->AddRef();
});
}
void
TimeoutManager::Freeze()
{
TimeStamp now = TimeStamp::Now();
for (Timeout *t = mTimeouts.GetFirst(); t; t = t->getNext()) {
ForEachTimeout([&](Timeout* aTimeout) {
// Save the current remaining time for this timeout. We will
// re-apply it when the window is Thaw()'d. This effectively
// shifts timers to the right as if time does not pass while
// the window is frozen.
if (t->mWhen > now) {
t->mTimeRemaining = t->mWhen - now;
if (aTimeout->mWhen > now) {
aTimeout->mTimeRemaining = aTimeout->mWhen - now;
} else {
t->mTimeRemaining = TimeDuration(0);
aTimeout->mTimeRemaining = TimeDuration(0);
}
// Since we are suspended there should be no OS timer set for
// this timeout entry.
MOZ_ASSERT(!t->mTimer);
}
MOZ_ASSERT(!aTimeout->mTimer);
});
}
void
@ -912,19 +928,19 @@ TimeoutManager::Thaw()
TimeStamp now = TimeStamp::Now();
DebugOnly<bool> _seenDummyTimeout = false;
for (Timeout *t = mTimeouts.GetFirst(); t; t = t->getNext()) {
ForEachTimeout([&](Timeout* aTimeout) {
// There's a chance we're being called with RunTimeout on the stack in which
// case we have a dummy timeout in the list that *must not* be resumed. It
// can be identified by a null mWindow.
if (!t->mWindow) {
if (!aTimeout->mWindow) {
NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
_seenDummyTimeout = true;
continue;
return;
}
// Set mWhen back to the time when the timer is supposed to fire.
t->mWhen = now + t->mTimeRemaining;
aTimeout->mWhen = now + aTimeout->mTimeRemaining;
MOZ_ASSERT(!t->mTimer);
}
MOZ_ASSERT(!aTimeout->mTimer);
});
}

View File

@ -13,6 +13,9 @@ class nsITimeoutHandler;
class nsGlobalWindow;
namespace mozilla {
class ThrottledEventQueue;
namespace dom {
// This class manages the timeouts in a Window's setTimeout/setInterval pool.
@ -44,9 +47,6 @@ public:
bool aRunningPendingTimeouts);
void ClearAllTimeouts();
// Insert aTimeout into the list, before all timeouts that would
// fire after it, but no earlier than mTimeoutInsertionPoint, if any.
void InsertTimeoutIntoList(mozilla::dom::Timeout* aTimeout);
uint32_t GetTimeoutId(mozilla::dom::Timeout::Reason aReason);
// Apply back pressure to the window if the TabGroup ThrottledEventQueue
@ -87,11 +87,15 @@ public:
template <class Callable>
void ForEachTimeout(Callable c)
{
for (Timeout* timeout = mTimeouts.GetFirst();
timeout;
timeout = timeout->getNext()) {
c(timeout);
}
mTimeouts.ForEach(c);
}
// Run some code for each Timeout in our list, but let the callback cancel
// the iteration by returning true.
template <class Callable>
void ForEachTimeoutAbortable(Callable c)
{
mTimeouts.ForEachAbortable(c);
}
private:
@ -105,6 +109,19 @@ private:
{
}
// Insert aTimeout into the list, before all timeouts that would
// fire after it, but no earlier than mTimeoutInsertionPoint, if any.
enum class SortBy
{
TimeRemaining,
TimeWhen
};
void Insert(mozilla::dom::Timeout* aTimeout, SortBy aSortBy);
nsresult ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS,
int32_t aMinTimeoutValueMS,
SortBy aSortBy,
mozilla::ThrottledEventQueue* aQueue);
const Timeout* GetFirst() const { return mTimeoutList.getFirst(); }
Timeout* GetFirst() { return mTimeoutList.getFirst(); }
const Timeout* GetLast() const { return mTimeoutList.getLast(); }
@ -122,6 +139,28 @@ private:
return mTimeoutInsertionPoint;
}
template <class Callable>
void ForEach(Callable c)
{
for (Timeout* timeout = GetFirst();
timeout;
timeout = timeout->getNext()) {
c(timeout);
}
}
template <class Callable>
void ForEachAbortable(Callable c)
{
for (Timeout* timeout = GetFirst();
timeout;
timeout = timeout->getNext()) {
if (c(timeout)) {
break;
}
}
}
private:
// mTimeoutList is generally sorted by mWhen, unless mTimeoutInsertionPoint is
// non-null. In that case, the dummy timeout pointed to by

View File

@ -12737,7 +12737,7 @@ nsDocument::CheckCustomElementName(const ElementCreationOptions& aOptions,
// only check aOptions if 'is' is passed and the webcomponents preference
// is enabled
if (!aOptions.mIs.WasPassed() ||
!Preferences::GetBool("dom.webcomponents.enabled")) {
!CustomElementRegistry::IsCustomElementEnabled()) {
return nullptr;
}

View File

@ -2221,190 +2221,6 @@ Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize,
*out_intSize = std::max<int32_t>(0, intEndInSrc - *out_intStartInSrc);
}
static bool
ZeroTexImageWithClear(WebGLContext* webgl, GLContext* gl, TexImageTarget target,
GLuint tex, uint32_t level, const webgl::FormatUsageInfo* usage,
uint32_t width, uint32_t height)
{
MOZ_ASSERT(gl->IsCurrent());
ScopedFramebuffer scopedFB(gl);
ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB());
const auto format = usage->format;
GLenum attachPoint = 0;
GLbitfield clearBits = 0;
if (format->IsColorFormat()) {
attachPoint = LOCAL_GL_COLOR_ATTACHMENT0;
clearBits = LOCAL_GL_COLOR_BUFFER_BIT;
}
if (format->d) {
attachPoint = LOCAL_GL_DEPTH_ATTACHMENT;
clearBits |= LOCAL_GL_DEPTH_BUFFER_BIT;
}
if (format->s) {
attachPoint = (format->d ? LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
: LOCAL_GL_STENCIL_ATTACHMENT);
clearBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
}
MOZ_RELEASE_ASSERT(attachPoint && clearBits, "GFX: No bits cleared.");
{
gl::GLContext::LocalErrorScope errorScope(*gl);
gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachPoint, target.get(), tex,
level);
if (errorScope.GetError()) {
MOZ_ASSERT(false);
return false;
}
}
auto status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
return false;
{
gl::GLContext::LocalErrorScope errorScope(*gl);
const bool fakeNoAlpha = false;
webgl->ForceClearFramebufferWithDefaultValues(clearBits, fakeNoAlpha);
if (errorScope.GetError()) {
MOZ_ASSERT(false);
return false;
}
}
return true;
}
bool
ZeroTextureData(WebGLContext* webgl, const char* funcName, GLuint tex,
TexImageTarget target, uint32_t level,
const webgl::FormatUsageInfo* usage, uint32_t xOffset, uint32_t yOffset,
uint32_t zOffset, uint32_t width, uint32_t height, uint32_t depth)
{
// This has two usecases:
// 1. Lazy zeroing of uninitialized textures:
// a. Before draw, when FakeBlack isn't viable. (TexStorage + Draw*)
// b. Before partial upload. (TexStorage + TexSubImage)
// 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image)
// We have no sympathy for any of these cases.
// "Doctor, it hurts when I do this!" "Well don't do that!"
webgl->GenerateWarning("%s: This operation requires zeroing texture data. This is"
" slow.",
funcName);
gl::GLContext* gl = webgl->GL();
gl->MakeCurrent();
GLenum scopeBindTarget;
switch (target.get()) {
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
scopeBindTarget = LOCAL_GL_TEXTURE_CUBE_MAP;
break;
default:
scopeBindTarget = target.get();
break;
}
ScopedBindTexture scopeBindTexture(gl, tex, scopeBindTarget);
auto compression = usage->format->compression;
if (compression) {
MOZ_RELEASE_ASSERT(!xOffset && !yOffset && !zOffset, "GFX: Can't zero compressed texture with offsets.");
auto sizedFormat = usage->format->sizedFormat;
MOZ_RELEASE_ASSERT(sizedFormat, "GFX: texture sized format not set");
const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) {
return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock;
};
const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth);
const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight);
CheckedUint32 checkedByteCount = compression->bytesPerBlock;
checkedByteCount *= widthBlocks;
checkedByteCount *= heightBlocks;
checkedByteCount *= depth;
if (!checkedByteCount.isValid())
return false;
const size_t byteCount = checkedByteCount.value();
UniqueBuffer zeros = calloc(1, byteCount);
if (!zeros)
return false;
ScopedUnpackReset scopedReset(webgl);
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it
// well.
GLenum error = DoCompressedTexSubImage(gl, target.get(), level, xOffset, yOffset,
zOffset, width, height, depth, sizedFormat,
byteCount, zeros.get());
if (error)
return false;
return true;
}
const auto driverUnpackInfo = usage->idealUnpack;
MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set.");
if (usage->IsRenderable() && depth == 1 &&
!xOffset && !yOffset && !zOffset)
{
// While we would like to skip the extra complexity of trying to zero with an FB
// clear, ANGLE_depth_texture requires this.
do {
if (ZeroTexImageWithClear(webgl, gl, target, tex, level, usage, width,
height))
{
return true;
}
} while (false);
}
const webgl::PackingInfo packing = driverUnpackInfo->ToPacking();
const auto bytesPerPixel = webgl::BytesPerPixel(packing);
CheckedUint32 checkedByteCount = bytesPerPixel;
checkedByteCount *= width;
checkedByteCount *= height;
checkedByteCount *= depth;
if (!checkedByteCount.isValid())
return false;
const size_t byteCount = checkedByteCount.value();
UniqueBuffer zeros = calloc(1, byteCount);
if (!zeros)
return false;
ScopedUnpackReset scopedReset(webgl);
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well.
const auto error = DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width,
height, depth, packing, zeros.get());
if (error)
return false;
return true;
}
////////////////////////////////////////////////////////////////////////////////
CheckedUint32

View File

@ -2132,12 +2132,6 @@ Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize,
uint32_t* const out_intStartInSrc, uint32_t* const out_intStartInDst,
uint32_t* const out_intSize);
bool
ZeroTextureData(WebGLContext* webgl, const char* funcName, GLuint tex,
TexImageTarget target, uint32_t level,
const webgl::FormatUsageInfo* usage, uint32_t xOffset, uint32_t yOffset,
uint32_t zOffset, uint32_t width, uint32_t height, uint32_t depth);
////
void

View File

@ -583,6 +583,160 @@ WebGLTexture::EnsureImageDataInitialized(const char* funcName, TexImageTarget ta
return InitializeImageData(funcName, target, level);
}
static void
ZeroANGLEDepthTexture(WebGLContext* webgl, GLuint tex,
const webgl::FormatUsageInfo* usage, uint32_t width,
uint32_t height)
{
const auto& format = usage->format;
GLenum attachPoint = 0;
GLbitfield clearBits = 0;
if (format->d) {
attachPoint = LOCAL_GL_DEPTH_ATTACHMENT;
clearBits |= LOCAL_GL_DEPTH_BUFFER_BIT;
}
if (format->s) {
attachPoint = (format->d ? LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
: LOCAL_GL_STENCIL_ATTACHMENT);
clearBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
}
MOZ_RELEASE_ASSERT(attachPoint && clearBits, "GFX: No bits cleared.");
////
const auto& gl = webgl->gl;
MOZ_ASSERT(gl->IsCurrent());
gl::ScopedFramebuffer scopedFB(gl);
const gl::ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB());
gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachPoint, LOCAL_GL_TEXTURE_2D,
tex, 0);
const auto& status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
MOZ_RELEASE_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
////
const bool fakeNoAlpha = false;
webgl->ForceClearFramebufferWithDefaultValues(clearBits, fakeNoAlpha);
}
static bool
ZeroTextureData(WebGLContext* webgl, const char* funcName, GLuint tex,
TexImageTarget target, uint32_t level,
const webgl::FormatUsageInfo* usage, uint32_t width, uint32_t height,
uint32_t depth)
{
// This has two usecases:
// 1. Lazy zeroing of uninitialized textures:
// a. Before draw, when FakeBlack isn't viable. (TexStorage + Draw*)
// b. Before partial upload. (TexStorage + TexSubImage)
// 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image)
// We have no sympathy for any of these cases.
// "Doctor, it hurts when I do this!" "Well don't do that!"
webgl->GenerateWarning("%s: This operation requires zeroing texture data. This is"
" slow.",
funcName);
gl::GLContext* gl = webgl->GL();
gl->MakeCurrent();
GLenum scopeBindTarget;
switch (target.get()) {
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
scopeBindTarget = LOCAL_GL_TEXTURE_CUBE_MAP;
break;
default:
scopeBindTarget = target.get();
break;
}
const gl::ScopedBindTexture scopeBindTexture(gl, tex, scopeBindTarget);
auto compression = usage->format->compression;
if (compression) {
auto sizedFormat = usage->format->sizedFormat;
MOZ_RELEASE_ASSERT(sizedFormat, "GFX: texture sized format not set");
const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) {
return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock;
};
const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth);
const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight);
CheckedUint32 checkedByteCount = compression->bytesPerBlock;
checkedByteCount *= widthBlocks;
checkedByteCount *= heightBlocks;
checkedByteCount *= depth;
if (!checkedByteCount.isValid())
return false;
const size_t byteCount = checkedByteCount.value();
UniqueBuffer zeros = calloc(1, byteCount);
if (!zeros)
return false;
ScopedUnpackReset scopedReset(webgl);
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it
// well.
const auto error = DoCompressedTexSubImage(gl, target.get(), level, 0, 0, 0,
width, height, depth, sizedFormat,
byteCount, zeros.get());
return !error;
}
const auto driverUnpackInfo = usage->idealUnpack;
MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set.");
if (webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
gl->IsANGLE() &&
usage->format->d)
{
// ANGLE_depth_texture does not allow uploads, so we have to clear.
// (Restriction because of D3D9)
MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D);
MOZ_ASSERT(level == 0);
ZeroANGLEDepthTexture(webgl, tex, usage, width, height);
return true;
}
const webgl::PackingInfo packing = driverUnpackInfo->ToPacking();
const auto bytesPerPixel = webgl::BytesPerPixel(packing);
CheckedUint32 checkedByteCount = bytesPerPixel;
checkedByteCount *= width;
checkedByteCount *= height;
checkedByteCount *= depth;
if (!checkedByteCount.isValid())
return false;
const size_t byteCount = checkedByteCount.value();
UniqueBuffer zeros = calloc(1, byteCount);
if (!zeros)
return false;
ScopedUnpackReset scopedReset(webgl);
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well.
const auto error = DoTexSubImage(gl, target, level, 0, 0, 0, width, height, depth,
packing, zeros.get());
return !error;
}
bool
WebGLTexture::InitializeImageData(const char* funcName, TexImageTarget target,
uint32_t level)
@ -596,8 +750,8 @@ WebGLTexture::InitializeImageData(const char* funcName, TexImageTarget target,
const auto& height = imageInfo.mHeight;
const auto& depth = imageInfo.mDepth;
if (!ZeroTextureData(mContext, funcName, mGLName, target, level, usage, 0, 0, 0,
width, height, depth))
if (!ZeroTextureData(mContext, funcName, mGLName, target, level, usage, width, height,
depth))
{
return false;
}

View File

@ -1930,6 +1930,90 @@ WebGLTexture::ValidateCopyTexImageForFeedback(const char* funcName, uint32_t lev
return true;
}
static bool
DoCopyTexOrSubImage(WebGLContext* webgl, const char* funcName, bool isSubImage,
const WebGLTexture* tex, TexImageTarget target, GLint level,
GLint xWithinSrc, GLint yWithinSrc,
uint32_t srcTotalWidth, uint32_t srcTotalHeight,
const webgl::FormatUsageInfo* srcUsage,
GLint xOffset, GLint yOffset, GLint zOffset,
uint32_t dstWidth, uint32_t dstHeight,
const webgl::FormatUsageInfo* dstUsage)
{
gl::GLContext* gl = webgl->gl;
gl->MakeCurrent();
////
uint32_t readX, readY;
uint32_t writeX, writeY;
uint32_t rwWidth, rwHeight;
Intersect(srcTotalWidth, xWithinSrc, dstWidth, &readX, &writeX, &rwWidth);
Intersect(srcTotalHeight, yWithinSrc, dstHeight, &readY, &writeY, &rwHeight);
////
GLenum error = 0;
do {
const auto& idealUnpack = dstUsage->idealUnpack;
if (!isSubImage) {
UniqueBuffer buffer;
if (rwWidth != dstWidth || rwHeight != dstHeight) {
const auto& pi = idealUnpack->ToPacking();
CheckedUint32 byteCount = BytesPerPixel(pi);
byteCount *= dstWidth;
byteCount *= dstHeight;
if (byteCount.isValid()) {
buffer = calloc(1, byteCount.value());
}
if (!buffer.get()) {
webgl->ErrorOutOfMemory("%s: Ran out of memory allocating zeros.",
funcName);
return false;
}
}
const ScopedUnpackReset unpackReset(webgl);
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);
error = DoTexImage(gl, target, level, idealUnpack, dstWidth, dstHeight, 1,
buffer.get());
if (error)
break;
}
if (!rwWidth || !rwHeight) {
// There aren't any pixels to copy, so we're 'done'.
return true;
}
const auto& srcFormat = srcUsage->format;
ScopedCopyTexImageSource maybeSwizzle(webgl, funcName, srcTotalWidth,
srcTotalHeight, srcFormat, dstUsage);
const uint8_t zOffset = 0;
error = DoCopyTexSubImage(gl, target, level, writeX, writeY, zOffset, readX,
readY, rwWidth, rwHeight);
if (error)
break;
return true;
} while (false);
if (error == LOCAL_GL_OUT_OF_MEMORY) {
webgl->ErrorOutOfMemory("%s: Ran out of memory during texture copy.", funcName);
return false;
}
MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
webgl->GenerateWarning("%s: Unexpected error during texture copy. Context lost.",
funcName);
webgl->ForceLoseContext();
return false;
}
// There is no CopyTexImage3D.
void
WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internalFormat,
@ -1960,11 +2044,13 @@ WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internal
// Get source info
const webgl::FormatUsageInfo* srcUsage;
uint32_t srcWidth;
uint32_t srcHeight;
if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcWidth, &srcHeight))
uint32_t srcTotalWidth;
uint32_t srcTotalHeight;
if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcTotalWidth,
&srcTotalHeight))
{
return;
auto srcFormat = srcUsage->format;
}
if (!ValidateCopyTexImageForFeedback(funcName, level))
return;
@ -1972,13 +2058,13 @@ WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internal
////////////////////////////////////
// Check that source and dest info are compatible
const auto& srcFormat = srcUsage->format;
const auto dstUsage = ValidateCopyDestUsage(funcName, mContext, srcFormat,
internalFormat);
if (!dstUsage)
return;
const auto dstFormat = dstUsage->format;
const auto& dstFormat = dstUsage->format;
if (!ValidateTargetForFormat(funcName, mContext, target, dstFormat))
return;
@ -1994,63 +2080,11 @@ WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internal
////////////////////////////////////
// Do the thing!
gl::GLContext* gl = mContext->gl;
gl->MakeCurrent();
ScopedCopyTexImageSource maybeSwizzle(mContext, funcName, srcWidth, srcHeight,
srcFormat, dstUsage);
uint32_t readX, readY;
uint32_t writeX, writeY;
uint32_t rwWidth, rwHeight;
Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth);
Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight);
const auto& idealUnpack = dstUsage->idealUnpack;
const auto& driverInternalFormat = idealUnpack->internalFormat;
GLenum error = DoCopyTexImage2D(gl, target, level, driverInternalFormat, x, y, width,
height);
do {
if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height))
break;
if (error)
break;
// 1. Zero the texture data.
// 2. CopyTexSubImage the subrect.
const uint8_t zOffset = 0;
if (!ZeroTextureData(mContext, funcName, mGLName, target, level, dstUsage, 0, 0,
zOffset, width, height, depth))
{
mContext->ErrorOutOfMemory("%s: Failed to zero texture data.", funcName);
MOZ_ASSERT(false, "Failed to zero texture data.");
return;
}
if (!rwWidth || !rwHeight) {
// There aren't any, so we're 'done'.
mContext->DummyReadFramebufferOperation(funcName);
return;
}
error = DoCopyTexSubImage(gl, target, level, writeX, writeY, zOffset, readX,
readY, rwWidth, rwHeight);
} while (false);
if (error == LOCAL_GL_OUT_OF_MEMORY) {
mContext->ErrorOutOfMemory("%s: Ran out of memory during texture copy.",
funcName);
return;
}
if (error) {
MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
mContext->GenerateWarning("%s: Unexpected error during texture copy. Context"
" lost.",
funcName);
mContext->ForceLoseContext();
const bool isSubImage = false;
if (!DoCopyTexOrSubImage(mContext, funcName, isSubImage, this, target, level, x, y,
srcTotalWidth, srcTotalHeight, srcUsage, 0, 0, 0, width,
height, dstUsage))
{
return;
}
@ -2087,8 +2121,8 @@ WebGLTexture::CopyTexSubImage(const char* funcName, TexImageTarget target, GLint
auto dstUsage = imageInfo->mFormat;
MOZ_ASSERT(dstUsage);
auto dstFormat = dstUsage->format;
auto dstFormat = dstUsage->format;
if (!mContext->IsWebGL2() && dstFormat->d) {
mContext->ErrorInvalidOperation("%s: Function may not be called on a texture of"
" format %s.",
@ -2100,11 +2134,13 @@ WebGLTexture::CopyTexSubImage(const char* funcName, TexImageTarget target, GLint
// Get source info
const webgl::FormatUsageInfo* srcUsage;
uint32_t srcWidth;
uint32_t srcHeight;
if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcWidth, &srcHeight))
uint32_t srcTotalWidth;
uint32_t srcTotalHeight;
if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcTotalWidth,
&srcTotalHeight))
{
return;
auto srcFormat = srcUsage->format;
}
if (!ValidateCopyTexImageForFeedback(funcName, level, zOffset))
return;
@ -2112,29 +2148,13 @@ WebGLTexture::CopyTexSubImage(const char* funcName, TexImageTarget target, GLint
////////////////////////////////////
// Check that source and dest info are compatible
auto srcFormat = srcUsage->format;
if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
return;
////////////////////////////////////
// Do the thing!
mContext->gl->MakeCurrent();
ScopedCopyTexImageSource maybeSwizzle(mContext, funcName, srcWidth, srcHeight,
srcFormat, dstUsage);
uint32_t readX, readY;
uint32_t writeX, writeY;
uint32_t rwWidth, rwHeight;
Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth);
Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight);
if (!rwWidth || !rwHeight) {
// There aren't any, so we're 'done'.
mContext->DummyReadFramebufferOperation(funcName);
return;
}
bool uploadWillInitialize;
if (!EnsureImageDataInitializedForUpload(this, funcName, target, level, xOffset,
yOffset, zOffset, width, height, depth,
@ -2143,21 +2163,11 @@ WebGLTexture::CopyTexSubImage(const char* funcName, TexImageTarget target, GLint
return;
}
GLenum error = DoCopyTexSubImage(mContext->gl, target, level, xOffset + writeX,
yOffset + writeY, zOffset, readX, readY, rwWidth,
rwHeight);
if (error == LOCAL_GL_OUT_OF_MEMORY) {
mContext->ErrorOutOfMemory("%s: Ran out of memory during texture copy.",
funcName);
return;
}
if (error) {
MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
mContext->GenerateWarning("%s: Unexpected error during texture copy. Context"
" lost.",
funcName);
mContext->ForceLoseContext();
const bool isSubImage = true;
if (!DoCopyTexOrSubImage(mContext, funcName, isSubImage, this, target, level, x, y,
srcTotalWidth, srcTotalHeight, srcUsage, xOffset, yOffset,
zOffset, width, height, dstUsage))
{
return;
}

View File

@ -251,25 +251,27 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML),
"Trying to HTML elements that don't have the XHTML namespace");
int32_t tag = parserService->HTMLCaseSensitiveAtomTagToId(name);
// Per the Custom Element specification, unknown tags that are valid custom
// element names should be HTMLElement instead of HTMLUnknownElement.
int32_t tag = parserService->HTMLCaseSensitiveAtomTagToId(name);
if ((tag == eHTMLTag_userdefined &&
nsContentUtils::IsCustomElementName(name)) ||
aIs) {
bool isCustomElementName = (tag == eHTMLTag_userdefined &&
nsContentUtils::IsCustomElementName(name));
if (isCustomElementName) {
NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
if (!*aResult) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsContentUtils::SetupCustomElement(*aResult, aIs);
return NS_OK;
} else {
*aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
}
*aResult = CreateHTMLElement(tag,
nodeInfo.forget(), aFromParser).take();
return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
if (!*aResult) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (isCustomElementName || aIs) {
nsContentUtils::SetupCustomElement(*aResult, aIs);
}
return NS_OK;
}
already_AddRefed<nsGenericHTMLElement>

View File

@ -101,6 +101,7 @@ function startTest() {
is(extendedButton.tagName, "BUTTON", "Created element should have local name of BUTTON");
is(extendedButton.__proto__, extendedProto, "Created element should have the prototype of the extended type.");
is(extendedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
is(extendedButton.type, "submit", "Created element should be a button with type of \"submit\"");
// document.createElementNS with different namespace than definition.
try {

View File

@ -58,7 +58,6 @@
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/ServoStyleSet.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -420,14 +419,6 @@ nsXBLBinding::GenerateAnonymousContent()
if (mContent)
mContent->UnsetAttr(namespaceID, name, false);
}
// Now that we've finished shuffling the tree around, go ahead and restyle it
// since frame construction is about to happen.
nsIPresShell* presShell = mBoundElement->OwnerDoc()->GetShell();
ServoStyleSet* servoSet = presShell->StyleSet()->GetAsServo();
if (servoSet) {
servoSet->StyleNewChildren(mBoundElement->AsElement());
}
}
nsIURI*

View File

@ -53,6 +53,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/Preferences.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/Element.h"
@ -406,6 +407,24 @@ nsXBLService::IsChromeOrResourceURI(nsIURI* aURI)
return false;
}
// RAII class to invoke StyleNewChildren for Elements in Servo-backed documents
// on destruction.
class MOZ_STACK_CLASS AutoStyleNewChildren
{
public:
explicit AutoStyleNewChildren(Element* aElement) : mElement(aElement) { MOZ_ASSERT(mElement); }
~AutoStyleNewChildren()
{
nsIPresShell* presShell = mElement->OwnerDoc()->GetShell();
ServoStyleSet* servoSet = presShell ? presShell->StyleSet()->GetAsServo() : nullptr;
if (servoSet) {
servoSet->StyleNewChildren(mElement);
}
}
private:
Element* mElement;
};
// This function loads a particular XBL file and installs all of the bindings
// onto the element.
@ -436,6 +455,15 @@ nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL,
return NS_OK;
}
// There are various places in this function where we shuffle content around
// the subtree and rebind things to and from insertion points. Once all that's
// done, we want to invoke StyleNewChildren to style any unstyled children
// that we may have after bindings have been removed and applied. This includes
// anonymous content created in this function, explicit children for which we
// defer styling until after XBL bindings are applied, and elements whose existing
// style was invalidated by a call to SetXBLInsertionParent.
AutoStyleNewChildren styleNewChildren(aContent->AsElement());
nsXBLBinding *binding = aContent->GetXBLBinding();
if (binding) {
if (binding->MarkedForDeath()) {

View File

@ -83,7 +83,7 @@
#define MAXSUGGESTION 15
#define MAXSHARPS 5
#define MAXWORDLEN 176
#define MAXWORDLEN 100
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
# define H_DEPRECATED __attribute__((__deprecated__))

View File

@ -0,0 +1,24 @@
Bug 1322666 - Change MAXWORDLEN to 100
diff --git a/extensions/spellcheck/hunspell/src/hunspell.hxx b/extensions/spellcheck/hunspell/src/hunspell.hxx
--- a/extensions/spellcheck/hunspell/src/hunspell.hxx
+++ b/extensions/spellcheck/hunspell/src/hunspell.hxx
@@ -78,17 +78,17 @@
#include "atypes.hxx"
#include <string>
#include <vector>
#define SPELL_XML "<?xml?>"
#define MAXSUGGESTION 15
#define MAXSHARPS 5
-#define MAXWORDLEN 176
+#define MAXWORDLEN 100
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
# define H_DEPRECATED __attribute__((__deprecated__))
#elif defined(_MSC_VER) && (_MSC_VER >= 1300)
# define H_DEPRECATED __declspec(deprecated)
#else
# define H_DEPRECATED
#endif

View File

@ -4464,13 +4464,6 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor fla
return true;
}
bool
BytecodeEmitter::emitDestructuringLHSInBranch(ParseNode* target, DestructuringFlavor flav)
{
TDZCheckCache tdzCache(this);
return emitDestructuringLHS(target, flav);
}
bool
BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted)
{
@ -4741,7 +4734,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
//
// let x, y;
// let a, b, c, d;
// let tmp, done, iter, result; // stack values
// let iter, result, done, value; // stack values
//
// iter = x[Symbol.iterator]();
//
@ -4749,115 +4742,113 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
// result = iter.next();
// done = result.done;
//
// if (done) {
// a = undefined;
// if (done)
// value = undefined;
// else
// value = result.value;
//
// result = undefined;
// done = true;
// } else {
// a = result.value;
//
// // Do next element's .next() and .done access here
// result = iter.next();
// done = result.done;
// }
// a = value;
//
// // ==== emitted by loop for b ====
// if (done) {
// b = undefined;
//
// result = undefined;
// done = true;
// value = undefined;
// } else {
// b = result.value;
//
// result = iter.next();
// done = result.done;
// if (done)
// value = undefined;
// else
// value = result.value;
// }
//
// b = value;
//
// // ==== emitted by loop for elision ====
// if (done) {
// result = undefined
// done = true
// value = undefined;
// } else {
// result.value;
//
// result = iter.next();
// done = result.done;
// if (done)
// value = undefined;
// else
// value = result.value;
// }
//
// // ==== emitted by loop for c ====
// if (done) {
// c = y;
// value = undefined;
// } else {
// tmp = result.value;
// if (tmp === undefined)
// tmp = y;
// c = tmp;
//
// // Don't do next element's .next() and .done access if
// // this is the last non-spread element.
// result = iter.next();
// done = result.done;
// if (done)
// value = undefined;
// else
// value = result.value;
// }
//
// if (value === undefined)
// value = y;
//
// c = value;
//
// // ==== emitted by loop for d ====
// if (done) {
// // Assing empty array when completed
// d = [];
// } else {
// d = [...iter];
// }
// if (done)
// value = [];
// else
// value = [...iter];
//
// d = value;
/*
* Use an iterator to destructure the RHS, instead of index lookup. We
* must leave the *original* value on the stack.
*/
// Use an iterator to destructure the RHS, instead of index lookup. We
// must leave the *original* value on the stack.
if (!emit1(JSOP_DUP)) // ... OBJ OBJ
return false;
if (!emitIterator()) // ... OBJ? ITER
if (!emitIterator()) // ... OBJ ITER
return false;
bool needToPopIterator = true;
for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
bool isHead = member == pattern->pn_head;
bool hasNext = !!member->pn_next;
if (member->isKind(PNK_SPREAD)) {
IfThenElseEmitter ifThenElse(this);
if (!isHead) {
// If spread is not the first element of the pattern,
// iterator can already be completed.
if (!ifThenElse.emitIfElse()) // ... OBJ? ITER
// ... OBJ ITER DONE
if (!ifThenElse.emitIfElse()) // ... OBJ ITER
return false;
if (!emit1(JSOP_POP)) // ... OBJ?
if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER ARRAY
return false;
if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ? ARRAY
return false;
if (!emitDestructuringLHSInBranch(member, flav)) // ... OBJ?
return false;
if (!ifThenElse.emitElse()) // ... OBJ? ITER
if (!ifThenElse.emitElse()) // ... OBJ ITER
return false;
}
// If iterator is not completed, create a new array with the rest
// of the iterator.
if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ? ITER ARRAY
if (!emit1(JSOP_DUP)) // ... OBJ ITER
return false;
if (!emitNumberOp(0)) // ... OBJ? ITER ARRAY INDEX
if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER ITER ARRAY
return false;
if (!emitSpread()) // ... OBJ? ARRAY INDEX
if (!emitNumberOp(0)) // ... OBJ ITER ITER ARRAY INDEX
return false;
if (!emit1(JSOP_POP)) // ... OBJ? ARRAY
if (!emitSpread()) // ... OBJ ITER ARRAY INDEX
return false;
if (!emitDestructuringLHSInBranch(member, flav)) // ... OBJ?
if (!emit1(JSOP_POP)) // ... OBJ ITER ARRAY
return false;
if (!isHead) {
if (!ifThenElse.emitEnd())
return false;
MOZ_ASSERT(ifThenElse.popped() == 1);
MOZ_ASSERT(ifThenElse.pushed() == 1);
}
needToPopIterator = false;
MOZ_ASSERT(!member->pn_next);
if (!emitDestructuringLHS(member, flav)) // ... OBJ ITER
return false;
MOZ_ASSERT(!hasNext);
break;
}
@ -4869,110 +4860,100 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
}
bool isElision = subpattern->isKind(PNK_ELISION);
bool hasNextNonSpread = member->pn_next && !member->pn_next->isKind(PNK_SPREAD);
bool hasNextSpread = member->pn_next && member->pn_next->isKind(PNK_SPREAD);
MOZ_ASSERT(!subpattern->isKind(PNK_SPREAD));
auto emitNext = [pattern](ExclusiveContext* cx, BytecodeEmitter* bce) {
if (!bce->emit1(JSOP_DUP)) // ... OBJ? ITER ITER
return false;
if (!bce->emitIteratorNext(pattern)) // ... OBJ? ITER RESULT
return false;
if (!bce->emit1(JSOP_DUP)) // ... OBJ? ITER RESULT RESULT
return false;
if (!bce->emitAtomOp(cx->names().done, JSOP_GETPROP)) // ... OBJ? ITER RESULT DONE?
return false;
return true;
};
if (isHead) {
if (!emitNext(cx, this)) // ... OBJ? ITER RESULT DONE?
return false;
}
IfThenElseEmitter ifThenElse(this);
if (!ifThenElse.emitIfElse()) // ... OBJ? ITER RESULT
return false;
if (!emit1(JSOP_POP)) // ... OBJ? ITER
return false;
if (pndefault) {
// Emit only pndefault tree here, as undefined check in emitDefault
// should always be true.
if (!emitInitializerInBranch(pndefault, subpattern)) // ... OBJ? ITER VALUE
return false;
} else {
if (!isElision) {
if (!emit1(JSOP_UNDEFINED)) // ... OBJ? ITER UNDEFINED
IfThenElseEmitter ifAlreadyDone(this);
if (!isHead) {
// If this element is not the first element of the pattern,
// iterator can already be completed.
// ... OBJ ITER DONE
if (hasNext) {
if (!emit1(JSOP_DUP)) // ... OBJ ITER DONE DONE
return false;
if (!emit1(JSOP_NOP_DESTRUCTURING))
}
if (!ifAlreadyDone.emitIfElse()) // ... OBJ ITER ?DONE
return false;
if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER ?DONE UNDEF
return false;
if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ ITER ?DONE UNDEF
return false;
if (!ifAlreadyDone.emitElse()) // ... OBJ ITER ?DONE
return false;
if (hasNext) {
if (!emit1(JSOP_POP)) // ... OBJ ITER
return false;
}
}
if (!isElision) {
if (!emitDestructuringLHSInBranch(subpattern, flav)) // ... OBJ? ITER
return false;
} else if (pndefault) {
if (!emit1(JSOP_POP)) // ... OBJ? ITER
if (!emit1(JSOP_DUP)) // ... OBJ ITER ITER
return false;
if (!emitIteratorNext(pattern)) // ... OBJ ITER RESULT
return false;
if (!emit1(JSOP_DUP)) // ... OBJ ITER RESULT RESULT
return false;
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ... OBJ ITER RESULT DONE
return false;
if (hasNext) {
if (!emit1(JSOP_DUP)) // ... OBJ ITER RESULT DONE DONE
return false;
}
// Setup next element's result when the iterator is done.
if (hasNextNonSpread) {
if (!emit1(JSOP_UNDEFINED)) // ... OBJ? ITER RESULT
return false;
if (!emit1(JSOP_NOP_DESTRUCTURING))
return false;
if (!emit1(JSOP_TRUE)) // ... OBJ? ITER RESULT DONE?
return false;
} else if (hasNextSpread) {
if (!emit1(JSOP_TRUE)) // ... OBJ? ITER DONE?
IfThenElseEmitter ifDone(this);
if (!ifDone.emitIfElse()) // ... OBJ ITER RESULT ?DONE
return false;
if (hasNext) {
if (!emit1(JSOP_SWAP)) // ... OBJ ITER ?DONE RESULT
return false;
}
if (!ifThenElse.emitElse()) // ... OBJ? ITER RESULT
if (!emit1(JSOP_POP)) // ... OBJ ITER ?DONE
return false;
if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER ?DONE UNDEF
return false;
if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ ITER ?DONE UNDEF
return false;
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... OBJ? ITER VALUE
if (!ifDone.emitElse()) // ... OBJ ITER RESULT ?DONE
return false;
if (hasNext) {
if (!emit1(JSOP_SWAP)) // ... OBJ ITER ?DONE RESULT
return false;
}
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... OBJ ITER ?DONE VALUE
return false;
if (!ifDone.emitEnd())
return false;
MOZ_ASSERT(ifDone.pushed() == 0);
if (!isHead) {
if (!ifAlreadyDone.emitEnd())
return false;
MOZ_ASSERT(ifAlreadyDone.pushed() == 1);
}
if (pndefault) {
if (!emitDefault(pndefault, subpattern)) // ... OBJ? ITER VALUE
if (!emitDefault(pndefault, subpattern)) // ... OBJ ITER ?DONE VALUE
return false;
}
if (!isElision) {
if (!emitDestructuringLHSInBranch(subpattern, flav)) // ... OBJ? ITER
if (!emitDestructuringLHS(subpattern, flav)) // ... OBJ ITER ?DONE
return false;
} else {
if (!emit1(JSOP_POP)) // ... OBJ? ITER
if (!emit1(JSOP_POP)) // ... OBJ ITER ?DONE
return false;
}
// Setup next element's result when the iterator is not done.
if (hasNextNonSpread) {
if (!emitNext(cx, this)) // ... OBJ? ITER RESULT DONE?
return false;
} else if (hasNextSpread) {
if (!emit1(JSOP_FALSE)) // ... OBJ? ITER DONE?
return false;
}
if (!ifThenElse.emitEnd())
return false;
if (hasNextNonSpread)
MOZ_ASSERT(ifThenElse.pushed() == 1);
else if (hasNextSpread)
MOZ_ASSERT(ifThenElse.pushed() == 0);
else
MOZ_ASSERT(ifThenElse.popped() == 1);
}
if (needToPopIterator) {
if (!emit1(JSOP_POP)) // ... OBJ?
return false;
}
if (!emit1(JSOP_POP)) // ... OBJ
return false;
return true;
}
@ -6120,7 +6101,7 @@ BytecodeEmitter::emitSpread(bool allowSelfHosted)
return false;
if (!emit1(JSOP_DUP)) // ITER ARR I RESULT RESULT
return false;
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER ARR I RESULT DONE?
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER ARR I RESULT DONE
return false;
if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // ITER ARR I RESULT
@ -6309,7 +6290,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
return false;
if (!emit1(JSOP_DUP)) // ITER RESULT RESULT
return false;
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE?
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE
return false;
if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
@ -6805,7 +6786,7 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
return false;
if (!emit1(JSOP_DUP)) // ITER RESULT RESULT
return false;
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE?
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE
return false;
JumpList beq;
@ -6977,14 +6958,13 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
RootedFunction fun(cx, funbox->function());
RootedAtom name(cx, fun->explicitName());
MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
MOZ_ASSERT_IF(pn->isOp(JSOP_FUNWITHPROTO), needsProto);
/*
* Set the |wasEmitted| flag in the funbox once the function has been
* emitted. Function definitions that need hoisting to the top of the
* function will be seen by emitFunction in two places.
*/
if (funbox->wasEmitted && pn->functionIsHoisted()) {
if (funbox->wasEmitted) {
// Annex B block-scoped functions are hoisted like any other
// block-scoped function to the top of their scope. When their
// definitions are seen for the second time, we need to emit the
@ -7113,7 +7093,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
}
if (needsProto) {
MOZ_ASSERT(pn->getOp() == JSOP_FUNWITHPROTO || pn->getOp() == JSOP_LAMBDA);
MOZ_ASSERT(pn->getOp() == JSOP_LAMBDA);
pn->setOp(JSOP_FUNWITHPROTO);
}
@ -10049,15 +10029,6 @@ CGConstList::finish(ConstArray* array)
array->vector[i] = list[i];
}
bool
CGObjectList::isAdded(ObjectBox* objbox)
{
// An objbox added to CGObjectList as non-first element has non-null
// emitLink member. The first element has null emitLink.
// Check for firstbox to cover the first element.
return objbox->emitLink || objbox == firstbox;
}
/*
* Find the index of the given object for code generator.
*
@ -10069,15 +10040,9 @@ CGObjectList::isAdded(ObjectBox* objbox)
unsigned
CGObjectList::add(ObjectBox* objbox)
{
if (isAdded(objbox))
return indexOf(objbox->object);
MOZ_ASSERT(!objbox->emitLink);
objbox->emitLink = lastbox;
lastbox = objbox;
// See the comment in CGObjectList::isAdded.
if (!firstbox)
firstbox = objbox;
return length++;
}
@ -10104,12 +10069,7 @@ CGObjectList::finish(ObjectArray* array)
MOZ_ASSERT(!*cursor);
MOZ_ASSERT(objbox->object->isTenured());
*cursor = objbox->object;
ObjectBox* tmp = objbox->emitLink;
// Clear emitLink for CGObjectList::isAdded.
objbox->emitLink = nullptr;
objbox = tmp;
} while (objbox != nullptr);
} while ((objbox = objbox->emitLink) != nullptr);
MOZ_ASSERT(cursor == array->vector);
}

View File

@ -43,12 +43,10 @@ class CGConstList {
struct CGObjectList {
uint32_t length; /* number of emitted so far objects */
ObjectBox* firstbox; /* first emitted object */
ObjectBox* lastbox; /* last emitted object */
CGObjectList() : length(0), firstbox(nullptr), lastbox(nullptr) {}
CGObjectList() : length(0), lastbox(nullptr) {}
bool isAdded(ObjectBox* objbox);
unsigned add(ObjectBox* objbox);
unsigned indexOf(JSObject* obj);
void finish(ObjectArray* array);
@ -648,7 +646,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter
// the stack and emits code to destructure a single lhs expression (either a
// name or a compound []/{} expression).
MOZ_MUST_USE bool emitDestructuringLHS(ParseNode* target, DestructuringFlavor flav);
MOZ_MUST_USE bool emitDestructuringLHSInBranch(ParseNode* target, DestructuringFlavor flav);
// emitDestructuringOps assumes the to-be-destructured value has been
// pushed on the stack and emits code to destructure each part of a [] or

View File

@ -649,14 +649,12 @@ class ParseNode
MOZ_ASSERT(pn_arity == PN_CODE && getKind() == PNK_FUNCTION);
MOZ_ASSERT(isOp(JSOP_LAMBDA) || // lambda, genexpr
isOp(JSOP_LAMBDA_ARROW) || // arrow function
isOp(JSOP_FUNWITHPROTO) || // already emitted lambda with needsProto
isOp(JSOP_DEFFUN) || // non-body-level function statement
isOp(JSOP_NOP) || // body-level function stmt in global code
isOp(JSOP_GETLOCAL) || // body-level function stmt in function code
isOp(JSOP_GETARG) || // body-level function redeclaring formal
isOp(JSOP_INITLEXICAL)); // block-level function stmt
return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) &&
!isOp(JSOP_FUNWITHPROTO) && !isOp(JSOP_DEFFUN);
return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN);
}
/*

View File

@ -1917,43 +1917,63 @@ CacheIRWriter::copyStubData(uint8_t* dest) const
{
uintptr_t* destWords = reinterpret_cast<uintptr_t*>(dest);
for (size_t i = 0; i < stubFields_.length(); i++) {
StubField::Type type = stubFields_[i].type();
switch (type) {
for (const StubField& field : stubFields_) {
switch (field.type()) {
case StubField::Type::RawWord:
*destWords = stubFields_[i].asWord();
*destWords = field.asWord();
break;
case StubField::Type::Shape:
InitGCPtr<Shape*>(destWords, stubFields_[i].asWord());
InitGCPtr<Shape*>(destWords, field.asWord());
break;
case StubField::Type::JSObject:
InitGCPtr<JSObject*>(destWords, stubFields_[i].asWord());
InitGCPtr<JSObject*>(destWords, field.asWord());
break;
case StubField::Type::ObjectGroup:
InitGCPtr<ObjectGroup*>(destWords, stubFields_[i].asWord());
InitGCPtr<ObjectGroup*>(destWords, field.asWord());
break;
case StubField::Type::Symbol:
InitGCPtr<JS::Symbol*>(destWords, stubFields_[i].asWord());
InitGCPtr<JS::Symbol*>(destWords, field.asWord());
break;
case StubField::Type::String:
InitGCPtr<JSString*>(destWords, stubFields_[i].asWord());
InitGCPtr<JSString*>(destWords, field.asWord());
break;
case StubField::Type::Id:
InitGCPtr<jsid>(destWords, stubFields_[i].asWord());
InitGCPtr<jsid>(destWords, field.asWord());
break;
case StubField::Type::RawInt64:
*reinterpret_cast<uint64_t*>(destWords) = stubFields_[i].asInt64();
*reinterpret_cast<uint64_t*>(destWords) = field.asInt64();
break;
case StubField::Type::Value:
InitGCPtr<JS::Value>(destWords, stubFields_[i].asInt64());
InitGCPtr<JS::Value>(destWords, field.asInt64());
break;
case StubField::Type::Limit:
MOZ_CRASH("Invalid type");
}
destWords += StubField::sizeInBytes(type) / sizeof(uintptr_t);
destWords += StubField::sizeInBytes(field.type()) / sizeof(uintptr_t);
}
}
bool
CacheIRWriter::stubDataEquals(const uint8_t* stubData) const
{
const uintptr_t* stubDataWords = reinterpret_cast<const uintptr_t*>(stubData);
for (const StubField& field : stubFields_) {
if (field.sizeIsWord()) {
if (field.asWord() != *stubDataWords)
return false;
stubDataWords++;
continue;
}
if (field.asInt64() != *reinterpret_cast<const uint64_t*>(stubDataWords))
return false;
stubDataWords += sizeof(uint64_t) / sizeof(uintptr_t);
}
return true;
}
HashNumber
CacheIRStubKey::hash(const CacheIRStubKey::Lookup& l)
{
@ -2065,28 +2085,46 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
return nullptr;
}
// We got our shared stub code and stub info. Time to allocate and attach a
// new stub.
MOZ_ASSERT(code);
MOZ_ASSERT(stubInfo);
MOZ_ASSERT(stub->isMonitoredFallback());
MOZ_ASSERT(stubInfo->stubDataSize() == writer.stubDataSize());
// Ensure we don't attach duplicate stubs. This can happen if a stub failed
// for some reason and the IR generator doesn't check for exactly the same
// conditions.
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
if (!iter->isCacheIR_Monitored())
continue;
ICCacheIR_Monitored* otherStub = iter->toCacheIR_Monitored();
if (otherStub->stubInfo() != stubInfo)
continue;
if (!writer.stubDataEquals(otherStub->stubDataStart()))
continue;
// We found a stub that's exactly the same as the stub we're about to
// attach. Just return nullptr, the caller should do nothing in this
// case.
return nullptr;
}
// Time to allocate and attach a new stub.
size_t bytesNeeded = stubInfo->stubDataOffset() + stubInfo->stubDataSize();
ICStubSpace* stubSpace = ICStubCompiler::StubSpaceForStub(stubInfo->makesGCCalls(),
outerScript, engine);
void* newStub = stubSpace->alloc(bytesNeeded);
if (!newStub)
void* newStubMem = stubSpace->alloc(bytesNeeded);
if (!newStubMem)
return nullptr;
ICStub* monitorStub = stub->toMonitoredFallbackStub()->fallbackMonitorStub()->firstMonitorStub();
new(newStub) ICCacheIR_Monitored(code, monitorStub, stubInfo);
auto newStub = new(newStubMem) ICCacheIR_Monitored(code, monitorStub, stubInfo);
writer.copyStubData((uint8_t*)newStub + stubInfo->stubDataOffset());
stub->addNewStub((ICStub*)newStub);
return (ICStub*)newStub;
writer.copyStubData(newStub->stubDataStart());
stub->addNewStub(newStub);
return newStub;
}
void
@ -2196,6 +2234,11 @@ CacheIRStubInfo::copyStubData(ICStub* src, ICStub* dest) const
}
}
uint8_t*
ICCacheIR_Monitored::stubDataStart()
{
return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
}
/* static */ ICCacheIR_Monitored*
ICCacheIR_Monitored::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,

View File

@ -897,7 +897,7 @@ GetPropIRGenerator::tryAttachDenseElement(HandleObject obj, ObjOperandId objId,
if (!obj->isNative())
return false;
if (uint32_t(idVal_.toInt32()) >= obj->as<NativeObject>().getDenseInitializedLength())
if (!obj->as<NativeObject>().containsDenseElement(uint32_t(idVal_.toInt32())))
return false;
writer.guardShape(objId, obj->as<NativeObject>().lastProperty());

View File

@ -229,9 +229,6 @@ class StubField
};
Type type_;
bool sizeIsWord() const { return sizeIsWord(type_); }
bool sizeIsInt64() const { return sizeIsInt64(type_); }
public:
StubField(uint64_t data, Type type)
: dataInt64_(data), type_(type)
@ -241,6 +238,9 @@ class StubField
Type type() const { return type_; }
bool sizeIsWord() const { return sizeIsWord(type_); }
bool sizeIsInt64() const { return sizeIsInt64(type_); }
uintptr_t asWord() const { MOZ_ASSERT(sizeIsWord()); return dataWord_; }
uint64_t asInt64() const { MOZ_ASSERT(sizeIsInt64()); return dataInt64_; }
} JS_HAZ_GC_POINTER;
@ -357,6 +357,7 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
return stubDataSize_;
}
void copyStubData(uint8_t* dest) const;
bool stubDataEquals(const uint8_t* stubData) const;
bool operandIsDead(uint32_t operandId, uint32_t currentInstruction) const {
if (operandId >= operandLastUsed_.length())

View File

@ -2273,6 +2273,8 @@ IonCompile(JSContext* cx, JSScript* script,
return reason;
}
AssertBasicGraphCoherency(builder->graph());
// If possible, compile the script off thread.
if (options.offThreadCompilationAvailable()) {
JitSpew(JitSpew_IonSyncLogs, "Can't log script %s:%" PRIuSIZE

View File

@ -192,9 +192,11 @@ MIRGraph::removeSuccessorBlocks(MBasicBlock* start)
removeBlock(block);
} else {
MOZ_ASSERT(block != osrBlock());
for (size_t j = 0; j < block->numPredecessors(); j++) {
if (!block->getPredecessor(j)->isMarked())
for (size_t j = 0; j < block->numPredecessors(); ) {
if (!block->getPredecessor(j)->isMarked()) {
j++;
continue;
}
block->removePredecessor(block->getPredecessor(j));
}
// This shouldn't have any instructions yet.

View File

@ -887,6 +887,8 @@ class ICCacheIR_Monitored : public ICMonitoredStub
const CacheIRStubInfo* stubInfo() const {
return stubInfo_;
}
uint8_t* stubDataStart();
};
// Updated stubs are IC stubs that use a TypeUpdate IC to track

View File

@ -0,0 +1,25 @@
var BUGNUMBER = 1322314;
var summary = "Function in computed property in class expression in array destructuring default";
print(BUGNUMBER + ": " + summary);
function* g([
a = class E {
[ (function() { return "foo"; })() ]() {
return 10;
}
}
]) {
yield a;
}
let C = [...g([])][0];
let x = new C();
assertEq(x.foo(), 10);
C = [...g([undefined])][0];
x = new C();
assertEq(x.foo(), 10);
if (typeof reportCompare === "function")
reportCompare(0, 0);

View File

@ -4,7 +4,7 @@
<div style="width: 1000px; height: 1000px"></div>
<script type="application/javascript;version=1.8">
function grabEventAndGo(event) {
gen.send(event);
gen.next(event);
}
function waitAsync() {
@ -17,7 +17,7 @@ function postPos() {
"*");
}
function runTest() {
function* runTest() {
var inner = document.getElementById("inner");
window.onload = grabEventAndGo;
// Wait for onLoad event.
@ -55,9 +55,6 @@ function runTest() {
postPos();
parent.postMessage("done", "*");
// Let parent process sent messages.
// "End" generator.
yield;
}
var gen = runTest();

View File

@ -18,10 +18,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=583889
SimpleTest.waitForExplicitFinish();
function grabEventAndGo(event) {
gen.send(event);
gen.next(event);
}
function runTest() {
function* runTest() {
window.onload = grabEventAndGo;
// Wait for onLoad event.
yield;
@ -43,8 +43,6 @@ function runTest() {
// finish(), yet let the test actually end first, to be safe.
SimpleTest.executeSoon(SimpleTest.finish);
// "End" generator.
yield;
}
var gen = runTest();

View File

@ -16,14 +16,13 @@ function olo()
function neext()
{
try {
iter.next();
} catch (e if e instanceof StopIteration) {
let {done} = iter.next();
if (done) {
clearInterval(interv);
}
}
function foo()
function* foo()
{
var docElem = document.documentElement;
var head = document.getElementById("head");

View File

@ -83,7 +83,7 @@ function waitInterrupt(result, gen) {
function testPhoom(isCapturing, x, y, expectEvent) {
var eventGen = (function() {
var eventGen = (function*() {
var innerdoc = document.getElementById('testframe').contentDocument;
for (var doc of [document, innerdoc]) {
@ -163,8 +163,6 @@ function testPhoom(isCapturing, x, y, expectEvent) {
}
setTimeout(nextTest, 0);
yield;
})();
setTimeout(function() { eventGen.next(); }, 0);

View File

@ -29,7 +29,7 @@
// should be fired when a page is restored from the bfcache as though it had
// reloaded.
//
function testIterator() {
function* testIterator() {
// Make sure bfcache is on.
enableBFCache(true);
@ -69,10 +69,7 @@
} );
yield;
// Tell the framework the test is finished. Include the final 'yield'
// statement to prevent a StopIteration exception from being thrown.
finish();
yield;
}
]]></script>

View File

@ -554,6 +554,9 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
// This is not the root element or we are calculating something other
// than font size, so rem is relative to the root element's font size.
// Find the root style context by walking up the style context tree.
// NOTE: We should not call ResolveStyleFor() against the root element
// to obtain the root style here because it may lead to reentrant call
// of nsStyleSet::GetContext().
nsStyleContext* rootStyle = aStyleContext;
while (rootStyle->GetParent()) {
rootStyle = rootStyle->GetParent();

View File

@ -506,7 +506,7 @@ strlcat(dst, src, siz)
#endif /* LINUX or WIN32 */
#if defined(USE_OWN_INET_NTOP) || defined(WIN32)
#if defined(USE_OWN_INET_NTOP) || (defined(WIN32) && WINVER < 0x0600)
#include <errno.h>
#ifdef WIN32
#include <Ws2ipdef.h>
@ -774,19 +774,5 @@ int gettimeofday(struct timeval *tv, void *tz)
return 0;
}
#if _MSC_VER < 1900
int snprintf(char *buffer, size_t n, const char *format, ...)
{
va_list argp;
int ret;
va_start(argp, format);
ret = _vscprintf(format, argp);
vsnprintf_s(buffer, n, _TRUNCATE, format, argp);
va_end(argp);
return ret;
}
#endif
#endif

View File

@ -64,8 +64,7 @@ int nr_write_pid_file(char *pid_filename);
int nr_reg_uint4_fetch_and_check(NR_registry key, UINT4 min, UINT4 max, int log_fac, int die, UINT4 *val);
int nr_reg_uint8_fetch_and_check(NR_registry key, UINT8 min, UINT8 max, int log_fac, int die, UINT8 *val);
#ifdef WIN32
int snprintf(char *buffer, size_t n, const char *format, ...);
#if defined(WIN32) && WINVER < 0x0600
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
int inet_pton(int af, const char *src, void *dst);
#endif

View File

@ -6672,7 +6672,7 @@ var Distribution = {
// aFile is an nsIFile
// aCallback takes the parsed JSON object as a parameter
readJSON: function dc_readJSON(aFile, aCallback) {
Task.spawn(function() {
Task.spawn(function*() {
let bytes = yield OS.File.read(aFile.path);
let raw = new TextDecoder().decode(bytes) || "";

View File

@ -257,7 +257,7 @@ function removeSnippet(messageId, snippetId) {
function writeStat(snippetId, timestamp) {
let data = gEncoder.encode(snippetId + "," + timestamp + ";");
Task.spawn(function() {
Task.spawn(function*() {
try {
let file = yield OS.File.open(gStatsPath, { append: true, write: true });
try {

View File

@ -213,7 +213,7 @@ var gDatabaseEnsured = false;
* Creates the database schema.
*/
function createDatabase(db) {
return Task.spawn(function create_database_task() {
return Task.spawn(function* create_database_task() {
yield db.execute(SQL.createItemsTable);
});
}
@ -222,7 +222,7 @@ function createDatabase(db) {
* Migrates the database schema to a new version.
*/
function upgradeDatabase(db, oldVersion, newVersion) {
return Task.spawn(function upgrade_database_task() {
return Task.spawn(function* upgrade_database_task() {
switch (oldVersion) {
case 1:
// Migration from v1 to latest:
@ -251,10 +251,10 @@ function upgradeDatabase(db, oldVersion, newVersion) {
* @resolves Handle on an opened SQLite database.
*/
function getDatabaseConnection() {
return Task.spawn(function get_database_connection_task() {
return Task.spawn(function* get_database_connection_task() {
let db = yield Sqlite.openConnection({ path: DB_PATH });
if (gDatabaseEnsured) {
throw new Task.Result(db);
return db;
}
try {
@ -277,7 +277,7 @@ function getDatabaseConnection() {
}
gDatabaseEnsured = true;
throw new Task.Result(db);
return db;
});
}
@ -350,10 +350,10 @@ HomeStorage.prototype = {
": you cannot save more than " + MAX_SAVE_COUNT + " items at once";
}
return Task.spawn(function save_task() {
return Task.spawn(function* save_task() {
let db = yield getDatabaseConnection();
try {
yield db.executeTransaction(function save_transaction() {
yield db.executeTransaction(function* save_transaction() {
if (options && options.replace) {
yield db.executeCached(SQL.deleteFromDataset, { dataset_id: this.datasetId });
}
@ -392,7 +392,7 @@ HomeStorage.prototype = {
* @resolves When the operation has completed.
*/
deleteAll: function() {
return Task.spawn(function delete_all_task() {
return Task.spawn(function* delete_all_task() {
let db = yield getDatabaseConnection();
try {
let params = { dataset_id: this.datasetId };

View File

@ -92,7 +92,7 @@ add_task(function* test_formdata() {
// Creates a tab, loads a page with some form fields,
// modifies their values and closes the tab.
function createAndRemoveTab() {
return Task.spawn(function () {
return Task.spawn(function* () {
// Create a new tab.
let tab = gBrowserApp.addTab(URL);
let browser = tab.browser;
@ -147,7 +147,7 @@ add_task(function* test_formdata2() {
// Creates a tab, loads a page with some form fields,
// modifies their values and closes the tab.
function createAndRemoveTab() {
return Task.spawn(function () {
return Task.spawn(function* () {
// Create a new tab.
let tab = gBrowserApp.addTab(URL);
let browser = tab.browser;
@ -207,7 +207,7 @@ add_task(function* test_formdata_navigation() {
// Creates a tab, loads a page with some form fields, modifies their values,
// navigates to a different page and back again and closes the tab.
function createNavigateAndRemoveTab() {
return Task.spawn(function () {
return Task.spawn(function* () {
// Create a new tab.
let tab = gBrowserApp.addTab(URL);
let browser = tab.browser;

View File

@ -65,7 +65,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=810981
// Creates a tab, sets a scroll position and closes the tab.
function createAndRemoveTab() {
return Task.spawn(function () {
return Task.spawn(function* () {
// Create a new tab.
tabScroll = BrowserApp.addTab(URL);
let browser = tabScroll.browser;
@ -120,7 +120,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=810981
// Creates a tab, sets a scroll position and closes the tab.
function createAndRemoveReaderTab() {
return Task.spawn(function () {
return Task.spawn(function* () {
// Create a new tab.
tabScroll = BrowserApp.addTab(URL_reader);
let browser = tabScroll.browser;
@ -176,7 +176,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=810981
// Creates a tab, sets a scroll position and zoom level and closes the tab.
function createAndRemoveTab() {
return Task.spawn(function () {
return Task.spawn(function* () {
// Create a new tab.
tabScroll = BrowserApp.addTab(URL);
let browser = tabScroll.browser;
@ -238,7 +238,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=810981
// Creates a tab, sets a scroll position and zoom level and closes the tab.
function createAndRemoveTab() {
return Task.spawn(function () {
return Task.spawn(function* () {
// Create a new tab.
tabScroll = BrowserApp.addTab(URL_desktop);
let browser = tabScroll.browser;

View File

@ -685,7 +685,7 @@ var _Task;
*
* Example usage:
*
* add_task(function test() {
* add_task(function* test() {
* let result = yield Promise.resolve(true);
*
* do_check_true(result);
@ -694,7 +694,7 @@ var _Task;
* do_check_eq(secondary, "expected value");
* });
*
* add_task(function test_early_return() {
* add_task(function* test_early_return() {
* let result = yield somethingThatReturnsAPromise();
*
* if (!result) {

View File

@ -882,7 +882,7 @@ GeckoDriver.prototype.executeScript = function*(cmd, resp) {
* @throws JavaScriptError
* If an Error was thrown whilst evaluating the script.
*/
GeckoDriver.prototype.executeAsyncScript = function (cmd, resp) {
GeckoDriver.prototype.executeAsyncScript = function* (cmd, resp) {
let {script, args, scriptTimeout} = cmd.parameters;
scriptTimeout = scriptTimeout || this.scriptTimeout;
@ -932,7 +932,7 @@ GeckoDriver.prototype.execute_ = function (script, args, timeout, opts = {}) {
*
* Scripts are expected to call the {@code finish} global when done.
*/
GeckoDriver.prototype.executeJSScript = function (cmd, resp) {
GeckoDriver.prototype.executeJSScript = function* (cmd, resp) {
let {script, args, scriptTimeout} = cmd.parameters;
scriptTimeout = scriptTimeout || this.scriptTimeout;

View File

@ -371,7 +371,7 @@ function serverDebug(metadata, response)
* Creates a generator that iterates over the contents of
* an nsIFile directory.
*/
function dirIter(dir)
function* dirIter(dir)
{
var en = dir.directoryEntries;
while (en.hasMoreElements()) {

View File

@ -15,12 +15,12 @@ function executeWithTimeout() {
);
}
add_task(function asyncTest_no1() {
add_task(function* asyncTest_no1() {
yield executeWithTimeout();
test1Complete = true;
});
add_task(function asyncTest_no2() {
add_task(function* asyncTest_no2() {
yield executeWithTimeout();
test2Complete = true;
});

View File

@ -3,10 +3,20 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* This file defines specific rules for print preview when using simplify mode.
* These rules already exist on aboutReaderControls.css, however, we decoupled it
* from the original file so we don't need to load a bunch of extra queries that
* will not take effect when using the simplify page checkbox. This file defines
* styling for title and author on the header element. */
* Some of these rules (styling for title and author on the header element)
* already exist on aboutReaderControls.css, however, we decoupled it from the
* original file so we don't need to load a bunch of extra queries that will not
* take effect when using the simplify page checkbox. */
body {
padding-top: 0px;
padding-bottom: 0px;
}
#container {
max-width: 100%;
font-family: Georgia, "Times New Roman", serif;
}
.header > h1 {
font-size: 1.6em;

View File

@ -554,6 +554,13 @@ var Printing = {
headStyleElement.setAttribute("type", "text/css");
content.document.head.appendChild(headStyleElement);
// Create link element referencing simplifyMode.css and append it to head
headStyleElement = content.document.createElement("link");
headStyleElement.setAttribute("rel", "stylesheet");
headStyleElement.setAttribute("href", "chrome://global/content/simplifyMode.css");
headStyleElement.setAttribute("type", "text/css");
content.document.head.appendChild(headStyleElement);
content.document.body.innerHTML = "";
// Create container div (main element) and append it to body
@ -567,12 +574,6 @@ var Printing = {
headerElement.setAttribute("class", "header");
containerElement.appendChild(headerElement);
// Create style element for header div and import simplifyMode.css
let controlHeaderStyle = content.document.createElement("style");
controlHeaderStyle.setAttribute("scoped", "");
controlHeaderStyle.textContent = "@import url(\"chrome://global/content/simplifyMode.css\");";
headerElement.appendChild(controlHeaderStyle);
// Jam the article's title and byline into header div
let titleElement = content.document.createElement("h1");
titleElement.setAttribute("id", "reader-title");

View File

@ -6,19 +6,6 @@ body {
padding: 64px 51px;
}
@media print {
#container {
max-width: 100% !important;
font-size: 14px !important;
font-family: Georgia, "Times New Roman", serif !important;
}
body {
padding-top: 0px;
padding-bottom: 0px;
}
}
body.loaded {
transition: color 0.4s, background-color 0.4s;
}

View File

@ -55,6 +55,7 @@
figure,
.wp-caption {
margin: 0 0 10px 0 !important;
padding: 0 !important;
}
}

View File

@ -28,7 +28,9 @@
#include "mozilla/Telemetry.h"
// Defined in dwmapi in a header that needs a higher numbered _WINNT #define
#ifndef DWM_SIT_DISPLAYFRAME
#define DWM_SIT_DISPLAYFRAME 0x1
#endif
namespace mozilla {
namespace widget {

View File

@ -604,7 +604,7 @@ WinUtils::SystemScaleFactor()
return systemScale;
}
#ifndef WM_DPICHANGED
#if WINVER < 0x603
typedef enum {
MDT_EFFECTIVE_DPI = 0,
MDT_ANGULAR_DPI = 1,

View File

@ -64,8 +64,9 @@ nsClipboard::nsClipboard() : nsBaseClipboard()
// to the OS clipboard.
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1");
if (observerService)
if (observerService) {
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
}
}
//-------------------------------------------------------------------------
@ -94,26 +95,34 @@ UINT nsClipboard::GetFormat(const char* aMimeStr, bool aMapHTMLMime)
{
UINT format;
if (strcmp(aMimeStr, kTextMime) == 0)
if (strcmp(aMimeStr, kTextMime) == 0) {
format = CF_TEXT;
else if (strcmp(aMimeStr, kUnicodeMime) == 0)
}
else if (strcmp(aMimeStr, kUnicodeMime) == 0) {
format = CF_UNICODETEXT;
else if (strcmp(aMimeStr, kRTFMime) == 0)
}
else if (strcmp(aMimeStr, kRTFMime) == 0) {
format = ::RegisterClipboardFormat(L"Rich Text Format");
}
else if (strcmp(aMimeStr, kJPEGImageMime) == 0 ||
strcmp(aMimeStr, kJPGImageMime) == 0 ||
strcmp(aMimeStr, kPNGImageMime) == 0)
strcmp(aMimeStr, kPNGImageMime) == 0) {
format = CF_DIBV5;
}
else if (strcmp(aMimeStr, kFileMime) == 0 ||
strcmp(aMimeStr, kFilePromiseMime) == 0)
strcmp(aMimeStr, kFilePromiseMime) == 0) {
format = CF_HDROP;
}
else if (strcmp(aMimeStr, kNativeHTMLMime) == 0 ||
aMapHTMLMime && strcmp(aMimeStr, kHTMLMime) == 0)
aMapHTMLMime && strcmp(aMimeStr, kHTMLMime) == 0) {
format = CF_HTML;
else if (strcmp(aMimeStr, kCustomTypesMime) == 0)
}
else if (strcmp(aMimeStr, kCustomTypesMime) == 0) {
format = CF_CUSTOMTYPES;
else
}
else {
format = ::RegisterClipboardFormatW(NS_ConvertASCIItoUTF16(aMimeStr).get());
}
return format;
}
@ -129,8 +138,9 @@ nsresult nsClipboard::CreateNativeDataObject(nsITransferable * aTransferable, ID
// the OLE IDataObject interface
nsDataObj * dataObj = new nsDataObj(uri);
if (!dataObj)
if (!dataObj) {
return NS_ERROR_OUT_OF_MEMORY;
}
dataObj->AddRef();
@ -253,8 +263,9 @@ nsresult nsClipboard::SetupNativeDataObject(nsITransferable * aTransferable, IDa
//-------------------------------------------------------------------------
NS_IMETHODIMP nsClipboard::SetNativeClipboardData ( int32_t aWhichClipboard )
{
if ( aWhichClipboard != kGlobalClipboard )
if (aWhichClipboard != kGlobalClipboard) {
return NS_ERROR_FAILURE;
}
mIgnoreEmptyNotification = true;
@ -412,8 +423,9 @@ nsresult nsClipboard::GetNativeDataOffClipboard(IDataObject * aDataObject, UINT
*aData = nullptr;
*aLen = 0;
if ( !aDataObject )
if (!aDataObject) {
return result;
}
UINT format = aFormat;
HRESULT hres = S_FALSE;
@ -505,8 +517,9 @@ nsresult nsClipboard::GetNativeDataOffClipboard(IDataObject * aDataObject, UINT
*aLen = fileNameLen * sizeof(char16_t);
result = NS_OK;
}
else
else {
result = NS_ERROR_OUT_OF_MEMORY;
}
}
GlobalUnlock (stm.hGlobal) ;
@ -583,8 +596,9 @@ nsresult nsClipboard::GetDataFromDataObject(IDataObject * aDataObject,
nsITransferable * aTransferable)
{
// make sure we have a good transferable
if ( !aTransferable )
if (!aTransferable) {
return NS_ERROR_INVALID_ARG;
}
nsresult res = NS_ERROR_FAILURE;
@ -592,8 +606,9 @@ nsresult nsClipboard::GetDataFromDataObject(IDataObject * aDataObject,
// obtained through conversion)
nsCOMPtr<nsIArray> flavorList;
res = aTransferable->FlavorsTransferableCanImport ( getter_AddRefs(flavorList) );
if ( NS_FAILED(res) )
if (NS_FAILED(res)) {
return NS_ERROR_FAILURE;
}
// Walk through flavors and see which flavor is on the clipboard them on the native clipboard,
uint32_t i;
@ -612,26 +627,30 @@ nsresult nsClipboard::GetDataFromDataObject(IDataObject * aDataObject,
uint32_t dataLen = 0;
bool dataFound = false;
if (nullptr != aDataObject) {
if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject, anIndex, format, flavorStr, &data, &dataLen)) )
if (NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject, anIndex, format, flavorStr, &data, &dataLen))) {
dataFound = true;
}
}
else if (nullptr != aWindow) {
if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow, anIndex, format, &data, &dataLen)) )
if (NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow, anIndex, format, &data, &dataLen))) {
dataFound = true;
}
}
// This is our second chance to try to find some data, having not found it
// when directly asking for the flavor. Let's try digging around in other
// flavors to help satisfy our craving for data.
if ( !dataFound ) {
if ( strcmp(flavorStr, kUnicodeMime) == 0 )
dataFound = FindUnicodeFromPlainText ( aDataObject, anIndex, &data, &dataLen );
if (strcmp(flavorStr, kUnicodeMime) == 0) {
dataFound = FindUnicodeFromPlainText(aDataObject, anIndex, &data, &dataLen);
}
else if ( strcmp(flavorStr, kURLMime) == 0 ) {
// drags from other windows apps expose the native
// CFSTR_INETURL{A,W} flavor
dataFound = FindURLFromNativeURL ( aDataObject, anIndex, &data, &dataLen );
if ( !dataFound )
dataFound = FindURLFromLocalFile ( aDataObject, anIndex, &data, &dataLen );
if (!dataFound) {
dataFound = FindURLFromLocalFile(aDataObject, anIndex, &data, &dataLen);
}
}
} // if we try one last ditch effort to find our data
@ -642,8 +661,9 @@ nsresult nsClipboard::GetDataFromDataObject(IDataObject * aDataObject,
// we have a file path in |data|. Create an nsLocalFile object.
nsDependentString filepath(reinterpret_cast<char16_t*>(data));
nsCOMPtr<nsIFile> file;
if ( NS_SUCCEEDED(NS_NewLocalFile(filepath, false, getter_AddRefs(file))) )
if (NS_SUCCEEDED(NS_NewLocalFile(filepath, false, getter_AddRefs(file)))) {
genericDataWrapper = do_QueryInterface(file);
}
free(data);
}
else if ( strcmp(flavorStr, kNativeHTMLMime) == 0 ) {
@ -651,8 +671,9 @@ nsresult nsClipboard::GetDataFromDataObject(IDataObject * aDataObject,
// the editor folks want CF_HTML exactly as it's on the clipboard, no conversions,
// no fancy stuff. Pull it off the clipboard, stuff it into a wrapper and hand
// it back to them.
if ( FindPlatformHTML(aDataObject, anIndex, &data, &dummy, &dataLen) )
nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper) );
if (FindPlatformHTML(aDataObject, anIndex, &data, &dummy, &dataLen)) {
nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper));
}
else
{
free(data);
@ -695,8 +716,9 @@ nsresult nsClipboard::GetDataFromDataObject(IDataObject * aDataObject,
if (strcmp(flavorStr, kRTFMime) == 0) {
// RTF on Windows is known to sometimes deliver an extra null byte.
if (dataLen > 0 && static_cast<char*>(data)[dataLen - 1] == '\0')
if (dataLen > 0 && static_cast<char*>(data)[dataLen - 1] == '\0') {
dataLen--;
}
}
}
@ -851,8 +873,9 @@ nsClipboard :: FindURLFromLocalFile ( IDataObject* inDataObject, UINT inIndex, v
file->GetLeafName(title);
// We rely on IsInternetShortcut check that file has a .url extension.
title.SetLength(title.Length() - 4);
if (title.IsEmpty())
if (title.IsEmpty()) {
title = urlString;
}
*outData = ToNewUnicode(urlString + NS_LITERAL_STRING("\n") + title);
*outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
@ -910,10 +933,12 @@ nsClipboard :: FindURLFromNativeURL ( IDataObject* inDataObject, UINT inIndex, v
bool unescaped = NS_UnescapeURL(static_cast<char*>(tempOutData), tempDataLen, esc_OnlyNonASCII | esc_SkipControl, urlUnescapedA);
nsString urlString;
if (unescaped)
if (unescaped) {
NS_CopyNativeToUnicode(urlUnescapedA, urlString);
else
}
else {
NS_CopyNativeToUnicode(nsDependentCString(static_cast<char*>(tempOutData), tempDataLen), urlString);
}
// the internal mozilla URL format, text/x-moz-url, contains
// URL\ntitle. Since we don't actually have a title here,
@ -936,13 +961,15 @@ nsClipboard :: ResolveShortcut ( nsIFile* aFile, nsACString& outURL )
{
nsCOMPtr<nsIFileProtocolHandler> fph;
nsresult rv = NS_GetFileProtocolHandler(getter_AddRefs(fph));
if (NS_FAILED(rv))
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsIURI> uri;
rv = fph->ReadURLFile(aFile, getter_AddRefs(uri));
if (NS_FAILED(rv))
if (NS_FAILED(rv)) {
return;
}
uri->GetSpec(outURL);
} // ResolveShortcut
@ -965,8 +992,9 @@ NS_IMETHODIMP
nsClipboard::GetNativeClipboardData ( nsITransferable * aTransferable, int32_t aWhichClipboard )
{
// make sure we have a good transferable
if ( !aTransferable || aWhichClipboard != kGlobalClipboard )
if ( !aTransferable || aWhichClipboard != kGlobalClipboard ) {
return NS_ERROR_FAILURE;
}
nsresult res;
@ -1006,13 +1034,15 @@ NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(const char** aFlavorList,
bool *_retval)
{
*_retval = false;
if (aWhichClipboard != kGlobalClipboard || !aFlavorList)
if (aWhichClipboard != kGlobalClipboard || !aFlavorList) {
return NS_OK;
}
for (uint32_t i = 0;i < aLength; ++i) {
for (uint32_t i = 0; i < aLength; ++i) {
#ifdef DEBUG
if (strcmp(aFlavorList[i], kTextMime) == 0)
NS_WARNING ( "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD" );
if (strcmp(aFlavorList[i], kTextMime) == 0) {
NS_WARNING("DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD");
}
#endif
UINT format = GetFormat(aFlavorList[i]);
@ -1026,8 +1056,9 @@ NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(const char** aFlavorList,
if (strcmp(aFlavorList[i], kUnicodeMime) == 0) {
// client asked for unicode and it wasn't present, check if we have CF_TEXT.
// We'll handle the actual data substitution in the data object.
if (IsClipboardFormatAvailable(GetFormat(kTextMime)))
if (IsClipboardFormatAvailable(GetFormat(kTextMime))) {
*_retval = true;
}
}
}
}

View File

@ -27,6 +27,8 @@
#ifndef SM_SYSTEMDOCKED
#define SM_CONVERTIBLESLATEMODE 0x00002003
#define SM_SYSTEMDOCKED 0x00002004
#endif
#if WINVER < 0x0601
typedef enum _AR_STATE
{
AR_ENABLED = 0x0,

View File

@ -18,7 +18,7 @@
#include "mozilla/TouchEvents.h"
// Desktop builds target apis for 502. Win8 Metro builds target 602.
#if WINVER < 0x0602
#if WINVER < 0x0601
DECLARE_HANDLE(HGESTUREINFO);

View File

@ -329,6 +329,118 @@ private:
Maybe<TimeStamp> CurrentWindowsTimeGetter::sBackwardsSkewStamp;
DWORD CurrentWindowsTimeGetter::sLastPostTime = 0;
#if defined(ACCESSIBILITY)
/**
* Windows touchscreen code works by setting a global WH_GETMESSAGE hook and
* injecting tiptsf.dll. The touchscreen process then posts registered messages
* to our main thread. The tiptsf hook picks up those registered messages and
* uses them as commands, some of which call into UIA, which then calls into
* MSAA, which then sends WM_GETOBJECT to us.
*
* We can get ahead of this by installing our own thread-local WH_GETMESSAGE
* hook. Since thread-local hooks are called ahead of global hooks, we will
* see these registered messages before tiptsf does. At this point we can then
* raise a flag that blocks a11y before invoking CallNextHookEx which will then
* invoke the global tiptsf hook. Then when we see WM_GETOBJECT, we check the
* flag by calling TIPMessageHandler::IsA11yBlocked().
*/
class TIPMessageHandler
{
public:
~TIPMessageHandler()
{
if (mHook) {
::UnhookWindowsHookEx(mHook);
}
}
static void Initialize()
{
MOZ_ASSERT(!sInstance);
sInstance = new TIPMessageHandler();
ClearOnShutdown(&sInstance);
}
static bool IsA11yBlocked()
{
MOZ_ASSERT(sInstance);
if (!sInstance) {
return false;
}
return sInstance->mA11yBlockCount > 0;
}
private:
TIPMessageHandler()
: mHook(nullptr)
, mA11yBlockCount(0)
{
MOZ_ASSERT(NS_IsMainThread());
// Registered messages used by tiptsf
mMessages[0] = ::RegisterWindowMessage(L"ImmersiveFocusNotification");
mMessages[1] = ::RegisterWindowMessage(L"TipCloseMenus");
mMessages[2] = ::RegisterWindowMessage(L"TabletInputPanelOpening");
mMessages[3] = ::RegisterWindowMessage(L"IHM Pen or Touch Event noticed");
mMessages[4] = ::RegisterWindowMessage(L"ProgrammabilityCaretVisibility");
mMessages[5] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPHidden");
mMessages[6] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPInfo");
mHook = ::SetWindowsHookEx(WH_GETMESSAGE, &TIPHook, nullptr,
::GetCurrentThreadId());
MOZ_ASSERT(mHook);
}
class MOZ_RAII A11yInstantiationBlocker
{
public:
A11yInstantiationBlocker()
{
MOZ_ASSERT(TIPMessageHandler::sInstance);
++TIPMessageHandler::sInstance->mA11yBlockCount;
}
~A11yInstantiationBlocker()
{
MOZ_ASSERT(TIPMessageHandler::sInstance &&
TIPMessageHandler::sInstance->mA11yBlockCount > 0);
--TIPMessageHandler::sInstance->mA11yBlockCount;
}
};
friend class A11yInstantiationBlocker;
static LRESULT CALLBACK TIPHook(int aCode, WPARAM aWParam, LPARAM aLParam)
{
if (aCode < 0 || !sInstance) {
return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
}
MSG* msg = reinterpret_cast<MSG*>(aLParam);
UINT& msgCode = msg->message;
for (uint32_t i = 0; i < ArrayLength(sInstance->mMessages); ++i) {
if (msgCode == sInstance->mMessages[i]) {
A11yInstantiationBlocker block;
return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
}
}
return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
}
static StaticAutoPtr<TIPMessageHandler> sInstance;
HHOOK mHook;
UINT mMessages[7];
uint32_t mA11yBlockCount;
};
StaticAutoPtr<TIPMessageHandler> TIPMessageHandler::sInstance;
#endif // defined(ACCESSIBILITY)
} // namespace mozilla
/**************************************************************
@ -456,6 +568,9 @@ nsWindow::nsWindow()
// WinTaskbar.cpp for details.
mozilla::widget::WinTaskbar::RegisterAppUserModelID();
KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
#if defined(ACCESSIBILITY)
mozilla::TIPMessageHandler::Initialize();
#endif // defined(ACCESSIBILITY)
IMEHandler::Initialize();
if (SUCCEEDED(::OleInitialize(nullptr))) {
sIsOleInitialized = TRUE;
@ -3281,9 +3396,9 @@ nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen)
if (aFullScreen && !sCurrentWindow) {
sCurrentWindow = this;
LPARAM pos = sCurrentWindow->lParamToClient(sMouseExitlParamScreen);
sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget,
sMouseExitwParam, pos, false,
WidgetMouseEvent::eLeftButton,
sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget,
sMouseExitwParam, pos, false,
WidgetMouseEvent::eLeftButton,
MOUSE_INPUT_SOURCE(), MOUSE_POINTERID());
}
@ -5599,7 +5714,7 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
// Do explicit casting to make it working on 64bit systems (see bug 649236
// for details).
int32_t objId = static_cast<DWORD>(lParam);
if (objId == OBJID_CLIENT) { // oleacc.dll will be loaded dynamically
if (!TIPMessageHandler::IsA11yBlocked() && objId == OBJID_CLIENT) { // oleacc.dll will be loaded dynamically
a11y::Accessible* rootAccessible = GetAccessible(); // Held by a11y cache
if (rootAccessible) {
IAccessible *msaaAccessible = nullptr;