Merge mozilla-central to autoland. r=merge a=merge CLOSED TREE

This commit is contained in:
Ciure Andrei 2017-12-02 12:19:55 +02:00
Родитель 6d6d5c3c75 1b7f664804
Коммит 48e5c9892f
81 изменённых файлов: 1512 добавлений и 633 удалений

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

@ -68,6 +68,7 @@ toolbar[customizable="true"] {
}
panelmultiview {
-moz-box-align: start;
-moz-binding: url("chrome://browser/content/customizableui/panelUI.xml#panelmultiview");
}

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

@ -5,6 +5,21 @@ add_task(async function() {
BrowserTestUtils.addTab(gBrowser);
BrowserTestUtils.addTab(gBrowser);
// While doing this test, we should make sure the selected tab in the tab
// preview is not changed by mouse events. That may happen after closing
// the selected tab with ctrl+W. Disable all mouse events to prevent it.
for (let node of ctrlTab.previews) {
node.style.pointerEvents = "none";
}
registerCleanupFunction(function() {
for (let node of ctrlTab.previews) {
try {
node.style.removeProperty("pointer-events");
} catch (e) {
}
}
});
checkTabs(4);
await ctrlTabTest([2], 1, 0);

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

@ -75,7 +75,7 @@ add_task(async function testExceptionAddition() {
await UrlClassifierTestUtils.addTestTrackers();
let privateWin = await promiseOpenAndLoadWindow({private: true}, true);
browser = privateWin.gBrowser;
let tab = browser.selectedTab = browser.addTab();
let tab = await BrowserTestUtils.openNewForegroundTab({ gBrowser: browser, waitForLoad: true, waitForStateStop: true });
TrackingProtection = browser.ownerGlobal.TrackingProtection;
await pushPrefs([PB_PREF, true]);
@ -109,7 +109,7 @@ add_task(async function testExceptionPersistence() {
info("Open another private browsing window");
let privateWin = await promiseOpenAndLoadWindow({private: true}, true);
browser = privateWin.gBrowser;
let tab = browser.selectedTab = browser.addTab();
let tab = await BrowserTestUtils.openNewForegroundTab({ gBrowser: browser, waitForLoad: true, waitForStateStop: true });
TrackingProtection = browser.ownerGlobal.TrackingProtection;
ok(TrackingProtection.enabled, "TP is still enabled");

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

@ -223,7 +223,7 @@ function promiseWindowWillBeClosed(win) {
Services.obs.addObserver(function observe(subject, topic) {
if (subject == win) {
Services.obs.removeObserver(observe, topic);
resolve();
executeSoon(resolve);
}
}, "domwindowclosed");
});

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

@ -1,6 +1,12 @@
[DEFAULT]
# to avoid overhead when running the browser normally, startupRecorder.js will
# do almost nothing unless browser.startup.record is true.
# gfx.canvas.willReadFrequently.enable is just an optimization, but needs to be
# set during early startup to have an impact as a canvas will be used by
# startupRecorder.js
prefs =
browser.startup.record=true
gfx.canvas.willReadFrequently.enable=true
support-files =
head.js
[browser_appmenu_reflows.js]
@ -9,6 +15,7 @@ skip-if = asan || debug # Bug 1382809, bug 1369959
[browser_startup.js]
[browser_startup_content.js]
skip-if = !e10s
[browser_startup_flicker.js]
[browser_tabclose_grow_reflows.js]
[browser_tabclose_reflows.js]
[browser_tabopen_reflows.js]
@ -20,5 +27,7 @@ skip-if = !e10s
skip-if = (os == 'linux') || (os == 'win' && debug) # Disabled on Linux and Windows debug due to perma failures. Bug 1392320.
[browser_urlbar_search_reflows.js]
[browser_windowclose_reflows.js]
[browser_windowopen_flicker.js]
skip-if = (debug && os == 'win') # Disabled on windows debug for intermittent leaks
[browser_windowopen_reflows.js]
skip-if = os == 'linux' # Disabled due to frequent failures. Bug 1380465.

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

@ -0,0 +1,99 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/*
* This test ensures that there is no unexpected flicker
* on the first window opened during startup.
*/
add_task(async function() {
let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
await startupRecorder.done;
// Ensure all the frame data is in the test compartment to avoid traversing
// a cross compartment wrapper for each pixel.
let frames = Cu.cloneInto(startupRecorder.data.frames, {});
let unexpectedRects = 0;
let alreadyFocused = false;
for (let i = 1; i < frames.length; ++i) {
let frame = frames[i], previousFrame = frames[i - 1];
let rects = compareFrames(frame, previousFrame);
// The first screenshot we get shows an unfocused browser window for some
// reason. This is likely due to the test harness, so we want to ignore it.
// We'll assume the changes we are seeing are due to this focus change if
// there are at least 5 areas that changed near the top of the screen, but
// will only ignore this once (hence the alreadyFocused variable).
if (!alreadyFocused && rects.length > 5 && rects.every(r => r.y2 < 100)) {
alreadyFocused = true;
// This is likely an issue caused by the test harness, but log it anyway.
todo(false,
"the window should be focused at first paint, " + rects.toSource());
continue;
}
rects = rects.filter(rect => {
let inRange = (val, min, max) => min <= val && val <= max;
let width = frame.width;
let exceptions = [
{name: "bug 1403648 - urlbar down arrow shouldn't flicker",
condition: r => r.h == 5 && inRange(r.w, 8, 9) && // 5x9px area
inRange(r.y1, 40, 80) && // in the toolbar
// at ~80% of the window width
inRange(r.x1, width * .75, width * .9)
},
{name: "bug 1394914 - sidebar toolbar icon should be visible at first paint",
condition: r => r.h == 13 && inRange(r.w, 14, 16) && // icon size
inRange(r.y1, 40, 80) && // in the toolbar
// near the right end of screen
inRange(r.x1, width - 100, width - 50)
},
{name: "bug 1403648 - urlbar should be focused at first paint",
condition: r => inRange(r.y2, 60, 80) && // in the toolbar
// taking 50% to 75% of the window width
inRange(r.w, width * .5, width * .75) &&
// starting at 15 to 25% of the window width
inRange(r.x1, width * .15, width * .25)
},
{name: "bug 1421460 - restore icon should be visible at first paint",
condition: r => r.w == 9 && r.h == 9 && // 9x9 icon
AppConstants.platform == "win" &&
// near the right end of the screen
inRange(r.x1, width - 80, width - 70)
},
];
let rectText = `${rect.toSource()}, window width: ${width}`;
for (let e of exceptions) {
if (e.condition(rect)) {
todo(false, e.name + ", " + rectText);
return false;
}
}
ok(false, "unexpected changed rect: " + rectText);
return true;
});
if (!rects.length) {
info("ignoring identical frame");
continue;
}
// Before dumping a frame with unexpected differences for the first time,
// ensure at least one previous frame has been logged so that it's possible
// to see the differences when examining the log.
if (!unexpectedRects) {
dumpFrame(previousFrame);
}
unexpectedRects += rects.length;
dumpFrame(frame);
}
is(unexpectedRects, 0, "should have 0 unknown flickering areas");
});

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

@ -0,0 +1,151 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/*
* This test ensures that there is no unexpected flicker
* when opening new windows.
*/
add_task(async function() {
// Flushing all caches helps to ensure that we get consistent
// behaviour when opening a new window, even if windows have been
// opened in previous tests.
Services.obs.notifyObservers(null, "startupcache-invalidate");
Services.obs.notifyObservers(null, "chrome-flush-skin-caches");
Services.obs.notifyObservers(null, "chrome-flush-caches");
let win = window.openDialog("chrome://browser/content/", "_blank",
"chrome,all,dialog=no,remote,suppressanimation",
"about:home");
// Avoid showing the remotecontrol UI.
await new Promise(resolve => {
win.addEventListener("DOMContentLoaded", () => {
delete win.Marionette;
win.Marionette = {running: false};
resolve();
}, {once: true});
});
let canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml",
"canvas");
canvas.mozOpaque = true;
let ctx = canvas.getContext("2d", {alpha: false, willReadFrequently: true});
let frames = [];
let afterPaintListener = event => {
let width, height;
canvas.width = width = win.innerWidth;
canvas.height = height = win.innerHeight;
ctx.drawWindow(win, 0, 0, width, height, "white",
ctx.DRAWWINDOW_DO_NOT_FLUSH | ctx.DRAWWINDOW_DRAW_VIEW |
ctx.DRAWWINDOW_ASYNC_DECODE_IMAGES |
ctx.DRAWWINDOW_USE_WIDGET_LAYERS);
frames.push({data: Cu.cloneInto(ctx.getImageData(0, 0, width, height).data, {}),
width, height});
};
win.addEventListener("MozAfterPaint", afterPaintListener);
await TestUtils.topicObserved("browser-delayed-startup-finished",
subject => subject == win);
await BrowserTestUtils.firstBrowserLoaded(win, false);
await BrowserTestUtils.browserStopped(win.gBrowser.selectedBrowser, "about:home");
await new Promise(resolve => {
// 10 is an arbitrary value here, it needs to be at least 2 to avoid
// races with code initializing itself using idle callbacks.
(function waitForIdle(count = 10) {
if (!count) {
resolve();
return;
}
Services.tm.idleDispatchToMainThread(() => {
waitForIdle(count - 1);
});
})();
});
win.removeEventListener("MozAfterPaint", afterPaintListener);
let unexpectedRects = 0;
let foundTinyPaint = false;
for (let i = 1; i < frames.length; ++i) {
let frame = frames[i], previousFrame = frames[i - 1];
if (!foundTinyPaint &&
previousFrame.width == 1 && previousFrame.height == 1) {
foundTinyPaint = true;
todo(false, "shouldn't first paint a 1x1px window");
continue;
}
let rects = compareFrames(frame, previousFrame).filter(rect => {
let inRange = (val, min, max) => min <= val && val <= max;
let width = frame.width;
const spaceBeforeFirstTab = AppConstants.platform == "macosx" ? 100 : 0;
let inFirstTab = r =>
inRange(r.x1, spaceBeforeFirstTab, spaceBeforeFirstTab + 50) && r.y1 < 30;
let exceptions = [
{name: "bug 1403648 - urlbar down arrow shouldn't flicker",
condition: r => r.h == 5 && inRange(r.w, 8, 9) && // 5x9px area
inRange(r.y1, 40, 80) && // in the toolbar
// at ~80% of the window width
inRange(r.x1, width * .75, width * .9)
},
{name: "bug 1394914 - sidebar toolbar icon should be visible at first paint",
condition: r => r.h == 13 && inRange(r.w, 14, 16) && // icon size
inRange(r.y1, 40, 80) && // in the toolbar
// near the right end of screen
inRange(r.x1, width - 100, width - 50)
},
{name: "bug 1421463 - reload toolbar icon shouldn't flicker",
condition: r => r.h == 13 && inRange(r.w, 14, 16) && // icon size
inRange(r.y1, 40, 80) && // in the toolbar
// near the left side of the screen
inRange(r.x1, 65, 100)
},
{name: "bug 1401955 - about:home favicon should be visible at first paint",
condition: r => inFirstTab(r) && inRange(r.h, 14, 15) && inRange(r.w, 14, 15)
},
{name: "bug 1401955 - space for about:home favicon should be there at first paint",
condition: r => inFirstTab(r) && inRange(r.w, 60, 80) && inRange(r.h, 8, 15)
},
];
let rectText = `${rect.toSource()}, window width: ${width}`;
for (let e of exceptions) {
if (e.condition(rect)) {
todo(false, e.name + ", " + rectText);
return false;
}
}
ok(false, "unexpected changed rect: " + rectText);
return true;
});
if (!rects.length) {
info("ignoring identical frame");
continue;
}
// Before dumping a frame with unexpected differences for the first time,
// ensure at least one previous frame has been logged so that it's possible
// to see the differences when examining the log.
if (!unexpectedRects) {
dumpFrame(previousFrame);
}
unexpectedRects += rects.length;
dumpFrame(frame);
}
is(unexpectedRects, 0, "should have 0 unknown flickering areas");
await BrowserTestUtils.closeWindow(win);
});

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

@ -269,3 +269,107 @@ async function addDummyHistoryEntries(searchStr = "") {
await PlacesUtils.history.clear();
});
}
function compareFrames(frame, previousFrame) {
// Accessing the Math global is expensive as the test executes in a
// non-syntactic scope. Accessing it as a lexical variable is enough
// to make the code JIT well.
const M = Math;
function expandRect(x, y, rect) {
if (rect.x2 < x)
rect.x2 = x;
else if (rect.x1 > x)
rect.x1 = x;
if (rect.y2 < y)
rect.y2 = y;
}
function isInRect(x, y, rect) {
return (rect.y2 == y || rect.y2 == y - 1) && rect.x1 - 1 <= x && x <= rect.x2 + 1;
}
if (frame.height != previousFrame.height ||
frame.width != previousFrame.width) {
// If the frames have different sizes, assume the whole window has
// been repainted when the window was resized.
return [{x1: 0, x2: frame.width, y1: 0, y2: frame.height}];
}
let l = frame.data.length;
let different = [];
let rects = [];
for (let i = 0; i < l; i += 4) {
let x = (i / 4) % frame.width;
let y = M.floor((i / 4) / frame.width);
for (let j = 0; j < 4; ++j) {
let index = i + j;
if (frame.data[index] != previousFrame.data[index]) {
let found = false;
for (let rect of rects) {
if (isInRect(x, y, rect)) {
expandRect(x, y, rect);
found = true;
break;
}
}
if (!found)
rects.unshift({x1: x, x2: x, y1: y, y2: y});
different.push(i);
break;
}
}
}
rects.reverse();
// The following code block merges rects that are close to each other
// (less than maxEmptyPixels away).
// This is needed to avoid having a rect for each letter when a label moves.
const maxEmptyPixels = 3;
let areRectsContiguous = function(r1, r2) {
return r1.y2 >= r2.y1 - 1 - maxEmptyPixels &&
r2.x1 - 1 - maxEmptyPixels <= r1.x2 &&
r2.x2 >= r1.x1 - 1 - maxEmptyPixels;
};
let hasMergedRects;
do {
hasMergedRects = false;
for (let r = rects.length - 1; r > 0; --r) {
let rr = rects[r];
for (let s = r - 1; s >= 0; --s) {
let rs = rects[s];
if (areRectsContiguous(rs, rr)) {
rs.x1 = Math.min(rs.x1, rr.x1);
rs.y1 = Math.min(rs.y1, rr.y1);
rs.x2 = Math.max(rs.x2, rr.x2);
rs.y2 = Math.max(rs.y2, rr.y2);
rects.splice(r, 1);
hasMergedRects = true;
break;
}
}
}
} while (hasMergedRects);
// For convenience, pre-compute the width and height of each rect.
rects.forEach(r => {
r.w = r.x2 - r.x1;
r.h = r.y2 - r.y1;
});
return rects;
}
function dumpFrame({data, width, height}) {
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
canvas.mozOpaque = true;
canvas.width = width;
canvas.height = height;
canvas.getContext("2d", {alpha: false, willReadFrequently: true})
.putImageData(new ImageData(data, width, height), 0, 0);
info(canvas.toDataURL());
}

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

@ -13,6 +13,7 @@ skip-if = debug # bug 1369731
[browser_devices_get_user_media_multi_process.js]
skip-if = debug && (os == "win" || os == "mac") # bug 1393761
[browser_devices_get_user_media_screen.js]
skip-if = (os == "win" && ccov) # bug 1421724
[browser_devices_get_user_media_tear_off_tab.js]
[browser_devices_get_user_media_unprompted_access.js]
[browser_devices_get_user_media_unprompted_access_in_frame.js]

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

@ -589,7 +589,9 @@ this.PanelMultiView = class {
this._panel.setAttribute("height", rect.height);
let viewRect;
if (viewNode.__lastKnownBoundingRect) {
if (reverse && viewNode.__lastKnownBoundingRect) {
// Use the cached size when going back to a previous view, but not when
// reopening a subview, because its contents may have changed.
viewRect = viewNode.__lastKnownBoundingRect;
viewNode.setAttribute("in-transition", true);
} else if (viewNode.customRectGetter) {
@ -604,6 +606,8 @@ this.PanelMultiView = class {
viewNode.setAttribute("in-transition", true);
} else {
let oldSibling = viewNode.nextSibling || null;
this._offscreenViewStack.style.minHeight =
this._viewContainer.style.height;
this._offscreenViewStack.appendChild(viewNode);
viewNode.setAttribute("in-transition", true);
@ -620,6 +624,8 @@ this.PanelMultiView = class {
} catch (ex) {
this._viewStack.appendChild(viewNode);
}
this._offscreenViewStack.style.removeProperty("min-height");
}
this._transitioning = true;
@ -643,7 +649,8 @@ this.PanelMultiView = class {
this._viewStack.style.transition = "transform var(--animation-easing-function)" +
" var(--panelui-subview-transition-duration)";
this._viewStack.style.willChange = "transform";
deepestNode.style.borderInlineStart = "1px solid var(--panel-separator-color)";
// Use an outline instead of a border so that the size is not affected.
deepestNode.style.outline = "1px solid var(--panel-separator-color)";
// Now set the viewContainer dimensions to that of the new view, which
// kicks of the height animation.
@ -734,7 +741,7 @@ this.PanelMultiView = class {
if (reverse)
this._viewStack.style.removeProperty("margin-inline-start");
let deepestNode = reverse ? previousViewNode : viewNode;
deepestNode.style.removeProperty("border-inline-start");
deepestNode.style.removeProperty("outline");
this._viewStack.style.removeProperty("transition");
}
if (phase >= TRANSITION_PHASES.TRANSITION) {
@ -857,6 +864,7 @@ this.PanelMultiView = class {
if (this._panel.state == "showing") {
let maxHeight = this._calculateMaxHeight();
this._viewStack.style.maxHeight = maxHeight + "px";
this._offscreenViewStack.style.maxHeight = maxHeight + "px";
}
break;
}

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

@ -24,12 +24,4 @@ panelmultiview[transitioning] {
.panel-viewcontainer.offscreen {
position: absolute;
top: 100000px;
left: 100000px;
}
.panel-viewcontainer.offscreen,
.panel-viewcontainer.offscreen > .panel-viewstack {
margin: 0;
padding: 0;
}

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

@ -630,10 +630,12 @@
label="&appMenuRemoteTabs.label;"
closemenu="none"
oncommand="PanelUI.showSubView('PanelUI-remotetabs', this)"/>
<toolbarseparator/>
<toolbarseparator hidden="true"/>
<label value="&appMenuRecentHighlights.label;"
hidden="true"
class="subview-subheader"/>
<toolbaritem id="appMenu-library-recentHighlights"
hidden="true"
orient="vertical"
smoothscroll="false"
flatList="true"

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

@ -28,6 +28,7 @@ const PanelUI = {
multiView: "appMenu-multiView",
helpView: "PanelUI-helpView",
libraryView: "appMenu-libraryView",
libraryRecentHighlights: "appMenu-library-recentHighlights",
menuButton: "PanelUI-menu-button",
panel: "appMenu-popup",
notificationPanel: "appMenu-notification-popup",
@ -305,7 +306,7 @@ const PanelUI = {
break;
case "ViewShowing":
if (aEvent.target == this.libraryView) {
this.onLibraryViewShowing(aEvent.target);
this.onLibraryViewShowing(aEvent.target).catch(Cu.reportError);
}
break;
}
@ -507,32 +508,51 @@ const PanelUI = {
* @param {panelview} viewNode The library view.
*/
async onLibraryViewShowing(viewNode) {
if (this._loadingRecentHighlights) {
return;
}
this._loadingRecentHighlights = true;
// Since the library is the first view shown, we don't want to add a blocker
// to the event, which would make PanelMultiView wait to show it.
let container = this.clearLibraryRecentHighlights();
if (!this.libraryRecentHighlightsEnabled) {
this._loadingRecentHighlights = false;
// to the event, which would make PanelMultiView wait to show it. Instead,
// we keep the space currently reserved for the items, but we hide them.
if (this._loadingRecentHighlights || !this.libraryRecentHighlightsEnabled) {
return;
}
// Make the elements invisible synchronously, before the view is shown.
this.makeLibraryRecentHighlightsInvisible();
// Perform the rest asynchronously while protecting from re-entrancy.
this._loadingRecentHighlights = true;
try {
await this.fetchAndPopulateLibraryRecentHighlights();
} finally {
this._loadingRecentHighlights = false;
}
},
/**
* Fetches the list of Recent Highlights and replaces the items in the Library
* view with the results.
*/
async fetchAndPopulateLibraryRecentHighlights() {
let highlights = await NewTabUtils.activityStreamLinks.getHighlights({
// As per bug 1402023, hard-coded limit, until Activity Stream develops a
// richer list.
numItems: 6,
withFavicons: true
}).catch(ex => {
// Just hide the section if we can't retrieve the items from the database.
Cu.reportError(ex);
return [];
});
// If there's nothing to display, or the panel is already hidden, get out.
let multiView = viewNode.panelMultiView;
if (!highlights.length || (multiView && multiView.getAttribute("panelopen") != "true")) {
this._loadingRecentHighlights = false;
// Since the call above is asynchronous, the panel may be already hidden
// at this point, but we still prepare the items for the next time the
// panel is shown, so their space is reserved. The part of this function
// that adds the elements is the least expensive anyways.
this.clearLibraryRecentHighlights();
if (!highlights.length) {
return;
}
let container = this.libraryRecentHighlights;
container.hidden = container.previousSibling.hidden =
container.previousSibling.previousSibling.hidden = false;
let fragment = document.createDocumentFragment();
@ -551,21 +571,29 @@ const PanelUI = {
fragment.appendChild(button);
}
container.appendChild(fragment);
},
this._loadingRecentHighlights = false;
/**
* Make all nodes from the 'Recent Highlights' section invisible while we
* refresh its contents. This is done while the Library view is opening to
* avoid showing potentially stale items, but still keep the space reserved.
*/
makeLibraryRecentHighlightsInvisible() {
for (let button of this.libraryRecentHighlights.children) {
button.style.visibility = "hidden";
}
},
/**
* Remove all the nodes from the 'Recent Highlights' section and hide it as well.
*/
clearLibraryRecentHighlights() {
let container = document.getElementById("appMenu-library-recentHighlights");
let container = this.libraryRecentHighlights;
while (container.firstChild) {
container.firstChild.remove();
}
container.hidden = container.previousSibling.hidden =
container.previousSibling.previousSibling.hidden = true;
return container;
},
/**

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

@ -55,7 +55,9 @@ add_task(async function test_tab_options_modals() {
}
aboutAddonsBrowser.removeEventListener("DOMWillOpenModalDialog", onModalDialog, true);
resolve();
// Wait for the next event tick to make sure the remaining part of the
// testcase runs after the dialog gets opened.
SimpleTest.executeSoon(resolve);
}, true);
});

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

@ -67,7 +67,7 @@ function promiseLoadedSidebar(cmd) {
return new Promise(resolve => {
let sidebar = document.getElementById("sidebar");
sidebar.addEventListener("load", function() {
resolve(sidebar);
executeSoon(() => resolve(sidebar));
}, {capture: true, once: true});
SidebarUI.show(cmd);

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

@ -63,7 +63,7 @@ function promiseLibraryClosed(organizer) {
return new Promise(resolve => {
// Wait for the Organizer window to actually be closed
organizer.addEventListener("unload", function() {
resolve();
executeSoon(resolve);
}, {once: true});
// Close Library window.
@ -413,7 +413,7 @@ var withSidebarTree = async function(type, taskFn) {
info("withSidebarTree: waiting sidebar load");
let sidebarLoadedPromise = new Promise(resolve => {
sidebar.addEventListener("load", function() {
resolve();
executeSoon(resolve);
}, {capture: true, once: true});
});
let sidebarId = type == "bookmarks" ? "viewBookmarksSidebar"

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

@ -1,347 +1,316 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
requestLongerTimeout(2);
requestLongerTimeout(3);
function test() {
waitForExplicitFinish();
requestLongerTimeout(3);
testRunner.runTests();
add_task(async function testAllow() {
await runTest(async (params, observeAllPromise, apply) => {
is(params.tree.view.rowCount, 0, "no cookie exceptions");
params.url.value = "test.com";
params.btnAllow.doCommand();
is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission text should be set correctly");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "http://test.com", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }]);
});
add_task(async function testBlock() {
await runTest(async (params, observeAllPromise, apply) => {
params.url.value = "test.com";
params.btnBlock.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
"permission should change to deny in UI");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "http://test.com", data: "changed",
capability: Ci.nsIPermissionManager.DENY_ACTION }]);
});
add_task(async function testAllowAgain() {
await runTest(async (params, observeAllPromise, apply) => {
params.url.value = "test.com";
params.btnAllow.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission should revert back to allow");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "http://test.com", data: "changed",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }]);
});
add_task(async function testRemove() {
await runTest(async (params, observeAllPromise, apply) => {
params.url.value = "test.com";
params.btnRemove.doCommand();
is(params.tree.view.rowCount, 0, "exception should be removed");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "http://test.com", data: "deleted" }]);
});
add_task(async function testAdd() {
await runTest(async (params, observeAllPromise, apply) => {
let uri = Services.io.newURI("http://test.com");
Services.perms.add(uri, "popup", Ci.nsIPermissionManager.DENY_ACTION);
is(params.tree.view.rowCount, 0, "adding unrelated permission should not change display");
apply();
await observeAllPromise;
Services.perms.remove(uri, "popup");
}, [{ type: "popup", origin: "http://test.com", data: "added",
capability: Ci.nsIPermissionManager.DENY_ACTION }]);
});
add_task(async function testAllowHTTPSWithPort() {
await runTest(async (params, observeAllPromise, apply) => {
params.url.value = "https://test.com:12345";
params.btnAllow.doCommand();
is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission text should be set correctly");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "https://test.com:12345", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }]);
});
add_task(async function testBlockHTTPSWithPort() {
await runTest(async (params, observeAllPromise, apply) => {
params.url.value = "https://test.com:12345";
params.btnBlock.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
"permission should change to deny in UI");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "https://test.com:12345", data: "changed",
capability: Ci.nsIPermissionManager.DENY_ACTION }]);
});
add_task(async function testAllowAgainHTTPSWithPort() {
await runTest(async (params, observeAllPromise, apply) => {
params.url.value = "https://test.com:12345";
params.btnAllow.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission should revert back to allow");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "https://test.com:12345", data: "changed",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }]);
});
add_task(async function testRemoveHTTPSWithPort() {
await runTest(async (params, observeAllPromise, apply) => {
params.url.value = "https://test.com:12345";
params.btnRemove.doCommand();
is(params.tree.view.rowCount, 0, "exception should be removed");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "https://test.com:12345", data: "deleted" }]);
});
add_task(async function testAllowPort() {
await runTest(async (params, observeAllPromise, apply) => {
params.url.value = "localhost:12345";
params.btnAllow.doCommand();
is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission text should be set correctly");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "http://localhost:12345", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }]);
});
add_task(async function testBlockPort() {
await runTest(async (params, observeAllPromise, apply) => {
params.url.value = "localhost:12345";
params.btnBlock.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
"permission should change to deny in UI");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "http://localhost:12345", data: "changed",
capability: Ci.nsIPermissionManager.DENY_ACTION }]);
});
add_task(async function testAllowAgainPort() {
await runTest(async (params, observeAllPromise, apply) => {
params.url.value = "localhost:12345";
params.btnAllow.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission should revert back to allow");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "http://localhost:12345", data: "changed",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }]);
});
add_task(async function testRemovePort() {
await runTest(async (params, observeAllPromise, apply) => {
params.url.value = "localhost:12345";
params.btnRemove.doCommand();
is(params.tree.view.rowCount, 0, "exception should be removed");
apply();
await observeAllPromise;
}, [{ type: "cookie", origin: "http://localhost:12345", data: "deleted" }]);
});
add_task(async function testSort() {
await runTest(async (params, observeAllPromise, apply) => {
for (let URL of ["http://a", "http://z", "http://b"]) {
let URI = Services.io.newURI(URL);
Services.perms.add(URI, "cookie", Ci.nsIPermissionManager.ALLOW_ACTION);
}
is(params.tree.view.rowCount, 3, "Three permissions should be present");
is(params.tree.view.getCellText(0, params.nameCol), "http://a",
"site should be sorted. 'a' should be first");
is(params.tree.view.getCellText(1, params.nameCol), "http://b",
"site should be sorted. 'b' should be second");
is(params.tree.view.getCellText(2, params.nameCol), "http://z",
"site should be sorted. 'z' should be third");
// Sort descending then check results in cleanup since sorting isn't synchronous.
EventUtils.synthesizeMouseAtCenter(params.doc.getElementById("siteCol"), {},
params.doc.defaultView);
is(params.tree.view.getCellText(0, params.nameCol), "http://z",
"site should be sorted. 'z' should be first");
is(params.tree.view.getCellText(1, params.nameCol), "http://b",
"site should be sorted. 'b' should be second");
is(params.tree.view.getCellText(2, params.nameCol), "http://a",
"site should be sorted. 'a' should be third");
apply();
await observeAllPromise;
for (let URL of ["http://a", "http://z", "http://b"]) {
let uri = Services.io.newURI(URL);
Services.perms.remove(uri, "cookie");
}
}, [{ type: "cookie", origin: "http://a", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION },
{ type: "cookie", origin: "http://z", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION },
{ type: "cookie", origin: "http://b", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }]);
});
async function runTest(test, observances) {
registerCleanupFunction(function() {
Services.prefs.clearUserPref("privacy.history.custom");
});
await openPreferencesViaOpenPreferencesAPI("panePrivacy", {leaveOpen: true});
// eslint-disable-next-line mozilla/no-cpows-in-tests
let doc = gBrowser.contentDocument;
let historyMode = doc.getElementById("historyMode");
historyMode.value = "custom";
historyMode.doCommand();
let promiseSubDialogLoaded =
promiseLoadSubDialog("chrome://browser/content/preferences/permissions.xul");
doc.getElementById("cookieExceptions").doCommand();
let win = await promiseSubDialogLoaded;
doc = win.document;
let params = {
doc,
tree: doc.getElementById("permissionsTree"),
nameCol: doc.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(0),
statusCol: doc.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(1),
url: doc.getElementById("url"),
btnAllow: doc.getElementById("btnAllow"),
btnBlock: doc.getElementById("btnBlock"),
btnRemove: doc.getElementById("removePermission"),
allowText: win.gPermissionManager._getCapabilityString(
Ci.nsIPermissionManager.ALLOW_ACTION),
denyText: win.gPermissionManager._getCapabilityString(
Ci.nsIPermissionManager.DENY_ACTION),
allow: Ci.nsIPermissionManager.ALLOW_ACTION,
deny: Ci.nsIPermissionManager.DENY_ACTION,
};
let btnApplyChanges = doc.getElementById("btnApplyChanges");
let observeAllPromise = createObserveAllPromise(observances);
await test(params, observeAllPromise, () => btnApplyChanges.doCommand());
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
}
var testRunner = {
function createObserveAllPromise(observances) {
return new Promise(resolve => {
let permObserver = {
observe(aSubject, aTopic, aData) {
if (aTopic != "perm-changed")
return;
tests:
[
{
test(params) {
params.url.value = "test.com";
params.btnAllow.doCommand();
is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission text should be set correctly");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "http://test.com", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
},
{
test(params) {
params.url.value = "test.com";
params.btnBlock.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
"permission should change to deny in UI");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
capability: Ci.nsIPermissionManager.DENY_ACTION }],
},
{
test(params) {
params.url.value = "test.com";
params.btnAllow.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission should revert back to allow");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
},
{
test(params) {
params.url.value = "test.com";
params.btnRemove.doCommand();
is(params.tree.view.rowCount, 0, "exception should be removed");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "http://test.com", data: "deleted" }],
},
{
expectPermObservancesDuringTestFunction: true,
test(params) {
let uri = Services.io.newURI("http://test.com");
Services.perms.add(uri, "popup", Ci.nsIPermissionManager.DENY_ACTION);
is(params.tree.view.rowCount, 0, "adding unrelated permission should not change display");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "popup", origin: "http://test.com", data: "added",
capability: Ci.nsIPermissionManager.DENY_ACTION }],
cleanUp(params) {
let uri = Services.io.newURI("http://test.com");
Services.perms.remove(uri, "popup");
},
},
{
test(params) {
params.url.value = "https://test.com:12345";
params.btnAllow.doCommand();
is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission text should be set correctly");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "https://test.com:12345", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
},
{
test(params) {
params.url.value = "https://test.com:12345";
params.btnBlock.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
"permission should change to deny in UI");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "https://test.com:12345", data: "changed",
capability: Ci.nsIPermissionManager.DENY_ACTION }],
},
{
test(params) {
params.url.value = "https://test.com:12345";
params.btnAllow.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission should revert back to allow");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "https://test.com:12345", data: "changed",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
},
{
test(params) {
params.url.value = "https://test.com:12345";
params.btnRemove.doCommand();
is(params.tree.view.rowCount, 0, "exception should be removed");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "https://test.com:12345", data: "deleted" }],
},
{
test(params) {
params.url.value = "localhost:12345";
params.btnAllow.doCommand();
is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission text should be set correctly");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "http://localhost:12345", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
},
{
test(params) {
params.url.value = "localhost:12345";
params.btnBlock.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
"permission should change to deny in UI");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "http://localhost:12345", data: "changed",
capability: Ci.nsIPermissionManager.DENY_ACTION }],
},
{
test(params) {
params.url.value = "localhost:12345";
params.btnAllow.doCommand();
is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
"origin name should be set correctly");
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
"permission should revert back to allow");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "http://localhost:12345", data: "changed",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
},
{
test(params) {
params.url.value = "localhost:12345";
params.btnRemove.doCommand();
is(params.tree.view.rowCount, 0, "exception should be removed");
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "http://localhost:12345", data: "deleted" }],
},
{
expectPermObservancesDuringTestFunction: true,
test(params) {
for (let URL of ["http://a", "http://z", "http://b"]) {
let URI = Services.io.newURI(URL);
Services.perms.add(URI, "cookie", Ci.nsIPermissionManager.ALLOW_ACTION);
}
if (observances.length == 0) {
// Should fail here as we are not expecting a notification, but we
// don't. See bug 1063410.
return;
}
is(params.tree.view.rowCount, 3, "Three permissions should be present");
is(params.tree.view.getCellText(0, params.nameCol), "http://a",
"site should be sorted. 'a' should be first");
is(params.tree.view.getCellText(1, params.nameCol), "http://b",
"site should be sorted. 'b' should be second");
is(params.tree.view.getCellText(2, params.nameCol), "http://z",
"site should be sorted. 'z' should be third");
info(`observed perm-changed (remaining ${observances.length - 1})`);
// Sort descending then check results in cleanup since sorting isn't synchronous.
EventUtils.synthesizeMouseAtCenter(params.doc.getElementById("siteCol"), {},
params.doc.defaultView);
params.btnApplyChanges.doCommand();
},
observances: [{ type: "cookie", origin: "http://a", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION },
{ type: "cookie", origin: "http://z", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION },
{ type: "cookie", origin: "http://b", data: "added",
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
cleanUp(params) {
is(params.tree.view.getCellText(0, params.nameCol), "http://z",
"site should be sorted. 'z' should be first");
is(params.tree.view.getCellText(1, params.nameCol), "http://b",
"site should be sorted. 'b' should be second");
is(params.tree.view.getCellText(2, params.nameCol), "http://a",
"site should be sorted. 'a' should be third");
let permission = aSubject.QueryInterface(Ci.nsIPermission);
let expected = observances.shift();
for (let URL of ["http://a", "http://z", "http://b"]) {
let uri = Services.io.newURI(URL);
Services.perms.remove(uri, "cookie");
}
},
},
],
is(aData, expected.data, "type of message should be the same");
for (let prop of ["type", "capability"]) {
if (expected[prop])
is(permission[prop], expected[prop],
"property: \"" + prop + "\" should be equal");
}
_currentTest: -1,
if (expected.origin) {
is(permission.principal.origin, expected.origin,
"property: \"origin\" should be equal");
}
runTests() {
this._currentTest++;
info("Running test #" + (this._currentTest + 1) + "\n");
let that = this;
let p = this.runCurrentTest(this._currentTest + 1);
p.then(function() {
if (that._currentTest == that.tests.length - 1) {
finish();
} else {
that.runTests();
if (observances.length == 0) {
Services.obs.removeObserver(permObserver, "perm-changed");
executeSoon(resolve);
}
}
});
},
runCurrentTest(testNumber) {
return new Promise(function(resolve, reject) {
let helperFunctions = {
windowLoad(win) {
let doc = win.document;
let params = {
doc,
tree: doc.getElementById("permissionsTree"),
nameCol: doc.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(0),
statusCol: doc.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(1),
url: doc.getElementById("url"),
btnAllow: doc.getElementById("btnAllow"),
btnBlock: doc.getElementById("btnBlock"),
btnApplyChanges: doc.getElementById("btnApplyChanges"),
btnRemove: doc.getElementById("removePermission"),
allowText: win.gPermissionManager._getCapabilityString(
Ci.nsIPermissionManager.ALLOW_ACTION),
denyText: win.gPermissionManager._getCapabilityString(
Ci.nsIPermissionManager.DENY_ACTION),
allow: Ci.nsIPermissionManager.ALLOW_ACTION,
deny: Ci.nsIPermissionManager.DENY_ACTION,
};
let permObserver = {
observe(aSubject, aTopic, aData) {
if (aTopic != "perm-changed")
return;
if (testRunner.tests[testRunner._currentTest].observances.length == 0) {
// Should fail here as we are not expecting a notification, but we don't.
// See bug 1063410.
return;
}
let permission = aSubject.QueryInterface(Ci.nsIPermission);
let expected = testRunner.tests[testRunner._currentTest].observances.shift();
is(aData, expected.data, "type of message should be the same");
for (let prop of ["type", "capability"]) {
if (expected[prop])
is(permission[prop], expected[prop],
"property: \"" + prop + "\" should be equal");
}
if (expected.origin) {
is(permission.principal.origin, expected.origin,
"property: \"origin\" should be equal");
}
Services.obs.removeObserver(permObserver, "perm-changed");
let testCase = testRunner.tests[testRunner._currentTest];
if (!testCase.expectPermObservancesDuringTestFunction) {
if (testCase.cleanUp) {
testCase.cleanUp(params);
}
gBrowser.removeCurrentTab();
resolve();
}
},
};
Services.obs.addObserver(permObserver, "perm-changed");
if (testRunner._currentTest == 0) {
is(params.tree.view.rowCount, 0, "no cookie exceptions");
}
try {
let testCase = testRunner.tests[testRunner._currentTest];
testCase.test(params);
if (testCase.expectPermObservancesDuringTestFunction) {
if (testCase.cleanUp) {
testCase.cleanUp(params);
}
gBrowser.removeCurrentTab();
resolve();
}
} catch (ex) {
ok(false, "exception while running test #" +
testNumber + ": " + ex);
}
},
};
registerCleanupFunction(function() {
Services.prefs.clearUserPref("privacy.history.custom");
});
openPreferencesViaOpenPreferencesAPI("panePrivacy", {leaveOpen: true}).then(function() {
// eslint-disable-next-line mozilla/no-cpows-in-tests
let doc = gBrowser.contentDocument;
let historyMode = doc.getElementById("historyMode");
historyMode.value = "custom";
historyMode.doCommand();
let promiseSubDialogLoaded =
promiseLoadSubDialog("chrome://browser/content/preferences/permissions.xul");
doc.getElementById("cookieExceptions").doCommand();
promiseSubDialogLoaded.then(function(win) {
helperFunctions.windowLoad(win);
});
});
});
},
};
};
Services.obs.addObserver(permObserver, "perm-changed");
});
}

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

@ -21,7 +21,13 @@ function open_subdialog_and_test_generic_start_state(browser, domcontentloadedFn
let subdialog = content.gSubDialog._topDialog;
info("waiting for subdialog DOMFrameContentLoaded");
await ContentTaskUtils.waitForEvent(win, "DOMFrameContentLoaded", true);
let dialogOpenPromise;
await new Promise(resolve => {
win.addEventListener("DOMFrameContentLoaded", () => {
dialogOpenPromise = ContentTaskUtils.waitForEvent(subdialog._overlay, "dialogopen");
resolve();
}, { once: true, capture: true });
});
let result;
if (args.domcontentloadedFnStr) {
// eslint-disable-next-line no-eval
@ -29,7 +35,7 @@ function open_subdialog_and_test_generic_start_state(browser, domcontentloadedFn
}
info("waiting for subdialog load");
await ContentTaskUtils.waitForEvent(subdialog._overlay, "dialogopen");
await dialogOpenPromise;
info("subdialog window is loaded");
let expectedStyleSheetURLs = subdialog._injectedStyleSheets.slice(0);

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

@ -67,7 +67,9 @@ function promiseLoadSubDialog(aURL) {
}
is(expectedStyleSheetURLs.length, 0, "All expectedStyleSheetURLs should have been found");
resolve(aEvent.detail.dialog._frame.contentWindow);
// Wait for the next event tick to make sure the remaining part of the
// testcase runs after the dialog gets ready for input.
executeSoon(() => resolve(aEvent.detail.dialog._frame.contentWindow));
});
});
}

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

@ -42,7 +42,7 @@ function synthesizeNativeMouseMove(aElement) {
return new Promise((resolve, reject) => {
function eventOccurred(e) {
aElement.removeEventListener("mousemove", eventOccurred, true);
resolve();
SimpleTest.executeSoon(resolve);
}
aElement.addEventListener("mousemove", eventOccurred, true);

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

@ -14,6 +14,24 @@ let firstPaintNotification = "widget-first-paint";
if (AppConstants.platform == "linux")
firstPaintNotification = "xul-window-visible";
let win, canvas;
let paints = [];
let afterPaintListener = () => {
let width, height;
canvas.width = width = win.innerWidth;
canvas.height = height = win.innerHeight;
if (width < 1 || height < 1)
return;
let ctx = canvas.getContext("2d", {alpha: false, willReadFrequently: true});
ctx.drawWindow(win, 0, 0, width, height, "white",
ctx.DRAWWINDOW_DO_NOT_FLUSH | ctx.DRAWWINDOW_DRAW_VIEW |
ctx.DRAWWINDOW_ASYNC_DECODE_IMAGES |
ctx.DRAWWINDOW_USE_WIDGET_LAYERS);
paints.push({data: ctx.getImageData(0, 0, width, height).data,
width, height});
};
/**
* The startupRecorder component observes notifications at various stages of
* startup and records the set of JS components and modules that were already
@ -83,6 +101,16 @@ startupRecorder.prototype = {
Services.obs.removeObserver(this, topic);
if (topic == firstPaintNotification &&
Services.prefs.getBoolPref("browser.startup.record", false)) {
win = Services.wm.getMostRecentWindow("navigator:browser");
canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml",
"canvas");
canvas.mozOpaque = true;
afterPaintListener();
win.addEventListener("MozAfterPaint", afterPaintListener);
}
if (topic == "sessionstore-windows-restored") {
if (!Services.prefs.getBoolPref("browser.startup.record", false)) {
this._resolve();
@ -107,6 +135,10 @@ startupRecorder.prototype = {
this.record("before becoming idle");
Services.obs.removeObserver(this, "image-drawing");
Services.obs.removeObserver(this, "image-loading");
win.removeEventListener("MozAfterPaint", afterPaintListener);
win = null;
this.data.frames = paints;
paints = null;
this._resolve();
this._resolve = null;
});

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

@ -32,6 +32,8 @@ build-clang.py accepts a JSON config format with the following fields:
* stages: Use 1, 2, or 3 to select different compiler stages. The default is 3.
* llvm_repo: SVN path to the LLVM repo.
* clang_repo: SVN path to the Clang repo.
* extra_repo: SVN path to the clang-tools-extra repo.
* lld_repo: SVN path to the lld repo.
* compiler_repo: SVN path to the compiler-rt repo.
* libcxx_repo: SVN path to the libcxx repo.
* libcxxabi_repo: SVN path to the libcxxabi repo.

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

@ -343,6 +343,7 @@ if __name__ == "__main__":
llvm_source_dir = source_dir + "/llvm"
clang_source_dir = source_dir + "/clang"
extra_source_dir = source_dir + "/extra"
lld_source_dir = source_dir + "/lld"
compiler_rt_source_dir = source_dir + "/compiler-rt"
libcxx_source_dir = source_dir + "/libcxx"
libcxxabi_source_dir = source_dir + "/libcxxabi"
@ -379,6 +380,7 @@ if __name__ == "__main__":
llvm_repo = config["llvm_repo"]
clang_repo = config["clang_repo"]
extra_repo = config.get("extra_repo")
lld_repo = config.get("lld_repo")
compiler_repo = config["compiler_repo"]
libcxx_repo = config["libcxx_repo"]
libcxxabi_repo = config.get("libcxxabi_repo")
@ -449,6 +451,8 @@ if __name__ == "__main__":
checkout_or_update(clang_repo, clang_source_dir)
checkout_or_update(compiler_repo, compiler_rt_source_dir)
checkout_or_update(libcxx_repo, libcxx_source_dir)
if lld_repo:
checkout_or_update(lld_repo, lld_source_dir)
if libcxxabi_repo:
checkout_or_update(libcxxabi_repo, libcxxabi_source_dir)
if extra_repo:
@ -460,6 +464,8 @@ if __name__ == "__main__":
llvm_source_dir + "/tools/clang"),
(extra_source_dir,
llvm_source_dir + "/tools/clang/tools/extra"),
(lld_source_dir,
llvm_source_dir + "/tools/lld"),
(compiler_rt_source_dir,
llvm_source_dir + "/projects/compiler-rt"),
(libcxx_source_dir,

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

@ -18,6 +18,8 @@
"r315677.patch",
"r316048.patch",
"r317705.patch",
"r317709.patch"
"r317709.patch",
"hide-gcda-profiling-symbols.patch",
"fflush-before-unlocking.patch"
]
}

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

@ -0,0 +1,12 @@
diff --git a/compiler-rt/lib/profile/GCDAProfiling.c b/compiler-rt/lib/profile/GCDAProfiling.c
index f0c05075a..11b1c4557 100644
--- a/compiler-rt/lib/profile/GCDAProfiling.c
+++ b/compiler-rt/lib/profile/GCDAProfiling.c
@@ -468,6 +468,7 @@ void llvm_gcda_end_file() {
unmap_file();
}
+ fflush(output_file);
flock(fd, LOCK_UN);
fclose(output_file);
output_file = NULL;

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

@ -9,7 +9,6 @@
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/SubtleCryptoBinding.h"
#include "mozilla/dom/WebCryptoTask.h"
#include "mozilla/Telemetry.h"
namespace mozilla {
namespace dom {
@ -26,7 +25,6 @@ NS_INTERFACE_MAP_END
SubtleCrypto::SubtleCrypto(nsIGlobalObject* aParent)
: mParent(aParent)
, mRecordedTelemetry(false)
{
}
@ -36,25 +34,12 @@ SubtleCrypto::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return SubtleCryptoBinding::Wrap(aCx, this, aGivenProto);
}
void
SubtleCrypto::RecordTelemetryOnce() {
if (mRecordedTelemetry) {
return;
}
mRecordedTelemetry = true;
JSObject* global = mParent->GetGlobalJSObject();
bool isSecure = JS_GetIsSecureContext(js::GetObjectCompartment(global));
Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD_SECURE, isSecure);
}
#define SUBTLECRYPTO_METHOD_BODY(Operation, aRv, ...) \
MOZ_ASSERT(mParent); \
RefPtr<Promise> p = Promise::Create(mParent, aRv); \
if (aRv.Failed()) { \
return nullptr; \
} \
RecordTelemetryOnce(); \
RefPtr<WebCryptoTask> task = \
WebCryptoTask::Create ## Operation ## Task(__VA_ARGS__); \
if (!task) { \

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

@ -119,10 +119,7 @@ public:
ErrorResult& aRv);
private:
void RecordTelemetryOnce();
nsCOMPtr<nsIGlobalObject> mParent;
bool mRecordedTelemetry;
};
} // namespace dom

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

@ -49,6 +49,7 @@
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ShadowRoot.h"
#include "nsLayoutUtils.h"
#include "mozilla/ScopeExit.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -92,8 +93,8 @@ protected:
int32_t aDepth);
nsresult SerializeRangeContextStart(const nsTArray<nsINode*>& aAncestorArray,
nsAString& aString);
nsresult SerializeRangeContextEnd(const nsTArray<nsINode*>& aAncestorArray,
nsAString& aString);
nsresult SerializeRangeContextEnd(nsAString& aString);
virtual int32_t
GetImmediateContextCount(const nsTArray<nsINode*>& aAncestorArray)
{
@ -185,6 +186,7 @@ protected:
AutoTArray<int32_t, 8> mStartOffsets;
AutoTArray<nsIContent*, 8> mEndNodes;
AutoTArray<int32_t, 8> mEndOffsets;
AutoTArray<AutoTArray<nsINode*, 8>, 8> mRangeContexts;
// Whether the serializer cares about being notified to scan elements to
// keep track of whether they are preformatted. This stores the out
// argument of nsIContentSerializer::Init().
@ -874,6 +876,9 @@ nsDocumentEncoder::SerializeRangeContextStart(const nsTArray<nsINode*>& aAncesto
if (mDisableContextSerialize) {
return NS_OK;
}
AutoTArray<nsINode*, 8>* serializedContext = mRangeContexts.AppendElement();
int32_t i = aAncestorArray.Length(), j;
nsresult rv = NS_OK;
@ -889,7 +894,7 @@ nsDocumentEncoder::SerializeRangeContextStart(const nsTArray<nsINode*>& aAncesto
// Either a general inclusion or as immediate context
if (IncludeInContext(node) || i < j) {
rv = SerializeNodeStart(node, 0, -1, aString);
serializedContext->AppendElement(node);
if (NS_FAILED(rv))
break;
}
@ -899,34 +904,24 @@ nsDocumentEncoder::SerializeRangeContextStart(const nsTArray<nsINode*>& aAncesto
}
nsresult
nsDocumentEncoder::SerializeRangeContextEnd(const nsTArray<nsINode*>& aAncestorArray,
nsAString& aString)
nsDocumentEncoder::SerializeRangeContextEnd(nsAString& aString)
{
if (mDisableContextSerialize) {
return NS_OK;
}
int32_t i = 0, j;
int32_t count = aAncestorArray.Length();
MOZ_RELEASE_ASSERT(!mRangeContexts.IsEmpty(), "Tried to end context without starting one.");
AutoTArray<nsINode*, 8>& serializedContext = mRangeContexts.LastElement();
nsresult rv = NS_OK;
for (nsINode* node : Reversed(serializedContext)) {
rv = SerializeNodeEnd(node, aString);
// currently only for table-related elements
j = GetImmediateContextCount(aAncestorArray);
while (i < count) {
nsINode *node = aAncestorArray.ElementAt(i++);
if (!node)
if (NS_FAILED(rv))
break;
// Either a general inclusion or as immediate context
if (IncludeInContext(node) || i - 1 < j) {
rv = SerializeNodeEnd(node, aString);
if (NS_FAILED(rv))
break;
}
}
mRangeContexts.RemoveElementAt(mRangeContexts.Length() - 1);
return rv;
}
@ -992,7 +987,7 @@ nsDocumentEncoder::SerializeRangeToString(nsRange *aRange,
rv = SerializeRangeNodes(aRange, mCommonParent, aOutputString, 0);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = SerializeRangeContextEnd(mCommonAncestors, aOutputString);
rv = SerializeRangeContextEnd(aOutputString);
NS_ENSURE_SUCCESS(rv, rv);
return rv;
@ -1029,6 +1024,11 @@ NS_IMETHODIMP
nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
nsAString& aOutputString)
{
MOZ_ASSERT(mRangeContexts.IsEmpty(), "Re-entrant call to nsDocumentEncoder.");
auto rangeContextGuard = MakeScopeExit([&] {
mRangeContexts.Clear();
});
if (!mDocument)
return NS_ERROR_NOT_INITIALIZED;
@ -1112,10 +1112,8 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
prevNode = node;
} else if (prevNode) {
// Went from a <tr> to a non-<tr>
mCommonAncestors.Clear();
nsContentUtils::GetAncestors(p->GetParentNode(), mCommonAncestors);
mDisableContextSerialize = false;
rv = SerializeRangeContextEnd(mCommonAncestors, output);
rv = SerializeRangeContextEnd(output);
NS_ENSURE_SUCCESS(rv, rv);
prevNode = nullptr;
}
@ -1134,10 +1132,8 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
nsCOMPtr<nsINode> p = do_QueryInterface(prevNode);
rv = SerializeNodeEnd(p, output);
NS_ENSURE_SUCCESS(rv, rv);
mCommonAncestors.Clear();
nsContentUtils::GetAncestors(p->GetParentNode(), mCommonAncestors);
mDisableContextSerialize = false;
rv = SerializeRangeContextEnd(mCommonAncestors, output);
rv = SerializeRangeContextEnd(output);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1196,6 +1192,10 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
NS_IMETHODIMP
nsDocumentEncoder::EncodeToStream(nsIOutputStream* aStream)
{
MOZ_ASSERT(mRangeContexts.IsEmpty(), "Re-entrant call to nsDocumentEncoder.");
auto rangeContextGuard = MakeScopeExit([&] {
mRangeContexts.Clear();
});
nsresult rv = NS_OK;
if (!mDocument)

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

@ -388,6 +388,7 @@ nsPlainTextSerializer::ScanElementForPreformat(Element* aElement)
NS_IMETHODIMP
nsPlainTextSerializer::ForgetElementForPreformat(Element* aElement)
{
MOZ_RELEASE_ASSERT(!mPreformatStack.empty(), "Tried to pop without previous push.");
mPreformatStack.pop();
return NS_OK;
}

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

@ -175,6 +175,7 @@ nsStaticAtom** const kAttributesHTML[] = {
&nsGkAtoms::contextmenu,
&nsGkAtoms::controls,
&nsGkAtoms::coords,
&nsGkAtoms::crossorigin,
&nsGkAtoms::datetime,
&nsGkAtoms::dir,
&nsGkAtoms::disabled,
@ -191,6 +192,7 @@ nsStaticAtom** const kAttributesHTML[] = {
&nsGkAtoms::hreflang,
&nsGkAtoms::icon,
&nsGkAtoms::id,
&nsGkAtoms::integrity,
&nsGkAtoms::ismap,
&nsGkAtoms::itemid,
&nsGkAtoms::itemprop,

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

@ -2,67 +2,43 @@
* Mixed Content Block frame navigates for target="_top" - Test for Bug 902350
*/
add_task(async function mixed_content_block_for_target_top_test() {
const PREF_ACTIVE = "security.mixed_content.block_active_content";
const httpsTestRoot = getRootDirectory(gTestPath)
.replace("chrome://mochitests/content", "https://example.com");
const PREF_ACTIVE = "security.mixed_content.block_active_content";
const gHttpTestRoot = "https://example.com/browser/dom/base/test/";
var origBlockActive;
var gTestBrowser = null;
await SpecialPowers.pushPrefEnv({ set: [[ PREF_ACTIVE, true ]] });
registerCleanupFunction(function() {
// Set preferences back to their original values
Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
});
let newTab = await BrowserTestUtils.openNewForegroundTab({ gBrowser,
waitForLoad: true });
let testBrowser = newTab.linkedBrowser;
function MixedTestsCompleted() {
gBrowser.removeCurrentTab();
window.focus();
finish();
}
var url = httpsTestRoot + "file_bug902350.html";
var frameUrl = httpsTestRoot + "file_bug902350_frame.html";
let loadPromise = BrowserTestUtils.browserLoaded(testBrowser, false, url);
let frameLoadPromise = BrowserTestUtils.browserLoaded(testBrowser, true,
frameUrl);
testBrowser.loadURI(url);
await loadPromise;
await frameLoadPromise;
function test() {
waitForExplicitFinish();
origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE);
Services.prefs.setBoolPref(PREF_ACTIVE, true);
var newTab = BrowserTestUtils.addTab(gBrowser);
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser;
BrowserTestUtils.browserLoaded(gTestBrowser).then(() => {
// about:blank is expected to be loaded here.
var url = gHttpTestRoot + "file_bug902350.html";
BrowserTestUtils.browserLoaded(gTestBrowser, true /*includeSubFrames*/).then(MixedTest1A);
gTestBrowser.loadURI(url);
});
}
// Need to capture 2 loads, one for the main page and one for the iframe
function MixedTest1A() {
BrowserTestUtils.browserLoaded(gTestBrowser, true /*includeSubFrames*/).then(MixedTest1B);
}
// Find the iframe and click the link in it
function MixedTest1B() {
BrowserTestUtils.browserLoaded(gTestBrowser).then(MixedTest1C);
ContentTask.spawn(gTestBrowser, null, function() {
// Find the iframe and click the link in it.
let insecureUrl = "http://example.com/";
let insecureLoadPromise = BrowserTestUtils.browserLoaded(testBrowser, false,
insecureUrl);
ContentTask.spawn(testBrowser, null, function() {
var frame = content.document.getElementById("testing_frame");
var topTarget = frame.contentWindow.document.getElementById("topTarget");
topTarget.click();
});
// The link click should have caused a load and should not invoke the Mixed Content Blocker
let {gIdentityHandler} = gTestBrowser.ownerGlobal;
// Navigating to insecure domain through target='_top' should succeed.
await insecureLoadPromise;
// The link click should not invoke the Mixed Content Blocker.
let {gIdentityHandler} = testBrowser.ownerGlobal;
ok (!gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
"Mixed Content Doorhanger did not appear when trying to navigate top");
}
function MixedTest1C() {
ContentTask.spawn(gTestBrowser, null, function() {
Assert.equal(content.location.href, "http://example.com/",
"Navigating to insecure domain through target='_top' failed.")
}).then(MixedTestsCompleted);
}
await BrowserTestUtils.removeTab(newTab);
});

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

@ -37,5 +37,12 @@ DeallocClientManagerParent(PClientManagerParent* aActor)
return true;
}
void
InitClientManagerParent(PClientManagerParent* aActor)
{
auto actor = static_cast<ClientManagerParent*>(aActor);
actor->Init();
}
} // namespace dom
} // namespace mozilla

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

@ -24,6 +24,9 @@ AllocClientManagerParent();
bool
DeallocClientManagerParent(PClientManagerParent* aActor);
void
InitClientManagerParent(PClientManagerParent* aActor);
} // namespace dom
} // namespace mozilla

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

@ -117,6 +117,13 @@ ClientManagerParent::ClientManagerParent()
ClientManagerParent::~ClientManagerParent()
{
mService->RemoveManager(this);
}
void
ClientManagerParent::Init()
{
mService->AddManager(this);
}
} // namespace dom

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

@ -63,6 +63,9 @@ class ClientManagerParent final : public PClientManagerParent
public:
ClientManagerParent();
~ClientManagerParent();
void
Init();
};
} // namespace dom

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

@ -6,9 +6,13 @@
#include "ClientManagerService.h"
#include "ClientManagerParent.h"
#include "ClientSourceParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/SystemGroup.h"
#include "nsIAsyncShutdown.h"
namespace mozilla {
namespace dom {
@ -56,22 +60,137 @@ MatchPrincipalInfo(const PrincipalInfo& aLeft, const PrincipalInfo& aRight)
MOZ_CRASH("unexpected principal type!");
}
class ClientShutdownBlocker final : public nsIAsyncShutdownBlocker
{
RefPtr<GenericPromise::Private> mPromise;
~ClientShutdownBlocker() = default;
public:
explicit ClientShutdownBlocker(GenericPromise::Private* aPromise)
: mPromise(aPromise)
{
MOZ_DIAGNOSTIC_ASSERT(mPromise);
}
NS_IMETHOD
GetName(nsAString& aNameOut) override
{
aNameOut =
NS_LITERAL_STRING("ClientManagerService: start destroying IPC actors early");
return NS_OK;
}
NS_IMETHOD
BlockShutdown(nsIAsyncShutdownClient* aClient) override
{
mPromise->Resolve(true, __func__);
aClient->RemoveBlocker(this);
return NS_OK;
}
NS_IMETHOD
GetState(nsIPropertyBag**) override
{
return NS_OK;
}
NS_DECL_ISUPPORTS
};
NS_IMPL_ISUPPORTS(ClientShutdownBlocker, nsIAsyncShutdownBlocker)
// Helper function the resolves a MozPromise when we detect that the browser
// has begun to shutdown.
RefPtr<GenericPromise>
OnShutdown()
{
RefPtr<GenericPromise::Private> ref = new GenericPromise::Private(__func__);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction("ClientManagerServer::OnShutdown",
[ref] () {
nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
if (!svc) {
ref->Resolve(true, __func__);
return;
}
nsCOMPtr<nsIAsyncShutdownClient> phase;
MOZ_ALWAYS_SUCCEEDS(svc->GetXpcomWillShutdown(getter_AddRefs(phase)));
if (!phase) {
ref->Resolve(true, __func__);
return;
}
nsCOMPtr<nsIAsyncShutdownBlocker> blocker = new ClientShutdownBlocker(ref);
nsresult rv =
phase->AddBlocker(blocker, NS_LITERAL_STRING(__FILE__), __LINE__,
NS_LITERAL_STRING("ClientManagerService shutdown"));
if (NS_FAILED(rv)) {
ref->Resolve(true, __func__);
return;
}
});
MOZ_ALWAYS_SUCCEEDS(
SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
return ref.forget();
}
} // anonymous namespace
ClientManagerService::ClientManagerService()
: mShutdown(false)
{
AssertIsOnBackgroundThread();
// While the ClientManagerService will be gracefully terminated as windows
// and workers are naturally killed, this can cause us to do extra work
// relatively late in the shutdown process. To avoid this we eagerly begin
// shutdown at the first sign it has begun. Since we handle normal shutdown
// gracefully we don't really need to block anything here. We just begin
// destroying our IPC actors immediately.
OnShutdown()->Then(GetCurrentThreadSerialEventTarget(), __func__,
[] () {
RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
if (svc) {
svc->Shutdown();
}
});
}
ClientManagerService::~ClientManagerService()
{
AssertIsOnBackgroundThread();
MOZ_DIAGNOSTIC_ASSERT(mSourceTable.Count() == 0);
MOZ_DIAGNOSTIC_ASSERT(mManagerList.IsEmpty());
MOZ_DIAGNOSTIC_ASSERT(sClientManagerServiceInstance == this);
sClientManagerServiceInstance = nullptr;
}
void
ClientManagerService::Shutdown()
{
AssertIsOnBackgroundThread();
// If many ClientManagerService are created and destroyed quickly we can
// in theory get more than one shutdown listener calling us.
if (mShutdown) {
return;
}
mShutdown = true;
// Begin destroying our various manager actors which will in turn destroy
// all source, handle, and operation actors.
AutoTArray<ClientManagerParent*, 16> list(mManagerList);
for (auto actor : list) {
Unused << PClientManagerParent::Send__delete__(actor);
}
}
// static
already_AddRefed<ClientManagerService>
ClientManagerService::GetOrCreateInstance()
@ -86,6 +205,20 @@ ClientManagerService::GetOrCreateInstance()
return ref.forget();
}
// static
already_AddRefed<ClientManagerService>
ClientManagerService::GetInstance()
{
AssertIsOnBackgroundThread();
if (!sClientManagerServiceInstance) {
return nullptr;
}
RefPtr<ClientManagerService> ref(sClientManagerServiceInstance);
return ref.forget();
}
bool
ClientManagerService::AddSource(ClientSourceParent* aSource)
{
@ -134,5 +267,28 @@ ClientManagerService::FindSource(const nsID& aID, const PrincipalInfo& aPrincipa
return source;
}
void
ClientManagerService::AddManager(ClientManagerParent* aManager)
{
AssertIsOnBackgroundThread();
MOZ_DIAGNOSTIC_ASSERT(aManager);
MOZ_ASSERT(!mManagerList.Contains(aManager));
mManagerList.AppendElement(aManager);
// If shutdown has already begun then immediately destroy the actor.
if (mShutdown) {
Unused << PClientManagerParent::Send__delete__(aManager);
}
}
void
ClientManagerService::RemoveManager(ClientManagerParent* aManager)
{
AssertIsOnBackgroundThread();
MOZ_DIAGNOSTIC_ASSERT(aManager);
DebugOnly<bool> removed = mManagerList.RemoveElement(aManager);
MOZ_ASSERT(removed);
}
} // namespace dom
} // namespace mozilla

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

@ -7,12 +7,14 @@
#define _mozilla_dom_ClientManagerService_h
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "mozilla/MozPromise.h"
#include "nsDataHashtable.h"
namespace mozilla {
namespace dom {
class ClientManagerParent;
class ClientSourceParent;
// Define a singleton service to manage client activity throughout the
@ -24,13 +26,24 @@ class ClientManagerService final
// optimize for insertion, removal, and lookup by UUID.
nsDataHashtable<nsIDHashKey, ClientSourceParent*> mSourceTable;
nsTArray<ClientManagerParent*> mManagerList;
bool mShutdown;
ClientManagerService();
~ClientManagerService();
void
Shutdown();
public:
static already_AddRefed<ClientManagerService>
GetOrCreateInstance();
// Returns nullptr if the service is not already created.
static already_AddRefed<ClientManagerService>
GetInstance();
bool
AddSource(ClientSourceParent* aSource);
@ -41,6 +54,12 @@ public:
FindSource(const nsID& aID,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
void
AddManager(ClientManagerParent* aManager);
void
RemoveManager(ClientManagerParent* aManager);
NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManagerService)
};

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

@ -23,6 +23,7 @@
#include "nsContentUtils.h"
#include "nsError.h"
#include "nsHostObjectURI.h"
#include "nsIAsyncShutdown.h"
#include "nsIMemoryReporter.h"
#include "nsIPrincipal.h"
#include "nsIUUIDGenerator.h"
@ -437,6 +438,7 @@ NS_IMPL_ISUPPORTS(BlobURLsReporter, nsIMemoryReporter)
class ReleasingTimerHolder final : public nsITimerCallback
, public nsINamed
, public nsIAsyncShutdownBlocker
{
public:
NS_DECL_ISUPPORTS
@ -444,27 +446,98 @@ public:
static void
Create(const nsACString& aURI, bool aBroadcastToOtherProcesses)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<ReleasingTimerHolder> holder =
new ReleasingTimerHolder(aURI, aBroadcastToOtherProcesses);
auto raii = mozilla::MakeScopeExit([&] {
holder->CancelTimerAndRevokeURI();
});
nsresult rv = NS_NewTimerWithCallback(getter_AddRefs(holder->mTimer),
holder, RELEASING_TIMER,
nsITimer::TYPE_ONE_SHOT,
SystemGroup::EventTargetFor(TaskCategory::Other));
NS_ENSURE_SUCCESS_VOID(rv);
nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
NS_ENSURE_TRUE_VOID(!!phase);
rv = phase->AddBlocker(holder, NS_LITERAL_STRING(__FILE__), __LINE__,
NS_LITERAL_STRING("ReleasingTimerHolder shutdown"));
NS_ENSURE_SUCCESS_VOID(rv);
raii.release();
}
// nsITimerCallback interface
NS_IMETHOD
Notify(nsITimer* aTimer) override
{
RevokeURI(mBroadcastToOtherProcesses);
return NS_OK;
}
// nsINamed interface
NS_IMETHOD
GetName(nsACString& aName) override
{
aName.AssignLiteral("ReleasingTimerHolder");
return NS_OK;
}
// nsIAsyncShutdownBlocker interface
NS_IMETHOD
GetName(nsAString& aName) override
{
aName.AssignLiteral("ReleasingTimerHolder");
return NS_OK;
}
NS_IMETHOD
BlockShutdown(nsIAsyncShutdownClient* aClient) override
{
CancelTimerAndRevokeURI();
return NS_OK;
}
NS_IMETHOD
GetState(nsIPropertyBag**) override
{
return NS_OK;
}
private:
ReleasingTimerHolder(const nsACString& aURI, bool aBroadcastToOtherProcesses)
: mURI(aURI)
, mBroadcastToOtherProcesses(aBroadcastToOtherProcesses)
{}
~ReleasingTimerHolder()
{}
void
RevokeURI(bool aBroadcastToOtherProcesses)
{
// Remove the shutting down blocker
nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
if (phase) {
phase->RemoveBlocker(this);
}
// If we have to broadcast the unregistration, let's do it now.
if (mBroadcastToOtherProcesses) {
if (aBroadcastToOtherProcesses) {
BroadcastBlobURLUnregistration(mURI);
}
DataInfo* info = GetDataInfo(mURI, true /* We care about revoked dataInfo */);
if (!info) {
// Already gone!
return NS_OK;
return;
}
MOZ_ASSERT(info->mRevoked);
@ -481,25 +554,31 @@ public:
delete gDataTable;
gDataTable = nullptr;
}
return NS_OK;
}
NS_IMETHOD
GetName(nsACString& aName) override
void
CancelTimerAndRevokeURI()
{
aName.AssignLiteral("ReleasingTimerHolder");
return NS_OK;
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
RevokeURI(false /* aBroadcastToOtherProcesses */);
}
private:
ReleasingTimerHolder(const nsACString& aURI, bool aBroadcastToOtherProcesses)
: mURI(aURI)
, mBroadcastToOtherProcesses(aBroadcastToOtherProcesses)
{}
static nsCOMPtr<nsIAsyncShutdownClient>
GetShutdownPhase()
{
nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
NS_ENSURE_TRUE(!!svc, nullptr);
~ReleasingTimerHolder()
{}
nsCOMPtr<nsIAsyncShutdownClient> phase;
nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(phase));
NS_ENSURE_SUCCESS(rv, nullptr);
return Move(phase);
}
nsCString mURI;
bool mBroadcastToOtherProcesses;
@ -507,7 +586,8 @@ private:
nsCOMPtr<nsITimer> mTimer;
};
NS_IMPL_ISUPPORTS(ReleasingTimerHolder, nsITimerCallback, nsINamed)
NS_IMPL_ISUPPORTS(ReleasingTimerHolder, nsITimerCallback, nsINamed,
nsIAsyncShutdownBlocker)
} // namespace mozilla

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

@ -5237,11 +5237,14 @@ public:
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (!mElement) {
return;
}
mElement->NotifyMediaStreamTracksAvailable(aStream);
}
private:
HTMLMediaElement* mElement;
WeakPtr<HTMLMediaElement> mElement;
};
class HTMLMediaElement::MediaStreamTrackListener :

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

@ -155,7 +155,9 @@ HTMLOutputElement::SetDefaultValue(const nsAString& aDefaultValue, ErrorResult&
{
mDefaultValue = aDefaultValue;
if (mValueModeFlag == eModeDefault) {
aRv = nsContentUtils::SetNodeTextContent(this, mDefaultValue, true);
// We can't pass mDefaultValue, because it'll be truncated when
// the element's descendants are changed.
aRv = nsContentUtils::SetNodeTextContent(this, aDefaultValue, true);
}
}

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

@ -323,8 +323,7 @@ VideoData::CreateAndCopyData(const VideoInfo& aInfo,
// We disable this code path on Windows version earlier of Windows 8 due to
// intermittent crashes with old drivers. See bug 1405110.
if (IsWin8OrLater() && !XRE_IsParentProcess() &&
aAllocator && aAllocator->GetCompositorBackendType()
== layers::LayersBackend::LAYERS_D3D11) {
aAllocator && aAllocator->SupportsD3D11()) {
RefPtr<layers::D3D11YCbCrImage> d3d11Image = new layers::D3D11YCbCrImage();
PlanarYCbCrData data = ConstructPlanarYCbCrData(aInfo, aBuffer, aPicture);
if (d3d11Image->SetData(layers::ImageBridgeChild::GetSingleton()

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

@ -532,11 +532,7 @@ WMFVideoMFTManager::InitializeDXVA()
return false;
}
MOZ_ASSERT(!mDXVA2Manager);
LayersBackend backend = GetCompositorBackendType(mKnowsCompositor);
bool useANGLE =
mKnowsCompositor ? mKnowsCompositor->GetCompositorUseANGLE() : false;
bool wrWithANGLE = (backend == LayersBackend::LAYERS_WR) && useANGLE;
if (backend != LayersBackend::LAYERS_D3D11 && !wrWithANGLE) {
if (!mKnowsCompositor || !mKnowsCompositor->SupportsD3D11()) {
mDXVAFailureReason.AssignLiteral("Unsupported layers backend");
return false;
}

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

@ -21,11 +21,11 @@ using namespace mozilla::dom;
NS_IMETHODIMP
ScriptElement::ScriptAvailable(nsresult aResult,
nsIScriptElement* aElement,
bool aIsInline,
bool aIsInlineClassicScript,
nsIURI* aURI,
int32_t aLineNo)
{
if (!aIsInline && NS_FAILED(aResult)) {
if (!aIsInlineClassicScript && NS_FAILED(aResult)) {
nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser);
if (parser) {
parser->PushDefinedInsertionPoint();

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

@ -69,7 +69,9 @@ public:
void FireScriptAvailable(nsresult aResult)
{
mElement->ScriptAvailable(aResult, mElement, mIsInline, mURI, mLineNo);
bool isInlineClassicScript = mIsInline && !IsModuleRequest();
mElement->ScriptAvailable(aResult, mElement, isInlineClassicScript, mURI,
mLineNo);
}
void FireScriptEvaluated(nsresult aResult)
{

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

@ -1860,6 +1860,8 @@ ScriptLoader::GetScriptSource(ScriptLoadRequest* aRequest, nsAutoString& inlineD
nsresult
ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest)
{
LOG(("ScriptLoadRequest (%p): Process request", aRequest));
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"Processing requests when running scripts is unsafe.");
NS_ASSERTION(aRequest->IsReadyToRun(),
@ -1871,6 +1873,7 @@ ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest)
!aRequest->AsModuleRequest()->mModuleScript)
{
// There was an error fetching a module script. Nothing to do here.
LOG(("ScriptLoadRequest (%p): Error loading request, firing error", aRequest));
FireScriptAvailable(NS_ERROR_FAILURE, aRequest);
return NS_OK;
}
@ -2892,7 +2895,7 @@ ScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
MOZ_ASSERT(!modReq->IsTopLevel());
MOZ_ASSERT(!modReq->isInList());
modReq->Cancel();
FireScriptAvailable(rv, aRequest);
// A single error is fired for the top level module.
} else if (mParserBlockingRequest == aRequest) {
MOZ_ASSERT(!aRequest->isInList());
mParserBlockingRequest = nullptr;

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

@ -10,7 +10,7 @@ interface nsIURI;
[scriptable, uuid(7b787204-76fb-4764-96f1-fb7a666db4f4)]
interface nsIScriptLoaderObserver : nsISupports {
/**
* The script is available for evaluation. For inline scripts, this
* method will be called synchronously. For externally loaded scripts,
@ -20,15 +20,16 @@ interface nsIScriptLoaderObserver : nsISupports {
* a script. If this is a failure code, script evaluation
* will not occur.
* @param aElement The element being processed.
* @param aIsInline Is this an inline script or externally loaded?
* @param aIsInline Is this an inline classic script (as opposed to an
* externally loaded classic script or module script)?
* @param aURI What is the URI of the script (the document URI if
* it is inline).
* @param aLineNo At what line does the script appear (generally 1
* if it is a loaded script).
*/
void scriptAvailable(in nsresult aResult,
void scriptAvailable(in nsresult aResult,
in nsIScriptElement aElement,
in boolean aIsInline,
in boolean aIsInlineClassicScript,
in nsIURI aURI,
in int32_t aLineNo);
@ -40,8 +41,7 @@ interface nsIScriptLoaderObserver : nsISupports {
* @param aElement The element being processed.
* @param aIsInline Is this an inline script or externally loaded?
*/
void scriptEvaluated(in nsresult aResult,
void scriptEvaluated(in nsresult aResult,
in nsIScriptElement aElement,
in boolean aIsInline);
};

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

@ -954,7 +954,7 @@ NS_IMPL_ISUPPORTS(txTransformNotifier,
NS_IMETHODIMP
txTransformNotifier::ScriptAvailable(nsresult aResult,
nsIScriptElement *aElement,
bool aIsInline,
bool aIsInlineClassicScript,
nsIURI *aURI,
int32_t aLineNo)
{

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

@ -558,6 +558,18 @@ public:
return true;
}
/**
* Indicates how many times the surface has been invalidated.
*/
virtual int32_t Invalidations() const {
return -1;
}
/**
* Increment the invalidation counter.
*/
virtual void Invalidate() { }
protected:
bool mIsMapped;
};

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

@ -477,8 +477,7 @@ ImageContainer::GetD3D11YCbCrRecycleAllocator(KnowsCompositor* aAllocator)
device = gfx::DeviceManagerDx::Get()->GetCompositorDevice();
}
LayersBackend backend = aAllocator->GetCompositorBackendType();
if (!device || backend != LayersBackend::LAYERS_D3D11) {
if (!device || !aAllocator->SupportsD3D11()) {
return nullptr;
}

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

@ -128,6 +128,7 @@ public:
, mStride(0)
, mMapCount(0)
, mHandleCount(0)
, mInvalidations(0)
, mFormat(SurfaceFormat::UNKNOWN)
, mClosed(false)
, mFinalized(false)
@ -248,6 +249,25 @@ public:
return mFinalized;
}
/**
* Indicates how many times the surface has been invalidated.
*/
int32_t Invalidations() const override
{
MutexAutoLock lock(mMutex);
return mInvalidations;
}
/**
* Increment the invalidation counter.
*/
void Invalidate() override
{
MutexAutoLock lock(mMutex);
++mInvalidations;
MOZ_ASSERT(mInvalidations >= 0);
}
/**
* While a HandleLock exists for the given surface, the shared memory handle
* cannot be released.
@ -314,6 +334,7 @@ private:
int32_t mStride;
int32_t mMapCount;
int32_t mHandleCount;
int32_t mInvalidations;
IntSize mSize;
RefPtr<SharedMemoryBasic> mBuf;
RefPtr<SharedMemoryBasic> mOldBuf;

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

@ -94,6 +94,12 @@ public:
return mTextureFactoryIdentifier.mSupportsComponentAlpha;
}
bool SupportsD3D11() const
{
return GetCompositorBackendType() == layers::LayersBackend::LAYERS_D3D11 ||
(GetCompositorBackendType() == layers::LayersBackend::LAYERS_WR && GetCompositorUseANGLE());
}
bool GetCompositorUseANGLE() const
{
return mTextureFactoryIdentifier.mCompositorUseANGLE;

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

@ -22,23 +22,23 @@ class SharedSurfacesChild::ImageKeyData final
public:
ImageKeyData(WebRenderLayerManager* aManager,
const wr::ImageKey& aImageKey,
uint32_t aGenerationId)
int32_t aInvalidations)
: mManager(aManager)
, mImageKey(aImageKey)
, mGenerationId(aGenerationId)
, mInvalidations(aInvalidations)
{ }
ImageKeyData(ImageKeyData&& aOther)
: mManager(Move(aOther.mManager))
, mImageKey(aOther.mImageKey)
, mGenerationId(aOther.mGenerationId)
, mInvalidations(aOther.mInvalidations)
{ }
ImageKeyData& operator=(ImageKeyData&& aOther)
{
mManager = Move(aOther.mManager);
mImageKey = aOther.mImageKey;
mGenerationId = aOther.mGenerationId;
mInvalidations = aOther.mInvalidations;
return *this;
}
@ -47,7 +47,7 @@ public:
RefPtr<WebRenderLayerManager> mManager;
wr::ImageKey mImageKey;
uint32_t mGenerationId;
int32_t mInvalidations;
};
class SharedSurfacesChild::SharedUserData final
@ -117,7 +117,7 @@ public:
wr::ImageKey UpdateKey(WebRenderLayerManager* aManager,
wr::IpcResourceUpdateQueue& aResources,
uint32_t aGenerationId)
int32_t aInvalidations)
{
MOZ_ASSERT(aManager);
MOZ_ASSERT(!aManager->IsDestroyed());
@ -138,9 +138,9 @@ public:
mKeys.RemoveElementAt(i);
} else if (entry.mManager == aManager) {
found = true;
if (entry.mGenerationId != aGenerationId) {
if (entry.mInvalidations != aInvalidations) {
aManager->AddImageKeyForDiscard(entry.mImageKey);
entry.mGenerationId = aGenerationId;
entry.mInvalidations = aInvalidations;
entry.mImageKey = aManager->WrBridge()->GetNextImageKey();
aResources.AddExternalImage(mId, entry.mImageKey);
}
@ -150,7 +150,7 @@ public:
if (!found) {
key = aManager->WrBridge()->GetNextImageKey();
ImageKeyData data(aManager, key, aGenerationId);
ImageKeyData data(aManager, key, aInvalidations);
mKeys.AppendElement(Move(data));
aResources.AddExternalImage(mId, key);
}
@ -176,7 +176,6 @@ SharedSurfacesChild::DestroySharedUserData(void* aClosure)
SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface,
WebRenderLayerManager* aManager,
wr::IpcResourceUpdateQueue& aResources,
uint32_t aGenerationId,
wr::ImageKey& aKey)
{
MOZ_ASSERT(NS_IsMainThread());
@ -188,6 +187,12 @@ SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface,
return NS_ERROR_NOT_INITIALIZED;
}
// Each time the surface changes, the producers of SourceSurfaceSharedData
// surfaces promise to increment the invalidation counter each time the
// surface has changed. We can use this counter to determine whether or not
// we should upate our paired ImageKey.
int32_t invalidations = aSurface->Invalidations();
static UserDataKey sSharedKey;
SharedUserData* data =
static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey));
@ -200,7 +205,7 @@ SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface,
data->SetId(manager->GetNextExternalImageId());
} else if (data->IsShared()) {
// It has already been shared with the GPU process, reuse the id.
aKey = data->UpdateKey(aManager, aResources, aGenerationId);
aKey = data->UpdateKey(aManager, aResources, invalidations);
return NS_OK;
}
@ -218,7 +223,7 @@ SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface,
if (pid == base::GetCurrentProcId()) {
SharedSurfacesParent::AddSameProcess(data->Id(), aSurface);
data->MarkShared();
aKey = data->UpdateKey(aManager, aResources, aGenerationId);
aKey = data->UpdateKey(aManager, aResources, invalidations);
return NS_OK;
}
@ -253,7 +258,7 @@ SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface,
SurfaceDescriptorShared(aSurface->GetSize(),
aSurface->Stride(),
format, handle));
aKey = data->UpdateKey(aManager, aResources, aGenerationId);
aKey = data->UpdateKey(aManager, aResources, invalidations);
return NS_OK;
}
@ -271,9 +276,8 @@ SharedSurfacesChild::Share(ImageContainer* aContainer,
return NS_ERROR_NOT_IMPLEMENTED;
}
uint32_t generation = 0;
AutoTArray<ImageContainer::OwningImage,4> images;
aContainer->GetCurrentImages(&images, &generation);
aContainer->GetCurrentImages(&images);
if (images.IsEmpty()) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -288,8 +292,7 @@ SharedSurfacesChild::Share(ImageContainer* aContainer,
}
auto sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
return Share(sharedSurface, aManager, aResources,
generation, aKey);
return Share(sharedSurface, aManager, aResources, aKey);
}
/* static */ void

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

@ -33,7 +33,6 @@ public:
static nsresult Share(gfx::SourceSurfaceSharedData* aSurface,
WebRenderLayerManager* aManager,
wr::IpcResourceUpdateQueue& aResources,
uint32_t aGenerationId,
wr::ImageKey& aKey);
static nsresult Share(ImageContainer* aContainer,

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

@ -40,7 +40,8 @@ StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParen
// Compute scale for fallback rendering.
gfx::Matrix transform2d;
if (aBoundTransform && aBoundTransform->CanDraw2D(&transform2d)) {
mScale = transform2d.ScaleFactors(true) * aParentSC.mScale;
mInheritedTransform = transform2d * aParentSC.mInheritedTransform;
mScale = mInheritedTransform.ScaleFactors(true);
}
mBuilder->PushStackingContext(wr::LayoutRect(),

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

@ -68,12 +68,18 @@ public:
// Export the inherited scale
gfx::Size GetInheritedScale() const { return mScale; }
const gfx::Matrix& GetInheritedTransform() const
{
return mInheritedTransform;
}
bool IsBackfaceVisible() const { return mTransform.IsBackfaceVisible(); }
private:
wr::DisplayListBuilder* mBuilder;
gfx::Matrix4x4 mTransform;
gfx::Size mScale;
gfx::Matrix mInheritedTransform;
};
} // namespace layers

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

@ -1,24 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
#ifndef GFX_FONTCONFIG_FONTS_H
#define GFX_FONTCONFIG_FONTS_H
#include "cairo.h"
#include "gfxTypes.h"
#include "gfxTextRun.h"
#include "nsAutoRef.h"
#include "nsTArray.h"
#include <pango/pango.h>
class gfxFcFontSet;
class gfxFcFont;
typedef struct _FcPattern FcPattern;
typedef struct FT_FaceRec_* FT_Face;
typedef struct FT_LibraryRec_ *FT_Library;
#endif /* GFX_FONTCONFIG_FONTS_H */

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

@ -13,13 +13,13 @@
#include "nsUnicodeProperties.h"
#include "gfx2DGlue.h"
#include "gfxFcPlatformFontList.h"
#include "gfxFontconfigFonts.h"
#include "gfxConfig.h"
#include "gfxContext.h"
#include "gfxUserFontSet.h"
#include "gfxUtils.h"
#include "gfxFT2FontBase.h"
#include "gfxPrefs.h"
#include "gfxTextRun.h"
#include "VsyncSource.h"
#include "mozilla/Atomics.h"
#include "mozilla/Monitor.h"

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

@ -460,6 +460,7 @@ private:
DECL_GFX_PREF(Once, "gfx.direct3d11.enable-debug-layer", Direct3D11EnableDebugLayer, bool, false);
DECL_GFX_PREF(Once, "gfx.direct3d11.break-on-error", Direct3D11BreakOnError, bool, false);
DECL_GFX_PREF(Once, "gfx.direct3d11.sleep-on-create-device", Direct3D11SleepOnCreateDevice, int32_t, 0);
DECL_GFX_PREF(Live, "gfx.downloadable_fonts.keep_color_bitmaps", KeepColorBitmaps, bool, false);
DECL_GFX_PREF(Live, "gfx.downloadable_fonts.keep_variation_tables", KeepVariationTables, bool, false);
DECL_GFX_PREF(Live, "gfx.downloadable_fonts.otl_validation", ValidateOTLTables, bool, true);
DECL_GFX_PREF(Live, "gfx.draw-color-bars", CompositorDrawColorBars, bool, false);

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

@ -180,6 +180,8 @@ public:
mCheckOTLTables = gfxPrefs::ValidateOTLTables();
// Whether to preserve Variation tables in downloaded fonts
mKeepVariationTables = gfxPrefs::KeepVariationTables();
// Whether to preserve color bitmap glyphs
mKeepColorBitmaps = gfxPrefs::KeepColorBitmaps();
}
virtual ots::TableAction GetTableAction(uint32_t aTag) override {
@ -199,7 +201,11 @@ public:
aTag == TRUETYPE_TAG('V', 'V', 'A', 'R'))) ||
aTag == TRUETYPE_TAG('S', 'V', 'G', ' ') ||
aTag == TRUETYPE_TAG('C', 'O', 'L', 'R') ||
aTag == TRUETYPE_TAG('C', 'P', 'A', 'L')) {
aTag == TRUETYPE_TAG('C', 'P', 'A', 'L') ||
(mKeepColorBitmaps &&
(aTag == TRUETYPE_TAG('C', 'B', 'D', 'T') ||
aTag == TRUETYPE_TAG('C', 'B', 'L', 'C'))) ||
false) {
return ots::TABLE_ACTION_PASSTHRU;
}
return ots::TABLE_ACTION_DEFAULT;
@ -232,6 +238,7 @@ private:
nsTHashtable<nsCStringHashKey> mWarningsIssued;
bool mCheckOTLTables;
bool mKeepVariationTables;
bool mKeepColorBitmaps;
};
// Call the OTS library to sanitize an sfnt before attempting to use it.

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

@ -101,7 +101,6 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
]
elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
EXPORTS += [
'gfxFontconfigFonts.h',
'gfxFT2FontBase.h',
'gfxGdkNativeRenderer.h',
'gfxPlatformGtk.h',

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

@ -27,3 +27,5 @@ win64.patch: SSE2 optimization for Microsoft Visual C++ x64 version
TypeFromSize.patch: Bug 656185 - Add a method to detect YUVType from plane sizes.
QuellGccWarnings.patch: Bug 711895 - Avoid some GCC compilation warnings.
clang-cl-workaround.patch: Bug 1422368 - Work around a clang-cl unresolved symbol bug.

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

@ -0,0 +1,26 @@
diff --git a/gfx/ycbcr/yuv_row_win.cpp b/gfx/ycbcr/yuv_row_win.cpp
--- a/gfx/ycbcr/yuv_row_win.cpp
+++ b/gfx/ycbcr/yuv_row_win.cpp
@@ -6,16 +6,22 @@
#include "mozilla/SSE.h"
#define kCoefficientsRgbU kCoefficientsRgbY + 2048
#define kCoefficientsRgbV kCoefficientsRgbY + 4096
extern "C" {
#if defined(MOZILLA_MAY_SUPPORT_SSE) && defined(_M_IX86)
+#if defined(__clang__)
+// clang-cl may erroneously discard the symbol `kCoefficientsRgbY`
+// https://bugs.llvm.org/show_bug.cgi?id=35290
+volatile auto keep_kCoefficientsRgbY_alive = &kCoefficientsRgbY;
+#endif
+
__declspec(naked)
void FastConvertYUVToRGB32Row_SSE(const uint8* y_buf,
const uint8* u_buf,
const uint8* v_buf,
uint8* rgb_buf,
int width) {
__asm {
pushad

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

@ -10,3 +10,4 @@ patch -p3 <convert.patch
patch -p3 <win64.patch
patch -p3 <TypeFromSize.patch
patch -p3 <QuellGccWarnings.patch
patch -p3 <clang-cl-workaround.patch

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

@ -11,6 +11,12 @@
extern "C" {
#if defined(MOZILLA_MAY_SUPPORT_SSE) && defined(_M_IX86)
#if defined(__clang__)
// clang-cl may erroneously discard the symbol `kCoefficientsRgbY`
// https://bugs.llvm.org/show_bug.cgi?id=35290
volatile auto keep_kCoefficientsRgbY_alive = &kCoefficientsRgbY;
#endif
__declspec(naked)
void FastConvertYUVToRGB32Row_SSE(const uint8* y_buf,
const uint8* u_buf,

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

@ -684,7 +684,10 @@ RasterImage::GetImageContainerAtSize(LayerManager* aManager,
const Maybe<SVGImageContext>& aSVGContext,
uint32_t aFlags)
{
return GetImageContainerImpl(aManager, aSize, aSVGContext, aFlags);
// We do not pass in the given SVG context because in theory it could differ
// between calls, but actually have no impact on the actual contents of the
// image container.
return GetImageContainerImpl(aManager, aSize, Nothing(), aFlags);
}
size_t

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

@ -610,6 +610,14 @@ imgFrame::ImageUpdatedInternal(const nsIntRect& aUpdateRect)
// decoded rect that extends outside the bounds of the frame rect.
mDecoded.IntersectRect(mDecoded, mFrameRect);
// Update our invalidation counters for any consumers watching for changes
// in the surface.
if (mRawSurface) {
mRawSurface->Invalidate();
}
if (mLockedSurface && mRawSurface != mLockedSurface) {
mLockedSurface->Invalidate();
}
return NS_OK;
}

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

@ -980,6 +980,13 @@ BackgroundParentImpl::DeallocPClientManagerParent(mozilla::dom::PClientManagerPa
return mozilla::dom::DeallocClientManagerParent(aActor);
}
mozilla::ipc::IPCResult
BackgroundParentImpl::RecvPClientManagerConstructor(mozilla::dom::PClientManagerParent* aActor)
{
mozilla::dom::InitClientManagerParent(aActor);
return IPC_OK();
}
} // namespace ipc
} // namespace mozilla

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

@ -268,6 +268,9 @@ protected:
virtual bool
DeallocPClientManagerParent(PClientManagerParent* aActor) override;
virtual mozilla::ipc::IPCResult
RecvPClientManagerConstructor(PClientManagerParent* aActor) override;
};
} // namespace ipc

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

@ -2688,7 +2688,7 @@ js::LoadScalar##T::Func(JSContext* cx, unsigned argc, Value* vp)
\
JS::AutoCheckCannotGC nogc(cx); \
T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc)); \
args.rval().setNumber((double) *target); \
args.rval().setNumber(JS::CanonicalizeNaN((double) *target)); \
return true; \
}

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

@ -0,0 +1,10 @@
var Uint32Array = TypedObject.float32.array(3);
const ONE_MINUS_EPSILON = 1 - Math.pow(2, -53);
const f = new Float64Array([0, 0]);
const u = new Uint32Array(f.buffer);
const diff = function(a, b) {
f[1] = b;
u[3 - ENDIAN];
};
ENDIAN = 1;
diff(1, ONE_MINUS_EPSILON)

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

@ -96,7 +96,7 @@
assertEq(runningInBrowser, false, "Only called when running in the shell.");
// Return early if no options are set.
var currentOptions = options();
var currentOptions = options ? options() : "";
if (currentOptions === "")
return;

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

@ -26,13 +26,13 @@ SUPPORT_FILES = set(["browser.js", "shell.js", "template.js", "user.js",
FRONTMATTER_WRAPPER_PATTERN = re.compile(
r'/\*\---\n([\s]*)((?:\s|\S)*)[\n\s*]---\*/', flags=re.DOTALL)
def convertTestFile(source):
def convertTestFile(source, includes):
"""
Convert a jstest test to a compatible Test262 test file.
"""
source = convertReportCompare(source)
source = updateMeta(source)
source = updateMeta(source, includes)
source = insertCopyrightLines(source)
return source
@ -153,7 +153,7 @@ def extractMeta(source):
return yaml.safe_load(unindented)
def updateMeta(source):
def updateMeta(source, includes):
"""
Captures the reftest meta and a pre-existing meta if any and merge them
into a single dict.
@ -166,13 +166,14 @@ def updateMeta(source):
frontmatter = extractMeta(source)
# Merge the reftest and frontmatter
merged = mergeMeta(reftest, frontmatter)
merged = mergeMeta(reftest, frontmatter, includes)
# Cleanup the metadata
properData = cleanupMeta(merged)
return insertMeta(source, properData)
def cleanupMeta(meta):
"""
Clean up all the frontmatter meta tags. This is not a lint tool, just a
@ -206,7 +207,7 @@ def cleanupMeta(meta):
return meta
def mergeMeta(reftest, frontmatter):
def mergeMeta(reftest, frontmatter, includes):
"""
Merge the metadata from reftest and an existing frontmatter and populate
required frontmatter fields properly.
@ -250,6 +251,10 @@ def mergeMeta(reftest, frontmatter):
"frontmatter error. %s != %s" % (error,
frontmatter["negative"]["type"]))
# Add the shell specific includes
if includes:
frontmatter["includes"] = list(includes)
return frontmatter
def insertCopyrightLines(source):
@ -295,53 +300,117 @@ def insertMeta(source, frontmatter):
else:
return "\n".join(lines) + source
def findAndCopyIncludes(dirPath, baseDir, includeDir):
relPath = os.path.relpath(dirPath, baseDir)
includes = []
# Recurse down all folders in the relative path until
# we reach the base directory of shell.js include files.
# Each directory will have a shell.js file to copy.
while (relPath):
# find the shell.js
shellFile = os.path.join(baseDir, relPath, "shell.js")
# create new shell.js file name
includeFileName = relPath.replace("/", "-") + "-shell.js"
includesPath = os.path.join(includeDir, includeFileName)
if os.path.exists(shellFile):
# if the file exists, include in includes
includes.append(includeFileName)
if not os.path.exists(includesPath):
shutil.copyfile(shellFile, includesPath)
relPath = os.path.split(relPath)[0]
shellFile = os.path.join(baseDir, "shell.js")
includesPath = os.path.join(includeDir, "shell.js")
if not os.path.exists(includesPath):
shutil.copyfile(shellFile, includesPath)
includes.append("shell.js")
if not os.path.exists(includesPath):
shutil.copyfile(shellFile, includesPath)
return includes
def exportTest262(args):
src = os.path.abspath(args.src[0])
outDir = os.path.abspath(args.out)
providedSrcs = args.src
includeShell = args.exportshellincludes
baseDir = os.getcwd()
# Create the output directory from scratch.
if os.path.isdir(outDir):
shutil.rmtree(outDir)
# Process all test directories recursively.
for (dirPath, _, fileNames) in os.walk(src):
relPath = os.path.relpath(dirPath, src)
# only make the includes directory if requested
includeDir = os.path.join(outDir, "harness-includes")
if includeShell:
os.makedirs(includeDir)
relOutDir = os.path.join(outDir, relPath)
# Go through each source path
for providedSrc in providedSrcs:
# This also creates the own outDir folder
if not os.path.exists(relOutDir):
os.makedirs(relOutDir)
src = os.path.abspath(providedSrc)
# the basename of the path will be used in case multiple "src" arguments
# are passed in to create an output directory for each "src".
basename = os.path.basename(src)
for fileName in fileNames:
# Skip browser.js and shell.js files
if fileName == "browser.js" or fileName == "shell.js":
continue
# Process all test directories recursively.
for (dirPath, _, fileNames) in os.walk(src):
filePath = os.path.join(dirPath, fileName)
testName = os.path.relpath(filePath, src) # captures folder/fileName
# we need to make and get the unique set of includes for this filepath
includes = []
if includeShell:
includes = findAndCopyIncludes(dirPath, baseDir, includeDir)
# Copy non-test files as is.
(_, fileExt) = os.path.splitext(fileName)
if fileExt != ".js":
shutil.copyfile(filePath, os.path.join(outDir, testName))
print("C %s" % testName)
continue
relPath = os.path.relpath(dirPath, src)
fullRelPath = os.path.join(basename, relPath)
# Read the original test source and preprocess it for Test262
with open(filePath, "rb") as testFile:
testSource = testFile.read()
# Make new test subdirectory to seperate from includes
currentOutDir = os.path.join(outDir, "tests", fullRelPath)
if not testSource:
print("SKIPPED %s" % testName)
continue
# This also creates the own outDir folder
if not os.path.exists(currentOutDir):
os.makedirs(currentOutDir)
newSource = convertTestFile(testSource)
for fileName in fileNames:
# Skip browser.js files
if fileName == "browser.js" or fileName == "shell.js" :
continue
with open(os.path.join(outDir, testName), "wb") as output:
output.write(newSource)
filePath = os.path.join(dirPath, fileName)
testName = os.path.join(fullRelPath, fileName) # captures folder(s)+filename
print("SAVED %s" % testName)
# Copy non-test files as is.
(_, fileExt) = os.path.splitext(fileName)
if fileExt != ".js":
shutil.copyfile(filePath, os.path.join(currentOutDir, fileName))
print("C %s" % testName)
continue
# Read the original test source and preprocess it for Test262
with open(filePath, "rb") as testFile:
testSource = testFile.read()
if not testSource:
print("SKIPPED %s" % testName)
continue
newSource = convertTestFile(testSource, includes)
with open(os.path.join(currentOutDir, fileName), "wb") as output:
output.write(newSource)
print("SAVED %s" % testName)
if __name__ == "__main__":
import argparse
@ -353,6 +422,8 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Export tests to match Test262 file compliance.")
parser.add_argument("--out", default="test262/export",
help="Output directory. Any existing directory will be removed! (default: %(default)s)")
parser.add_argument("--exportshellincludes", action="store_true",
help="Optionally export shell.js files as includes in exported tests. Only use for testing, do not use for exporting to test262 (test262 tests should have as few dependencies as possible).")
parser.add_argument("src", nargs="+", help="Source folder with test files to export")
parser.set_defaults(func=exportTest262)
args = parser.parse_args()

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

@ -6766,6 +6766,8 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
// Snap even if we have a scale in the context. But don't snap if
// we have something that's not translation+scale, or if the scale flips in
// the X or Y direction, because snapped image drawing can't handle that yet.
// Any changes to this algorithm will need to be reflected in
// ComputeImageContainerDrawingParameters.
if (!currentMatrix.HasNonAxisAlignedTransform() &&
currentMatrix._11 > 0.0 && currentMatrix._22 > 0.0 &&
aCtx->UserToDevicePixelSnapped(fill, true) &&
@ -7191,15 +7193,49 @@ nsLayoutUtils::ComputeImageContainerDrawingParameters(imgIContainer*
viewportSize.height)));
}
// Compute our size in layer pixels.
const LayerIntSize layerSize =
RoundedToInt(LayerSize(aDestRect.Width() * scaleFactors.width,
aDestRect.Height() * scaleFactors.height));
// Attempt to snap pixels, the same as ComputeSnappedImageDrawingParameters.
// Any changes to the algorithm here will need to be reflected there.
bool snapped = false;
gfxSize gfxLayerSize;
const gfx::Matrix& itm = aSc.GetInheritedTransform();
if (!itm.HasNonAxisAlignedTransform() &&
itm._11 > 0.0 &&
itm._22 > 0.0) {
gfxRect rect(gfxPoint(aDestRect.X(), aDestRect.Y()),
gfxSize(aDestRect.Width(), aDestRect.Height()));
gfxPoint p1 = ThebesPoint(itm.TransformPoint(ToPoint(rect.TopLeft())));
gfxPoint p2 = ThebesPoint(itm.TransformPoint(ToPoint(rect.TopRight())));
gfxPoint p3 = ThebesPoint(itm.TransformPoint(ToPoint(rect.BottomRight())));
if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
p1.Round();
p3.Round();
rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(),
std::max(p1.y, p3.y) - rect.Y()));
// An empty size is unacceptable so we ensure our suggested size is at
// least 1 pixel wide/tall.
gfxLayerSize = gfxSize(std::max(rect.Width(), 1.0),
std::max(rect.Height(), 1.0));
snapped = true;
}
}
if (!snapped) {
// Compute our size in layer pixels.
const LayerIntSize layerSize =
RoundedToInt(LayerSize(aDestRect.Width() * scaleFactors.width,
aDestRect.Height() * scaleFactors.height));
// An empty size is unacceptable so we ensure our suggested size is at least
// 1 pixel wide/tall.
gfxLayerSize = gfxSize(std::max(layerSize.width, 1),
std::max(layerSize.height, 1));
}
// Determine the optimal image size to use. An empty size is unacceptable so
// we ensure our suggested size is at least 1 pixel wide/tall.
gfxSize gfxLayerSize = gfxSize(std::max(layerSize.width, 1),
std::max(layerSize.height, 1));
return aImage->OptimalImageSizeForDest(gfxLayerSize,
imgIContainer::FRAME_CURRENT,
samplingFilter, aFlags);

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

@ -2829,7 +2829,6 @@ static nsresult
pref_LoadPrefsInDirList(const char* aListId);
static const char kTelemetryPref[] = "toolkit.telemetry.enabled";
static const char kOldTelemetryPref[] = "toolkit.telemetry.enabledPreRelease";
static const char kChannelPref[] = "app.update.channel";
// clang-format off
@ -3485,12 +3484,6 @@ Preferences::InitializeUserPrefs()
// Don't set mCurrentFile until we're done so that dirty flags work properly.
sPreferences->mCurrentFile = prefsFile.forget();
// Migrate the old prerelease telemetry pref.
if (!Preferences::GetBool(kOldTelemetryPref, true)) {
Preferences::SetBoolInAnyProcess(kTelemetryPref, false);
Preferences::ClearUser(kOldTelemetryPref);
}
sPreferences->NotifyServiceObservers(NS_PREFSERVICE_READ_TOPIC_ID);
}

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

@ -791,6 +791,11 @@ pref("gfx.downloadable_fonts.otl_validation", false);
pref("gfx.downloadable_fonts.otl_validation", true);
#endif
// Whether to preserve color bitmap tables in fonts (bypassing OTS).
// Currently these are supported only on platforms where we use Freetype
// to render fonts (Linux/Gtk and Android).
pref("gfx.downloadable_fonts.keep_color_bitmaps", false);
// Whether to preserve OpenType variation tables in fonts (bypassing OTS)
pref("gfx.downloadable_fonts.keep_variation_tables", false);

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

@ -866,6 +866,10 @@ nsHostResolver::ResolveHost(const char *host,
LOG_HOST(host, netInterface),
(af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
// We need to lock in case any other thread is reading
// addr_info.
MutexAutoLock lock(he->rec->addr_info_lock);
he->rec->addr_info = nullptr;
if (unspecHe->rec->negative) {
he->rec->negative = unspecHe->rec->negative;

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

@ -6949,7 +6949,13 @@ nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
// earlier in ReadFromCache, so this must be a response from the network.
MOZ_ASSERT(request == mTransactionPump);
LOG((" First response from network\n"));
mFirstResponseSource = RESPONSE_FROM_NETWORK;
{
// Race condition with OnCacheEntryCheck, which is not limited
// to main thread.
mozilla::MutexAutoLock lock(mRCWNLock);
mFirstResponseSource = RESPONSE_FROM_NETWORK;
mOnStartRequestTimestamp = TimeStamp::Now();
}
mAvailableCachedAltDataType.Truncate();
} else if (WRONG_RACING_RESPONSE_SOURCE(request)) {
LOG((" Early return when racing. This response not needed."));
@ -6965,7 +6971,9 @@ nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
"If we have both pumps, the cache content must be partial");
mAfterOnStartRequestBegun = true;
mOnStartRequestTimestamp = TimeStamp::Now();
if (mOnStartRequestTimestamp.IsNull()) {
mOnStartRequestTimestamp = TimeStamp::Now();
}
Telemetry::Accumulate(Telemetry::HTTP_ONSTART_SUSPEND_TOTAL_TIME,
mSuspendTotalTime);

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

@ -105,14 +105,14 @@ this.ContentTaskUtils = {
return;
}
subject.removeEventListener(eventName, listener, capture);
resolve(event);
setTimeout(() => resolve(event), 0);
} catch (ex) {
try {
subject.removeEventListener(eventName, listener, capture);
} catch (ex2) {
// Maybe the provided object does not support removeEventListener.
}
reject(ex);
setTimeout(() => reject(ex), 0);
}
}, capture, wantsUntrusted);
});

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

@ -1,5 +0,0 @@
[HTMLOutputElement.html]
type: testharness
[defaultValue on HTMLOutputElement must enqueue disconnectedCallback when removing a custom element]
expected: FAIL

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

@ -1,5 +0,0 @@
[fetch-error-2.html]
type: testharness
[Test that failure to fetch dependency leads to error event on script.]
expected: FAIL

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

@ -11149,14 +11149,6 @@
"n_values": 20,
"description": "Methods invoked under window.crypto.subtle (0=encrypt, 1=decrypt, 2=sign, 3=verify, 4=digest, 5=generateKey, 6=deriveKey, 7=deriveBits, 8=importKey, 9=exportKey, 10=wrapKey, 11=unwrapKey)"
},
"WEBCRYPTO_METHOD_SECURE": {
"alert_emails": ["seceng-telemetry@mozilla.com"],
"record_in_processes": ["main", "content"],
"expires_in_version": "60",
"kind": "boolean",
"bug_numbers": [1333140],
"description": "Whether a method invoked under window.crypto.subtle is in a secure context"
},
"WEBCRYPTO_ALG": {
"record_in_processes": ["main", "content"],
"expires_in_version": "never",