Merge autoland to central, a=merge

MozReview-Commit-ID: 4NzLU3zKJOz
This commit is contained in:
Wes Kocher 2017-06-19 17:01:09 -07:00
Родитель 2bcd258281 754a9682b2
Коммит 1e80580419
137 изменённых файлов: 2655 добавлений и 1374 удалений

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

@ -13,7 +13,6 @@ module.exports = {
"block-scoped-var": "error",
"camelcase": "error",
"comma-dangle": ["error", "never"],
"comma-style": ["error", "last"],
"complexity": ["error", 20],
"consistent-this": "off",
"curly": ["error", "multi-line"],

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

@ -12,8 +12,8 @@
// * add an entry here in the proper ordering (based on spans)
// The <a/> part of the snippet will be linked to the corresponding url.
const DEFAULT_SNIPPETS_URLS = [
"https://www.mozilla.org/firefox/features/?utm_source=snippet&utm_medium=snippet&utm_campaign=default+feature+snippet"
, "https://addons.mozilla.org/firefox/?utm_source=snippet&utm_medium=snippet&utm_campaign=addons"
"https://www.mozilla.org/firefox/features/?utm_source=snippet&utm_medium=snippet&utm_campaign=default+feature+snippet",
"https://addons.mozilla.org/firefox/?utm_source=snippet&utm_medium=snippet&utm_campaign=addons"
];
const SNIPPETS_UPDATE_INTERVAL_MS = 14400000; // 4 hours.

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

@ -310,11 +310,11 @@ var StarUI = {
fn();
}, {"capture": true, "once": true});
};
gEditItemOverlay.initPanel({ node: aNode
, onPanelReady
, hiddenRows: ["description", "location",
"loadInSidebar", "keyword"]
, focusedElement: "preferred"});
gEditItemOverlay.initPanel({ node: aNode,
onPanelReady,
hiddenRows: ["description", "location",
"loadInSidebar", "keyword"],
focusedElement: "preferred"});
this.panel.openPopup(aAnchorElement, aPosition);
},
@ -499,8 +499,8 @@ var PlacesCommandHook = {
}
if (description) {
info.annotations = [{ name: PlacesUIUtils.DESCRIPTION_ANNO
, value: description }];
info.annotations = [{ name: PlacesUIUtils.DESCRIPTION_ANNO,
value: description }];
}
info.guid = await PlacesTransactions.NewBookmark(info).transact();
@ -572,8 +572,8 @@ var PlacesCommandHook = {
async bookmarkLink(aParentId, aURL, aTitle, aDescription = "") {
let node = await PlacesUIUtils.fetchNodeLike({ url: aURL });
if (node) {
PlacesUIUtils.showBookmarkDialog({ action: "edit"
, node
PlacesUIUtils.showBookmarkDialog({ action: "edit",
node
}, window.top);
return;
}
@ -581,16 +581,16 @@ var PlacesCommandHook = {
let ip = new InsertionPoint(aParentId,
PlacesUtils.bookmarks.DEFAULT_INDEX,
Components.interfaces.nsITreeView.DROP_ON);
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "bookmark"
, uri: makeURI(aURL)
, title: aTitle
, description: aDescription
, defaultInsertionPoint: ip
, hiddenRows: [ "description"
, "location"
, "loadInSidebar"
, "keyword" ]
PlacesUIUtils.showBookmarkDialog({ action: "add",
type: "bookmark",
uri: makeURI(aURL),
title: aTitle,
description: aDescription,
defaultInsertionPoint: ip,
hiddenRows: [ "description",
"location",
"loadInSidebar",
"keyword" ]
}, window.top);
},
@ -623,10 +623,10 @@ var PlacesCommandHook = {
bookmarkCurrentPages: function PCH_bookmarkCurrentPages() {
let pages = this.uniqueCurrentPages;
if (pages.length > 1) {
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "folder"
, URIList: pages
, hiddenRows: [ "description" ]
PlacesUIUtils.showBookmarkDialog({ action: "add",
type: "folder",
URIList: pages,
hiddenRows: [ "description" ]
}, window);
}
},
@ -667,16 +667,16 @@ var PlacesCommandHook = {
description = (await this._getPageDetails(gBrowser.selectedBrowser)).description;
}
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "livemark"
, feedURI
, siteURI: gBrowser.currentURI
, title
, description
, defaultInsertionPoint: toolbarIP
, hiddenRows: [ "feedLocation"
, "siteLocation"
, "description" ]
PlacesUIUtils.showBookmarkDialog({ action: "add",
type: "livemark",
feedURI,
siteURI: gBrowser.currentURI,
title,
description,
defaultInsertionPoint: toolbarIP,
hiddenRows: [ "feedLocation",
"siteLocation",
"description" ]
}, window);
},

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

@ -1054,7 +1054,54 @@ toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > #downloads-
-moz-image-region: auto;
}
%ifndef MOZ_WIDGET_GTK
%ifdef MOZ_WIDGET_COCOA
/* On Mac, use the properties "-moz-window-transform" and "-moz-window-opacity"
instead of "transform" and "opacity" for these animations.
The -moz-window* properties apply to the whole window including the window's
shadow, and they don't affect the window's "shape", so the system doesn't
have to recompute the shadow shape during the animation. This makes them a
lot faster. In fact, Gecko no longer triggers shadow shape recomputations
for repaints.
These properties are not implemented on other platforms. */
#BMB_bookmarksPopup {
-moz-window-transform: scale(.4);
-moz-window-opacity: 0;
transition-property: -moz-window-transform, -moz-window-opacity;
transition-duration: 0.15s;
transition-timing-function: ease-out;
}
#BMB_bookmarksPopup[animate="open"] {
-moz-window-transform: none;
-moz-window-opacity: 1.0;
}
#BMB_bookmarksPopup[animate="cancel"] {
-moz-window-transform: none;
}
#BMB_bookmarksPopup[arrowposition="after_start"]:-moz-locale-dir(ltr),
#BMB_bookmarksPopup[arrowposition="after_end"]:-moz-locale-dir(rtl) {
-moz-window-transform-origin: 20px top;
}
#BMB_bookmarksPopup[arrowposition="after_end"]:-moz-locale-dir(ltr),
#BMB_bookmarksPopup[arrowposition="after_start"]:-moz-locale-dir(rtl) {
-moz-window-transform-origin: calc(100% - 20px) top;
}
#BMB_bookmarksPopup[arrowposition="before_start"]:-moz-locale-dir(ltr),
#BMB_bookmarksPopup[arrowposition="before_end"]:-moz-locale-dir(rtl) {
-moz-window-transform-origin: 20px bottom;
}
#BMB_bookmarksPopup[arrowposition="before_end"]:-moz-locale-dir(ltr),
#BMB_bookmarksPopup[arrowposition="before_start"]:-moz-locale-dir(rtl) {
-moz-window-transform-origin: calc(100% - 20px) bottom;
}
%elifndef MOZ_WIDGET_GTK
#BMB_bookmarksPopup {
transform: scale(.4);
@ -1092,7 +1139,6 @@ toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > #downloads-
#BMB_bookmarksPopup[arrowposition="before_start"]:-moz-locale-dir(rtl) {
transform-origin: calc(100% - 20px) bottom;
}
%endif
/* Customize mode */

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

@ -5749,14 +5749,14 @@ function contentAreaClick(event, isPanelClick) {
// This is the Opera convention for a special link that, when clicked,
// allows to add a sidebar panel. The link's title attribute contains
// the title that should be used for the sidebar panel.
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "bookmark"
, uri: makeURI(href)
, title: linkNode.getAttribute("title")
, loadBookmarkInSidebar: true
, hiddenRows: [ "description"
, "location"
, "keyword" ]
PlacesUIUtils.showBookmarkDialog({ action: "add",
type: "bookmark",
uri: makeURI(href),
title: linkNode.getAttribute("title"),
loadBookmarkInSidebar: true,
hiddenRows: [ "description",
"location",
"keyword" ]
}, window);
event.preventDefault();
return;
@ -6646,18 +6646,18 @@ function AddKeywordForSearchField() {
let bookmarkData = message.data;
let title = gNavigatorBundle.getFormattedString("addKeywordTitleAutoFill",
[bookmarkData.title]);
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "bookmark"
, uri: makeURI(bookmarkData.spec)
, title
, description: bookmarkData.description
, keyword: ""
, postData: bookmarkData.postData
, charSet: bookmarkData.charset
, hiddenRows: [ "location"
, "description"
, "tags"
, "loadInSidebar" ]
PlacesUIUtils.showBookmarkDialog({ action: "add",
type: "bookmark",
uri: makeURI(bookmarkData.spec),
title,
description: bookmarkData.description,
keyword: "",
postData: bookmarkData.postData,
charSet: bookmarkData.charset,
hiddenRows: [ "location",
"description",
"tags",
"loadInSidebar" ]
}, window);
}
mm.addMessageListener("ContextMenu:SearchFieldBookmarkData:Result", onMessage);

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

@ -35,8 +35,7 @@ var panelProgressListener = {
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
window.parent.document.getElementById("sidebar-throbber").removeAttribute("loading");
}
}
,
},
onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
UpdateBackForwardCommands(getPanelBrowser().webNavigation);

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

@ -167,9 +167,6 @@ this.PanelMultiView = class {
get _mainView() {
return this._mainViewId ? this.document.getElementById(this._mainViewId) : null;
}
get showingSubViewAsMainView() {
return this.node.getAttribute("mainViewIsSubView") == "true";
}
get _transitioning() {
return this.__transitioning;
@ -460,6 +457,7 @@ this.PanelMultiView = class {
};
// Make sure that new panels always have a title set.
let cancel = false;
if (this.panelViews && aAnchor) {
if (aAnchor && !viewNode.hasAttribute("title"))
viewNode.setAttribute("title", aAnchor.getAttribute("label"));
@ -468,17 +466,17 @@ this.PanelMultiView = class {
if (custWidget) {
if (custWidget.onInit)
custWidget.onInit(aAnchor);
custWidget.onViewShowing({ target: aAnchor, detail });
custWidget.onViewShowing({ target: viewNode, preventDefault: () => cancel = true, detail });
}
}
viewNode.setAttribute("current", true);
if (this.panelViews && this._mainViewWidth)
viewNode.style.maxWidth = viewNode.style.minWidth = this._mainViewWidth + "px";
let evt = new window.CustomEvent("ViewShowing", { bubbles: true, cancelable: true, detail });
viewNode.dispatchEvent(evt);
let cancel = evt.defaultPrevented;
if (!cancel)
cancel = evt.defaultPrevented;
if (detail.blockers.size) {
try {
let results = await Promise.all(detail.blockers);
@ -494,6 +492,7 @@ this.PanelMultiView = class {
}
this._currentSubView = viewNode;
viewNode.setAttribute("current", true);
if (this.panelViews) {
this.node.setAttribute("viewtype", "subview");
if (!playTransition)
@ -625,8 +624,8 @@ this.PanelMultiView = class {
this._viewContainer.removeAttribute("transition-reverse");
evt = new window.CustomEvent("ViewShown", { bubbles: true, cancelable: false });
viewNode.dispatchEvent(evt);
viewNode.dispatchEvent(new window.CustomEvent("ViewShown",
{ bubbles: true, cancelable: false }));
}, { once: true });
});
}, { once: true });
@ -638,6 +637,8 @@ this.PanelMultiView = class {
// Now that the subview is visible, we can check the height of the
// description elements it contains.
this.descriptionHeightWorkaround(viewNode);
viewNode.dispatchEvent(new window.CustomEvent("ViewShown",
{ bubbles: true, cancelable: false }));
});
this._shiftMainView(aAnchor);
}

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

@ -117,16 +117,16 @@
<vbox id="PanelUI-remotetabs-main" observes="sync-syncnow-state">
<vbox id="PanelUI-remotetabs-buttons">
<toolbarbutton id="PanelUI-remotetabs-view-sidebar"
class="subviewbutton"
class="subviewbutton subviewbutton-iconic"
observes="viewTabsSidebar"
label="&appMenuRemoteTabs.sidebar.label;"/>
<toolbarbutton id="PanelUI-remotetabs-view-managedevices"
class="subviewbutton"
class="subviewbutton subviewbutton-iconic"
label="&appMenuRemoteTabs.managedevices.label;"
oncommand="gSync.openDevicesManagementPage('syncedtabs-menupanel');"/>
<toolbarbutton id="PanelUI-remotetabs-syncnow"
observes="sync-status"
class="subviewbutton"
class="subviewbutton subviewbutton-iconic"
oncommand="gSync.doSync();"
closemenu="none"/>
<menuseparator id="PanelUI-remotetabs-separator"/>

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

@ -515,12 +515,14 @@ const PanelUI = {
multiView.setAttribute("nosubviews", "true");
multiView.setAttribute("viewCacheId", "appMenu-viewCache");
if (gPhotonStructure) {
tempPanel.setAttribute("photon", true);
multiView.setAttribute("mainViewId", viewNode.id);
multiView.appendChild(viewNode);
}
tempPanel.appendChild(multiView);
multiView.setAttribute("mainViewIsSubView", "true");
multiView.setMainView(viewNode);
if (!gPhotonStructure) {
multiView.setMainView(viewNode);
}
viewNode.classList.add("cui-widget-panelview");
let viewShown = false;

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

@ -14,8 +14,10 @@ add_task(async function() {
let historyButton = document.getElementById("history-panelmenu");
ok(historyButton, "History button appears in Panel Menu");
historyButton.click();
let historyPanel = document.getElementById("PanelUI-history");
let promise = BrowserTestUtils.waitForEvent(historyPanel, "ViewShown");
historyButton.click();
await promise;
ok(historyPanel.getAttribute("current"), "History Panel is in view");
let panelHiddenPromise = promisePanelHidden(window);

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

@ -130,6 +130,7 @@ this.browserAction = class extends ExtensionAPI {
let view = document.createElementNS(XUL_NS, "panelview");
view.id = this.viewId;
view.setAttribute("flex", "1");
view.setAttribute("extension", true);
document.getElementById("PanelUI-multiView").appendChild(view);
document.addEventListener("popupshowing", this);
@ -171,6 +172,10 @@ this.browserAction = class extends ExtensionAPI {
// Google Chrome onClicked extension API.
if (popupURL) {
try {
// FIXME: The line below needs to change eventually, but for now:
// ensure the view is _always_ visible _before_ `popup.attach()` is
// called. PanelMultiView.jsm dictates different behavior.
event.target.setAttribute("current", true);
let popup = this.getPopup(document.defaultView, popupURL);
event.detail.addBlocker(popup.attach(event.target));
} catch (e) {

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

@ -3,6 +3,8 @@
"use strict";
add_task(async function testPopupBorderRadius() {
await SpecialPowers.pushPrefEnv({set: [["browser.photon.structure.enabled", false]]});
let extension = ExtensionTestUtils.loadExtension({
background() {
browser.tabs.query({active: true, currentWindow: true}, tabs => {

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

@ -2,6 +2,8 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
requestLongerTimeout(2);
let extData = {
manifest: {
"sidebar_action": {

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

@ -4,7 +4,6 @@ module.exports = {
"rules": {
"block-scoped-var": "error",
"comma-dangle": "off",
"comma-style": ["error", "last"],
"complexity": ["error", {"max": 21}],
"dot-notation": "error",
"indent": ["error", 2, {"SwitchCase": 1, "ArrayExpression": "first", "ObjectExpression": "first"}],

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

@ -367,7 +367,10 @@ BrowserGlue.prototype = {
this._onDeviceConnected(data);
break;
case "fxaccounts:device_disconnected":
this._onDeviceDisconnected();
data = JSON.parse(data);
if (data.isLocalDevice) {
this._onDeviceDisconnected();
}
break;
case "weave:engine:clients:display-uris":
this._onDisplaySyncURIs(subject);

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

@ -579,9 +579,9 @@ this.PlacesUIUtils = {
if (!this.PLACES_FLAVORS.includes(aData.type))
throw new Error(`itemGuid unexpectedly set on ${aData.type} data`);
let info = { guid: aData.itemGuid
, newParentGuid: aNewParentGuid
, newIndex: aIndex };
let info = { guid: aData.itemGuid,
newParentGuid: aNewParentGuid,
newIndex: aIndex };
if (aCopy) {
info.excludingAnnotation = "Places/SmartBookmark";
return PlacesTransactions.Copy(info);
@ -597,16 +597,16 @@ this.PlacesUIUtils = {
throw new Error("Can't copy a container from a legacy-transactions build");
if (aData.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR) {
return PlacesTransactions.NewSeparator({ parentGuid: aNewParentGuid
, index: aIndex });
return PlacesTransactions.NewSeparator({ parentGuid: aNewParentGuid,
index: aIndex });
}
let title = aData.type != PlacesUtils.TYPE_UNICODE ? aData.title
: aData.uri;
return PlacesTransactions.NewBookmark({ uri: NetUtil.newURI(aData.uri)
, title
, parentGuid: aNewParentGuid
, index: aIndex });
return PlacesTransactions.NewBookmark({ uri: NetUtil.newURI(aData.uri),
title,
parentGuid: aNewParentGuid,
index: aIndex });
},
/**

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

@ -309,18 +309,18 @@ var BookmarkPropertiesPanel = {
switch (this._action) {
case ACTION_EDIT:
gEditItemOverlay.initPanel({ node: this._node
, hiddenRows: this._hiddenRows
, focusedElement: "first" });
gEditItemOverlay.initPanel({ node: this._node,
hiddenRows: this._hiddenRows,
focusedElement: "first" });
acceptButton.disabled = gEditItemOverlay.readOnly;
break;
case ACTION_ADD:
this._node = await this._promiseNewItem();
// Edit the new item
gEditItemOverlay.initPanel({ node: this._node
, hiddenRows: this._hiddenRows
, postData: this._postData
, focusedElement: "first" });
gEditItemOverlay.initPanel({ node: this._node,
hiddenRows: this._hiddenRows,
postData: this._postData,
focusedElement: "first" });
// Empty location field if the uri is about:blank, this way inserting a new
// url will be easier for the user, Accept button will be automatically
@ -630,12 +630,12 @@ var BookmarkPropertiesPanel = {
let parentGuid = await PlacesUtils.promiseItemGuid(containerId);
let annotations = [];
if (this._description) {
annotations.push({ name: PlacesUIUtils.DESCRIPTION_ANNO
, value: this._description });
annotations.push({ name: PlacesUIUtils.DESCRIPTION_ANNO,
value: this._description });
}
if (this._loadInSidebar) {
annotations.push({ name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO
, value: true });
annotations.push({ name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO,
value: true });
}
let itemGuid;

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

@ -290,14 +290,14 @@ PlacesController.prototype = {
break;
case "placesCmd_createBookmark":
let node = this._view.selectedNode;
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "bookmark"
, hiddenRows: [ "description"
, "keyword"
, "location"
, "loadInSidebar" ]
, uri: NetUtil.newURI(node.uri)
, title: node.title
PlacesUIUtils.showBookmarkDialog({ action: "add",
type: "bookmark",
hiddenRows: [ "description",
"keyword",
"location",
"loadInSidebar" ],
uri: NetUtil.newURI(node.uri),
title: node.title
}, window.top);
break;
}
@ -677,9 +677,9 @@ PlacesController.prototype = {
if (!node)
return;
PlacesUIUtils.showBookmarkDialog({ action: "edit"
, node
, hiddenRows: [ "folderPicker" ]
PlacesUIUtils.showBookmarkDialog({ action: "edit",
node,
hiddenRows: [ "folderPicker" ]
}, window.top);
},
@ -734,10 +734,10 @@ PlacesController.prototype = {
throw Cr.NS_ERROR_NOT_AVAILABLE;
let performed =
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: aType
, defaultInsertionPoint: ip
, hiddenRows: [ "folderPicker" ]
PlacesUIUtils.showBookmarkDialog({ action: "add",
type: aType,
defaultInsertionPoint: ip,
hiddenRows: [ "folderPicker" ]
}, window.top);
if (performed) {
// Select the new item.
@ -765,8 +765,8 @@ PlacesController.prototype = {
return;
}
let txn = PlacesTransactions.NewSeparator({ parentGuid: await ip.promiseGuid()
, index: ip.index });
let txn = PlacesTransactions.NewSeparator({ parentGuid: await ip.promiseGuid(),
index: ip.index });
let guid = await txn.transact();
let itemId = await PlacesUtils.promiseItemId(guid);
// Select the new item.

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

@ -51,8 +51,8 @@ var gMoveBookmarksDialog = {
// Nothing to do if the node is already under the selected folder.
if (node.parent.itemId == selectedFolderId)
continue;
await PlacesTransactions.Move({ guid: node.bookmarkGuid
, newParentGuid }).transact();
await PlacesTransactions.Move({ guid: node.bookmarkGuid,
newParentGuid }).transact();
}
}).then(null, Components.utils.reportError);
},

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

@ -642,16 +642,16 @@ var PlacesOrganizer = {
if (selectedNode && !PlacesUtils.nodeIsSeparator(selectedNode)) {
detailsDeck.selectedIndex = 1;
gEditItemOverlay.initPanel({ node: selectedNode
, hiddenRows: ["folderPicker"] });
gEditItemOverlay.initPanel({ node: selectedNode,
hiddenRows: ["folderPicker"] });
this._detectAndSetDetailsPaneMinimalState(selectedNode);
} else if (!selectedNode && aNodeList[0]) {
if (aNodeList.every(PlacesUtils.nodeIsURI)) {
let uris = aNodeList.map(node => PlacesUtils._uri(node.uri));
detailsDeck.selectedIndex = 1;
gEditItemOverlay.initPanel({ uris
, hiddenRows: ["folderPicker",
gEditItemOverlay.initPanel({ uris,
hiddenRows: ["folderPicker",
"loadInSidebar",
"location",
"keyword",

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

@ -149,8 +149,8 @@ function finishTest() {
*/
var bookmarksObserver = {
QueryInterface: XPCOMUtils.generateQI([
Ci.nsINavBookmarkObserver
, Ci.nsIAnnotationObserver
Ci.nsINavBookmarkObserver,
Ci.nsIAnnotationObserver
]),
// nsIAnnotationObserver

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

@ -186,8 +186,8 @@ function finishTest() {
*/
var bookmarksObserver = {
QueryInterface: XPCOMUtils.generateQI([
Ci.nsINavBookmarkObserver
, Ci.nsIAnnotationObserver
Ci.nsINavBookmarkObserver,
Ci.nsIAnnotationObserver
]),
// nsIAnnotationObserver

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

@ -9,19 +9,19 @@
*/
const URIS = [
"http://a.example1.com/"
, "http://b.example1.com/"
, "http://b.example2.com/"
, "http://c.example3.com/"
"http://a.example1.com/",
"http://b.example1.com/",
"http://b.example2.com/",
"http://c.example3.com/"
];
const TOPIC_CONNECTION_CLOSED = "places-connection-closed";
var EXPECTED_NOTIFICATIONS = [
"places-shutdown"
, "places-will-close-connection"
, "places-expiration-finished"
, "places-connection-closed"
"places-shutdown",
"places-will-close-connection",
"places-expiration-finished",
"places-connection-closed"
];
const UNEXPECTED_NOTIFICATIONS = [

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

@ -16,9 +16,10 @@ function test() {
UITourTest();
}
const oldState = UIState.get();
registerCleanupFunction(async function() {
await signOut();
gSync.updateAllUI(UIState.get());
gSync.updateAllUI(oldState);
});
var tests = [
@ -35,7 +36,7 @@ var tests = [
await setSignedInUser();
let userData = await fxAccounts.getSignedInUser();
isnot(userData, null, "Logged in now");
gSync.updateAllUI(UIState.get());
gSync.updateAllUI({ status: UIState. STATUS_SIGNED_IN, email: "foo@example.com" });
await showMenuPromise("appMenu");
await showHighlightPromise("accountStatus");
let highlight = document.getElementById("UITourHighlightContainer");

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

@ -32,9 +32,6 @@ module.exports = {
// No space padding in parentheses
"space-in-parens": ["error", "never"],
// Commas at the end of the line not the start
"comma-style": "error",
// Require braces around blocks that start a new line
"curly": ["error", "all"],

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

@ -27,9 +27,8 @@ add_task(async function test_first_time_save() {
form.querySelector("#tel").value = "1-345-345-3456";
// Wait 500ms before submission to make sure the input value applied
setTimeout(() => {
form.querySelector("input[type=submit]").click();
}, 500);
await new Promise(resolve => setTimeout(resolve, 500));
form.querySelector("input[type=submit]").click();
});
await promiseShown;
@ -65,12 +64,11 @@ add_task(async function test_non_first_time_save() {
form.querySelector("#tel").value = "1-650-903-0800";
// Wait 500ms before submission to make sure the input value applied
setTimeout(() => {
form.querySelector("input[type=submit]").click();
}, 500);
await new Promise(resolve => setTimeout(resolve, 500));
form.querySelector("input[type=submit]").click();
});
await new Promise(resolve => setTimeout(resolve, 1000));
await sleep(1000);
is(PopupNotifications.panel.state, "closed", "Doorhanger is hidden");
}
);

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

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 44.1 (41455) - http://www.bohemiancoding.com/sketch -->
<title>Tip / Check</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Tips-/-Navigation" transform="translate(-30.000000, -117.000000)" stroke-width="2">
<g id="Group">
<g id="Tip-/-Check" transform="translate(30.000000, 117.000000)">
<circle id="Oval-2" stroke="#FFFFFF" fill="#33F70C" fill-rule="evenodd" cx="10" cy="10" r="9"></circle>
<polyline id="Path-31" stroke="#165866" stroke-linecap="round" stroke-linejoin="round" points="5.5 10.5 8.5 13.5 14.5 6.5"></polyline>
</g>
</g>
</g>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 991 B

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

@ -55,19 +55,20 @@
}
#onboarding-overlay.onboarding-opened > #onboarding-overlay-dialog {
width: 1200px;
height: 550px;
width: 960px;
height: 510px;
background: #f5f5f7;
border: 1px solid rgba(9, 6, 13, 0.1); /* #09060D, 0.1 opacity */
border-radius: 3px;
position: relative;
margin: 0 calc(50% - 600px);
top: calc(50% - 275px);
margin: 0 calc(50% - 480px);
top: calc(50% - 255px);
display: grid;
grid-template-rows: [dialog-start] 76px [page-start] 1fr [footer-start] 40px [dialog-end];
grid-template-columns: [dialog-start] 240px [page-start] 1fr [dialog-end];
grid-template-rows: [dialog-start] 76px [page-start] 1fr [footer-start] 30px [dialog-end];
grid-template-columns: [dialog-start] 230px [page-start] 1fr [dialog-end];
}
@media (max-height: 550px) {
@media (max-height: 510px) {
#onboarding-overlay.onboarding-opened > #onboarding-overlay-dialog {
top: 0;
}
@ -76,11 +77,12 @@
#onboarding-overlay-dialog > header {
grid-row: dialog-start / page-start;
grid-column: dialog-start / tour-end;
margin-top: 36px;
margin-top: 24px;
margin-bottom: 0;
margin-inline-end: 0;
margin-inline-start: 36px;
font-size: 22px;
font-weight: 200;
}
#onboarding-overlay-dialog > nav {
@ -112,12 +114,15 @@
#onboarding-tour-list > li {
list-style: none;
height: 48px;
border-inline-start: 6px solid transparent;
padding-inline-start: 66px;
line-height: 48px;
border-radius: 2px;
padding-inline-start: 49px;
padding-top: 14px;
padding-bottom: 14px;
margin-inline-start: 10px;
margin-bottom: 9px;
background-repeat: no-repeat;
background-position: left 27px center;
background-position: left 10px top 7px;
background-size: 34px;
font-size: 16px;
cursor: pointer;
@ -127,6 +132,17 @@
background-position: right 27px center;
}
#onboarding-tour-list > li.onboarding-complete::before {
content: url("img/icons_tour-complete.svg");
position: relative;
left: 6px;
top: -10px;
}
#onboarding-tour-list > li.onboarding-complete {
padding-inline-start: 29px;
}
#onboarding-tour-list > li.onboarding-active,
#onboarding-tour-list > li:hover {
border-inline-start-color: #5ce6e6;
@ -139,20 +155,23 @@
grid-column: page-start;
display: grid;
grid-template-rows: [tour-page-start] 393px [tour-button-start] 1fr [tour-page-end];
grid-template-columns: [tour-page-start] 480px [tour-content-start] 1fr [tour-page-end];
grid-template-columns: [tour-page-start] 368px [tour-content-start] 1fr [tour-page-end];
}
.onboarding-tour-description {
grid-row: tour-page-start / tour-page-end;
grid-column: tour-page-start / tour-content-start;
padding: 40px;
font-size: 15px;
padding-inline-end: 40px;
line-height: 22px;
padding-inline-start: 40px;
padding-inline-end: 28px;
}
.onboarding-tour-description > h1 {
font-size: 36px;
margin: 40px 0 20px 0;
margin-top: 16px;
font-weight: 300;
line-height: 44px;
}
.onboarding-tour-content {
@ -192,15 +211,19 @@
}
.onboarding-tour-button > button {
padding: 10px 20px;
font-size: 15px;
font-weight: 600;
line-height: 21px;
background: #0d96ff;
border: none;
border-radius: 3px;
padding: 10px 20px;
font-size: 14px;
color: #fff;
box-shadow: 0 1px 0 rgba(0,0,0,0.23);
cursor: pointer;
float: inline-end;
margin-inline-end: 70px;
margin-inline-end: 26px;
margin-top: -32px;
}
.onboarding-tour-button > button:active {

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

@ -47,14 +47,14 @@ var ContentClick = {
// This is the Opera convention for a special link that, when clicked,
// allows to add a sidebar panel. The link's title attribute contains
// the title that should be used for the sidebar panel.
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "bookmark"
, uri: Services.io.newURI(json.href)
, title: json.title
, loadBookmarkInSidebar: true
, hiddenRows: [ "description"
, "location"
, "keyword" ]
PlacesUIUtils.showBookmarkDialog({ action: "add",
type: "bookmark",
uri: Services.io.newURI(json.href),
title: json.title,
loadBookmarkInSidebar: true,
hiddenRows: [ "description",
"location",
"keyword" ]
}, window);
return;
}

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

@ -22,8 +22,8 @@ const PREF_TASKBAR_REFRESH = "refreshInSeconds";
// Hash keys for pendingStatements.
const LIST_TYPE = {
FREQUENT: 0
, RECENT: 1
FREQUENT: 0,
RECENT: 1
}
/**
@ -405,8 +405,8 @@ this.WinTaskbarJumpList =
for (let row; (row = aResultSet.getNextRow());) {
try {
aCallback.call(aScope,
{ uri: row.getResultByIndex(1)
, title: row.getResultByIndex(2)
{ uri: row.getResultByIndex(1),
title: row.getResultByIndex(2)
});
} catch (e) {}
}

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

@ -261,7 +261,8 @@ photonpanelmultiview .panel-subview-header {
}
#appMenu-popup > .panel-arrowcontainer > .panel-arrowcontent,
#PanelUI-popup > .panel-arrowcontainer > .panel-arrowcontent {
#PanelUI-popup > .panel-arrowcontainer > .panel-arrowcontent,
panel[photon] > .panel-arrowcontainer > .panel-arrowcontent {
overflow: hidden;
}
@ -338,7 +339,8 @@ panelview:not([mainview]) .toolbarbutton-text,
/* START photonpanelview adjustments */
#appMenu-popup > .panel-arrowcontainer > .panel-arrowcontent {
#appMenu-popup > .panel-arrowcontainer > .panel-arrowcontent,
panel[photon] > .panel-arrowcontainer > .panel-arrowcontent {
padding: 0;
border-radius: 0;
}
@ -348,7 +350,8 @@ photonpanelmultiview panelview {
padding: 6px 0;
}
#appMenu-popup panelview {
#appMenu-popup panelview,
#customizationui-widget-multiview panelview:not([extension]) {
min-width: @menuPanelWidth@;
}
@ -1250,16 +1253,18 @@ photonpanelmultiview .subviewbutton:focus {
outline: 0;
}
photonpanelmultiview .subviewbutton > .toolbarbutton-text {
padding-inline-start: 24px; /* This is 16px for the icon + 8px for the padding as defined above. */
}
photonpanelmultiview .subviewbutton-iconic:not(.subviewbutton-back) > .toolbarbutton-text,
photonpanelmultiview .cui-withicon > .toolbarbutton-text,
photonpanelmultiview .subviewbutton[image] > .toolbarbutton-text,
photonpanelmultiview .subviewbutton[checked="true"] > .toolbarbutton-text,
photonpanelmultiview .panel-banner-item > .toolbarbutton-multiline-text {
padding-inline-start: 8px; /* See '.subviewbutton-iconic > .toolbarbutton-text' rule above. */
}
photonpanelmultiview .subviewbutton:not(.subviewbutton-iconic):not([checked="true"]) > .toolbarbutton-text {
padding-inline-start: 24px; /* This is 16px for the icon + 8px for the padding as defined above. */
}
photonpanelmultiview .PanelUI-subView .panel-subview-footer {
font-size: 1.2rem;
}
@ -2068,7 +2073,9 @@ photonpanelmultiview .PanelUI-subView .panel-header > .subviewbutton-back {
}
.panel-header > .subviewbutton-back > .toolbarbutton-text {
display: none;
/* !important to override .cui-widget-panel toolbarbutton:not([wrap]) > .toolbarbutton-text
* selector further down. */
display: none !important;
}
photonpanelmultiview .PanelUI-subView toolbarseparator {
@ -2076,4 +2083,14 @@ photonpanelmultiview .PanelUI-subView toolbarseparator {
margin-inline-end: 0;
}
photonpanelmultiview#customizationui-widget-multiview > .panel-viewcontainer {
overflow: hidden;
}
/* This is explicitly overriding the overflow properties set above. */
photonpanelmultiview .cui-widget-panelview {
overflow-x: visible;
overflow-y: visible;
}
/* END photon adjustments */

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

@ -249,3 +249,24 @@ toolbarpaletteitem[place="palette"] > #zoom-controls > #zoom-in-button {
#appMenu-fullscreen-button[checked] {
list-style-image: url(chrome://browser/skin/fullscreen-exit.svg);
}
#appMenu-library-history-button {
list-style-image: url(chrome://browser/skin/history.svg);
}
#appMenu-library-remotetabs-button {
list-style-image: url("chrome://browser/skin/synced-tabs.svg");
}
#PanelUI-remotetabs-syncnow {
list-style-image: url("chrome://browser/skin/sync.svg");
}
#PanelUI-remotetabs-view-managedevices {
list-style-image: url("chrome://browser/skin/device-mobile.svg");
}
#appMenuViewHistorySidebar,
#PanelUI-remotetabs-view-sidebar {
list-style-image: url("chrome://browser/skin/sidebars.svg");
}

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

@ -177,7 +177,7 @@ body,
flex-direction: column;
width: 100%;
height: 100%;
overflow-x: hidden;
overflow: hidden;
}
.requests-list-wrapper {

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

@ -111,5 +111,7 @@ module.exports = {
messageClose,
messageTableDataGet,
networkMessageUpdate,
// for test purpose only.
messageTableDataReceive,
};

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

@ -316,7 +316,11 @@ function limitTopLevelMessageCount(state, record, logLimit) {
const isInRemovedId = id => removedMessagesId.includes(id);
const mapHasRemovedIdKey = map => map.findKey((value, id) => isInRemovedId(id));
const objectHasRemovedIdKey = obj => Object.keys(obj).findIndex(isInRemovedId) !== -1;
const cleanUpCollection = map => removedMessagesId.forEach(id => map.remove(id));
const cleanUpList = list => list.filter(id => {
return isInRemovedId(id) === false;
});
const cleanUpObject = object => [...Object.entries(object)]
.reduce((res, [id, value]) => {
if (!isInRemovedId(id)) {
@ -328,7 +332,7 @@ function limitTopLevelMessageCount(state, record, logLimit) {
record.set("messagesById", record.messagesById.withMutations(cleanUpCollection));
if (record.messagesUiById.find(isInRemovedId)) {
record.set("messagesUiById", record.messagesUiById.withMutations(cleanUpCollection));
record.set("messagesUiById", cleanUpList(record.messagesUiById));
}
if (mapHasRemovedIdKey(record.messagesTableDataById)) {
record.set("messagesTableDataById",
@ -337,12 +341,11 @@ function limitTopLevelMessageCount(state, record, logLimit) {
if (mapHasRemovedIdKey(record.groupsById)) {
record.set("groupsById", record.groupsById.withMutations(cleanUpCollection));
}
if (Object.keys(record.repeatById).includes(removedMessagesId)) {
if (objectHasRemovedIdKey(record.repeatById)) {
record.set("repeatById", cleanUpObject(record.repeatById));
}
if (Object.keys(record.networkMessagesUpdateById).includes(removedMessagesId)) {
if (objectHasRemovedIdKey(record.networkMessagesUpdateById)) {
record.set("networkMessagesUpdateById",
cleanUpObject(record.networkMessagesUpdateById));
}

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

@ -118,6 +118,7 @@ describe("Message reducer:", () => {
it("clears the messages list in response to MESSAGES_CLEAR action", () => {
const { dispatch, getState } = setupStore([
"console.log('foobar', 'test')",
"console.log('foobar', 'test')",
"console.log(undefined)",
"console.table(['red', 'green', 'blue']);",
@ -136,6 +137,45 @@ describe("Message reducer:", () => {
expect(getAllRepeatById(state)).toEqual({});
});
it("cleans the repeatsById object when messages are pruned", () => {
const { dispatch, getState } = setupStore(
[
"console.log('foobar', 'test')",
"console.log('foobar', 'test')",
"console.log(undefined)",
"console.log(undefined)",
],
null, {
logLimit: 2
}
);
// Check that we have the expected data.
let messages = getAllMessagesById(getState());
let repeats = getAllRepeatById(getState());
expect(Object.keys(repeats).length).toBe(2);
const lastMessageId = messages.last().id;
// This addition will prune the first message out of the store.
let packet = stubPackets.get("console.log('foobar', 'test')");
dispatch(actions.messageAdd(packet));
messages = getAllMessagesById(getState());
repeats = getAllRepeatById(getState());
// There should be only the data for the "undefined" message.
expect(Object.keys(repeats)).toEqual([lastMessageId]);
expect(Object.keys(repeats).length).toBe(1);
expect(repeats[lastMessageId]).toBe(2);
// This addition will prune the first message out of the store.
packet = stubPackets.get("console.log(undefined)");
dispatch(actions.messageAdd(packet));
// repeatById should now be empty.
expect(getAllRepeatById(getState())).toEqual({});
});
it("properly limits number of messages", () => {
const { dispatch, getState } = setupStore([]);
@ -371,6 +411,43 @@ describe("Message reducer:", () => {
expect(messagesUi.size).toBe(0);
});
it("cleans the messages UI list when messages are pruned", () => {
const { dispatch, getState } = setupStore(
["console.trace()", "console.log(undefined)", "console.trace()"],
null, {
logLimit: 3
}
);
// Check that we have the expected data.
let messages = getAllMessagesById(getState());
let messagesUi = getAllMessagesUiById(getState());
expect(messagesUi.size).toBe(2);
expect(messagesUi.first()).toBe(messages.first().id);
const lastMessageId = messages.last().id;
expect(messagesUi.last()).toBe(lastMessageId);
// This addition will prune the first message out of the store.
let packet = stubPackets.get("console.log(undefined)");
dispatch(actions.messageAdd(packet));
messages = getAllMessagesById(getState());
messagesUi = getAllMessagesUiById(getState());
// There should be only the id of the last console.trace message.
expect(messagesUi.size).toBe(1);
expect(messagesUi.first()).toBe(lastMessageId);
// These additions will prune the last console.trace message out of the store.
packet = stubPackets.get("console.log('foobar', 'test')");
dispatch(actions.messageAdd(packet));
packet = stubPackets.get("console.log(undefined)");
dispatch(actions.messageAdd(packet));
// messagesUiById should now be empty.
expect(getAllMessagesUiById(getState()).size).toBe(0);
});
it("opens console.group messages when they are added", () => {
const { dispatch, getState } = setupStore([]);
@ -477,29 +554,69 @@ describe("Message reducer:", () => {
groupsById = getAllGroupsById(getState());
expect(groupsById.size).toBe(0);
});
it("cleans the groupsById property when messages are pruned", () => {
const { dispatch, getState } = setupStore(
[
"console.group('bar')",
"console.group()",
"console.groupEnd()",
"console.groupEnd('bar')",
"console.group('bar')",
"console.groupEnd('bar')",
"console.log('foobar', 'test')",
],
null, {
logLimit: 3
}
);
// Check that we have the expected data.
let groupsById = getAllGroupsById(getState());
expect(groupsById.size).toBe(3);
// This addition will prune the first group (and its child group) out of the store.
let packet = stubPackets.get("console.log(undefined)");
dispatch(actions.messageAdd(packet));
groupsById = getAllGroupsById(getState());
// There should be only the id of the last console.trace message.
expect(groupsById.size).toBe(1);
// This additions will prune the last group message out of the store.
packet = stubPackets.get("console.log('foobar', 'test')");
dispatch(actions.messageAdd(packet));
// groupsById should now be empty.
expect(getAllGroupsById(getState()).size).toBe(0);
});
});
describe("networkMessagesUpdateById", () => {
it("adds the network update message when network update action is called", () => {
const { dispatch, getState } = setupStore([
"GET request",
"XHR GET request"
]);
const { dispatch, getState } = setupStore([]);
let packet = clonePacket(stubPackets.get("GET request"));
let updatePacket = clonePacket(stubPackets.get("GET request update"));
packet.actor = "message1";
updatePacket.networkInfo.actor = "message1";
dispatch(actions.messageAdd(packet));
dispatch(actions.networkMessageUpdate(updatePacket.networkInfo));
let networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates).length).toBe(0);
expect(Object.keys(networkUpdates)).toEqual(["message1"]);
let updatePacket = stubPackets.get("GET request update");
dispatch(actions.networkMessageUpdate(updatePacket));
packet = clonePacket(stubPackets.get("GET request"));
updatePacket = stubPackets.get("XHR GET request update");
packet.actor = "message2";
updatePacket.networkInfo.actor = "message2";
dispatch(actions.messageAdd(packet));
dispatch(actions.networkMessageUpdate(updatePacket.networkInfo));
networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates).length).toBe(1);
let xhrUpdatePacket = stubPackets.get("XHR GET request update");
dispatch(actions.networkMessageUpdate(xhrUpdatePacket));
networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates).length).toBe(2);
expect(Object.keys(networkUpdates)).toEqual(["message1", "message2"]);
});
it("resets networkMessagesUpdateById in response to MESSAGES_CLEAR action", () => {
@ -508,7 +625,7 @@ describe("Message reducer:", () => {
]);
const updatePacket = stubPackets.get("XHR GET request update");
dispatch(actions.networkMessageUpdate(updatePacket));
dispatch(actions.networkMessageUpdate(updatePacket.networkInfo));
let networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates).length).toBe(1);
@ -518,5 +635,124 @@ describe("Message reducer:", () => {
networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates).length).toBe(0);
});
it("cleans the networkMessagesUpdateById property when messages are pruned", () => {
const { dispatch, getState } = setupStore([], null, {
logLimit: 3
});
// Add 3 network messages and their updates
let packet = clonePacket(stubPackets.get("XHR GET request"));
let updatePacket = clonePacket(stubPackets.get("XHR GET request update"));
packet.actor = "message1";
updatePacket.networkInfo.actor = "message1";
dispatch(actions.messageAdd(packet));
dispatch(actions.networkMessageUpdate(updatePacket.networkInfo));
packet.actor = "message2";
updatePacket.networkInfo.actor = "message2";
dispatch(actions.messageAdd(packet));
dispatch(actions.networkMessageUpdate(updatePacket.networkInfo));
packet.actor = "message3";
updatePacket.networkInfo.actor = "message3";
dispatch(actions.messageAdd(packet));
dispatch(actions.networkMessageUpdate(updatePacket.networkInfo));
// Check that we have the expected data.
let messages = getAllMessagesById(getState());
const [
firstNetworkMessageId,
secondNetworkMessageId,
thirdNetworkMessageId
] = [...messages.keys()];
let networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates)).toEqual([
firstNetworkMessageId,
secondNetworkMessageId,
thirdNetworkMessageId
]);
// This addition will remove the first network message.
packet = stubPackets.get("console.log(undefined)");
dispatch(actions.messageAdd(packet));
networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates)).toEqual([
secondNetworkMessageId,
thirdNetworkMessageId
]);
// This addition will remove the second network message.
packet = stubPackets.get("console.log('foobar', 'test')");
dispatch(actions.messageAdd(packet));
networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates)).toEqual([
thirdNetworkMessageId
]);
// This addition will remove the last network message.
packet = stubPackets.get("console.log(undefined)");
dispatch(actions.messageAdd(packet));
// networkMessageUpdateById should now be empty.
networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates)).toEqual([]);
});
});
describe("messagesTableDataById", () => {
it("resets messagesTableDataById in response to MESSAGES_CLEAR action", () => {
const { dispatch, getState } = setupStore([
"console.table(['a', 'b', 'c'])"
]);
let messages = getAllMessagesById(getState());
const data = Symbol("tableData");
dispatch(actions.messageTableDataReceive(messages.first().id, data));
let table = getAllMessagesTableDataById(getState());
expect(table.size).toBe(1);
expect(table.get(messages.first().id)).toBe(data);
dispatch(actions.messagesClear());
expect(getAllMessagesTableDataById(getState()).size).toBe(0);
});
it("cleans the messagesTableDataById property when messages are pruned", () => {
const { dispatch, getState } = setupStore([], null, {
logLimit: 2
});
// Add 2 table message and their data.
dispatch(actions.messageAdd(stubPackets.get("console.table(['a', 'b', 'c'])")));
dispatch(actions.messageAdd(
stubPackets.get("console.table(['red', 'green', 'blue']);")));
let messages = getAllMessagesById(getState());
const tableData1 = Symbol();
const tableData2 = Symbol();
const [id1, id2] = [...messages.keys()];
dispatch(actions.messageTableDataReceive(id1, tableData1));
dispatch(actions.messageTableDataReceive(id2, tableData2));
let table = getAllMessagesTableDataById(getState());
expect(table.size).toBe(2);
// This addition will remove the first table message.
dispatch(actions.messageAdd(stubPackets.get("console.log(undefined)")));
table = getAllMessagesTableDataById(getState());
expect(table.size).toBe(1);
expect(table.get(id2)).toBe(tableData2);
// This addition will remove the second table message.
dispatch(actions.messageAdd(stubPackets.get("console.log('foobar', 'test')")));
expect(getAllMessagesTableDataById(getState()).size).toBe(0);
});
});
});

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

@ -1501,6 +1501,52 @@ exports.CSS_PROPERTIES = {
"unset"
]
},
"-moz-window-opacity": {
"isInherited": false,
"subproperties": [
"-moz-window-opacity"
],
"supports": [
7
],
"values": [
"inherit",
"initial",
"unset"
]
},
"-moz-window-transform": {
"isInherited": false,
"subproperties": [
"-moz-window-transform"
],
"supports": [],
"values": [
"inherit",
"initial",
"unset"
]
},
"-moz-window-transform-origin": {
"isInherited": false,
"subproperties": [
"-moz-window-transform-origin"
],
"supports": [
6,
8
],
"values": [
"bottom",
"center",
"inherit",
"initial",
"left",
"right",
"top",
"unset"
]
},
"-webkit-align-content": {
"isInherited": false,
"subproperties": [
@ -3150,6 +3196,9 @@ exports.CSS_PROPERTIES = {
"will-change",
"-moz-window-dragging",
"-moz-window-shadow",
"-moz-window-opacity",
"-moz-window-transform",
"-moz-window-transform-origin",
"word-break",
"word-spacing",
"overflow-wrap",

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

@ -2657,6 +2657,11 @@ Element::ParseAttribute(int32_t aNamespaceID,
const nsAString& aValue,
nsAttrValue& aResult)
{
if (aAttribute == nsGkAtoms::lang) {
aResult.ParseAtom(aValue);
return true;
}
if (aNamespaceID == kNameSpaceID_None) {
MOZ_ASSERT(aAttribute != nsGkAtoms::_class,
"The class attribute should be preparsed and therefore should "

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

@ -348,6 +348,33 @@ nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix,
return NS_ERROR_FAILURE;
}
nsIAtom*
nsIContent::GetLang() const
{
for (const auto* content = this; content; content = content->GetParent()) {
if (!content->GetAttrCount() || !content->IsElement()) {
continue;
}
auto* element = content->AsElement();
// xml:lang has precedence over lang on HTML elements (see
// XHTML1 section C.7).
const nsAttrValue* attr =
element->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
if (!attr && element->SupportsLangAttr()) {
attr = element->GetParsedAttr(nsGkAtoms::lang);
}
if (attr) {
MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
MOZ_ASSERT(attr->GetAtomValue());
return attr->GetAtomValue();
}
}
return nullptr;
}
already_AddRefed<nsIURI>
nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const
{

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

@ -928,26 +928,18 @@ public:
/**
* Determining language. Look at the nearest ancestor element that has a lang
* attribute in the XML namespace or is an HTML/SVG element and has a lang in
* no namespace attribute. Returns false if no language was specified.
* no namespace attribute.
*
* Returns null if no language was specified. Can return the empty atom.
*/
nsIAtom* GetLang() const;
bool GetLang(nsAString& aResult) const {
for (const nsIContent* content = this; content; content = content->GetParent()) {
if (content->GetAttrCount() > 0) {
// xml:lang has precedence over lang on HTML elements (see
// XHTML1 section C.7).
bool hasAttr = content->GetAttr(kNameSpaceID_XML, nsGkAtoms::lang,
aResult);
if (!hasAttr && content->SupportsLangAttr()) {
hasAttr = content->GetAttr(kNameSpaceID_None, nsGkAtoms::lang,
aResult);
}
NS_ASSERTION(hasAttr || aResult.IsEmpty(),
"GetAttr that returns false should not make string non-empty");
if (hasAttr) {
return true;
}
}
if (auto* lang = GetLang()) {
aResult.Assign(nsDependentAtomString(lang));
return true;
}
return false;
}

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

@ -4041,6 +4041,7 @@ function* runCallbackTests(aForTests)
dumpUnexpectedNotifications(1);
input.focus();
yield waitUntilNotificationsReceived();
notifications = [];
TIP.setPendingCompositionString("foo");
TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE);
@ -4067,12 +4068,13 @@ function* runCallbackTests(aForTests)
checkPositionChangeNotification(notifications[2], description + "synthesizeMouseAtCenter(input, {}) during composition");
dumpUnexpectedNotifications(3);
input.focus();
yield waitUntilNotificationsReceived();
notifications = [];
// XXX On macOS, window.moveBy() doesn't cause notify-position-change.
// Investigate this later (although, we cannot notify position change to
// native IME on macOS).
if (!kIsMac) {
input.focus();
notifications = [];
window.moveBy(0, 10);
yield waitUntilNotificationsReceived();
is(notifications.length, 1,
@ -4080,7 +4082,6 @@ function* runCallbackTests(aForTests)
checkPositionChangeNotification(notifications[0], description + "window.moveBy(0, 10)");
dumpUnexpectedNotifications(1);
input.focus();
notifications = [];
window.moveBy(10, 0);
yield waitUntilNotificationsReceived();

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

@ -1219,17 +1219,17 @@ MapLangAttributeInto(const nsMappedAttributes* aAttributes, GenericSpecifiedValu
}
const nsAttrValue* langValue = aAttributes->GetAttr(nsGkAtoms::lang);
if (!langValue || langValue->Type() != nsAttrValue::eString) {
if (!langValue) {
return;
}
MOZ_ASSERT(langValue->Type() == nsAttrValue::eAtom);
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Font))) {
aData->SetIdentStringValueIfUnset(eCSSProperty__x_lang,
langValue->GetStringValue());
aData->SetIdentAtomValueIfUnset(eCSSProperty__x_lang,
langValue->GetAtomValue());
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Text))) {
if (!aData->PropertyIsSet(eCSSProperty_text_emphasis_position)) {
const nsAString& lang = langValue->GetStringValue();
const nsIAtom* lang = langValue->GetAtomValue();
if (nsStyleUtil::MatchesLanguagePrefix(lang, u"zh")) {
aData->SetKeywordValue(eCSSProperty_text_emphasis_position,
NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH);

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

@ -2243,36 +2243,20 @@ MediaCacheStream::SetPlaybackRate(uint32_t aBytesPerSecond)
}
nsresult
MediaCacheStream::Seek(int32_t aWhence, int64_t aOffset)
MediaCacheStream::SeekInternal(int64_t aOffset)
{
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
if (mClosed)
return NS_ERROR_FAILURE;
int64_t oldOffset = mStreamOffset;
int64_t newOffset = mStreamOffset;
switch (aWhence) {
case PR_SEEK_END:
if (mStreamLength < 0)
return NS_ERROR_FAILURE;
newOffset = mStreamLength + aOffset;
break;
case PR_SEEK_CUR:
newOffset += aOffset;
break;
case PR_SEEK_SET:
newOffset = aOffset;
break;
default:
NS_ERROR("Unknown whence");
if (aOffset < 0) {
return NS_ERROR_FAILURE;
}
if (newOffset < 0)
mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
if (mClosed) {
return NS_ERROR_FAILURE;
mStreamOffset = newOffset;
}
int64_t oldOffset = mStreamOffset;
mStreamOffset = aOffset;
LOG("Stream %p Seek to %" PRId64, this, mStreamOffset);
mMediaCache->NoteSeek(this, oldOffset);
@ -2301,11 +2285,10 @@ MediaCacheStream::Tell()
}
nsresult
MediaCacheStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
MediaCacheStream::ReadInternal(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
{
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
if (mClosed)
return NS_ERROR_FAILURE;
@ -2374,7 +2357,7 @@ MediaCacheStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
}
// No data has been read yet, so block
mon.Wait();
mMediaCache->GetReentrantMonitor().Wait();
if (mClosed) {
// We may have successfully read some data, but let's just throw
// that out.
@ -2419,9 +2402,9 @@ MediaCacheStream::ReadAt(int64_t aOffset, char* aBuffer,
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
nsresult rv = Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
nsresult rv = SeekInternal(aOffset);
if (NS_FAILED(rv)) return rv;
return Read(aBuffer, aCount, aBytes);
return ReadInternal(aBuffer, aCount, aBytes);
}
nsresult

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

@ -335,17 +335,12 @@ public:
// These methods must be called on a different thread from the main
// thread. They should always be called on the same thread for a given
// stream.
// This can fail when aWhence is NS_SEEK_END and no stream length
// is known.
nsresult Seek(int32_t aWhence, int64_t aOffset);
int64_t Tell();
// Seeks to aOffset in the stream then performs a Read operation.
// *aBytes gets the number of bytes that were actually read. This can
// be less than aCount. If the first byte of data is not in the cache,
// this will block until the data is available or the stream is
// closed, otherwise it won't block.
nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
// Seeks to aOffset in the stream then performs a Read operation. See
// 'Read' for argument and return details.
nsresult ReadAt(int64_t aOffset, char* aBuffer,
uint32_t aCount, uint32_t* aBytes);
@ -439,6 +434,13 @@ private:
// Update mPrincipal given that data has been received from aPrincipal
bool UpdatePrincipal(nsIPrincipal* aPrincipal);
nsresult SeekInternal(int64_t aOffset);
// *aBytes gets the number of bytes that were actually read. This can
// be less than aCount. If the first byte of data is not in the cache,
// this will block until the data is available or the stream is
// closed, otherwise it won't block.
nsresult ReadInternal(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
// Instance of MediaCache to use with this MediaCacheStream.
RefPtr<MediaCache> mMediaCache;

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

@ -62,19 +62,19 @@ MediaDecoderReaderWrapper::RequestAudioData()
}
RefPtr<MediaDecoderReaderWrapper::VideoDataPromise>
MediaDecoderReaderWrapper::RequestVideoData(media::TimeUnit aTimeThreshold)
MediaDecoderReaderWrapper::RequestVideoData(const media::TimeUnit& aTimeThreshold)
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(!mShutdown);
if (aTimeThreshold > media::TimeUnit::Zero()) {
aTimeThreshold += StartTime();
}
const auto threshold = aTimeThreshold > media::TimeUnit::Zero()
? aTimeThreshold + StartTime()
: aTimeThreshold;
int64_t startTime = StartTime().ToMicroseconds();
return InvokeAsync(
mReader->OwnerThread(), mReader.get(), __func__,
&MediaDecoderReader::RequestVideoData, aTimeThreshold)
&MediaDecoderReader::RequestVideoData, threshold)
->Then(mOwnerThread, __func__,
[startTime] (RefPtr<VideoData> aVideo) {
aVideo->AdjustForStartTime(startTime);

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

@ -42,7 +42,7 @@ public:
RefPtr<AudioDataPromise> RequestAudioData();
RefPtr<VideoDataPromise>
RequestVideoData(media::TimeUnit aTimeThreshold);
RequestVideoData(const media::TimeUnit& aTimeThreshold);
RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType);

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

@ -192,16 +192,24 @@ H264Converter::Shutdown()
{
mInitPromiseRequest.DisconnectIfExists();
mDecodePromiseRequest.DisconnectIfExists();
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mDrainRequest.DisconnectIfExists();
mFlushRequest.DisconnectIfExists();
mShutdownRequest.DisconnectIfExists();
mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mNeedAVCC.reset();
mShutdownRequest.DisconnectIfExists();
if (mShutdownPromise) {
// We have a shutdown in progress, return that promise instead as we can't
// shutdown a decoder twice.
return mShutdownPromise.forget();
}
return ShutdownDecoder();
}
RefPtr<ShutdownPromise>
H264Converter::ShutdownDecoder()
{
mNeedAVCC.reset();
if (mDecoder) {
RefPtr<MediaDataDecoder> decoder = mDecoder.forget();
return decoder->Shutdown();
@ -479,7 +487,7 @@ void H264Converter::FlushThenShutdownDecoder(MediaRawData* aPendingSample)
return;
}
mShutdownPromise = Shutdown();
mShutdownPromise = ShutdownDecoder();
mShutdownPromise
->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
__func__,

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

@ -76,6 +76,7 @@ private:
void DecodeFirstSample(MediaRawData* aSample);
void DrainThenFlushDecoder(MediaRawData* aPendingSample);
void FlushThenShutdownDecoder(MediaRawData* aPendingSample);
RefPtr<ShutdownPromise> ShutdownDecoder();
RefPtr<PlatformDecoderModule> mPDM;
const VideoInfo mOriginalConfig;

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

@ -3,7 +3,18 @@
<head>
</head>
<body>
<video src="789075.webm" preload="metadata" onloadedmetadata="this.src=''; document.documentElement.className=undefined"></video>
<video src="789075.webm" preload="metadata" id="v">
</video>
<!-- Note we reset 'src' to release decoder resources and cubeb streams to prevent OOM or OpenCubeb() failures. -->
<script type="application/javascript">
var video = document.getElementById("v");
video.onloadeddata = function () {
video.play();
};
video.onended = function () {
video.src="";
document.documentElement.className = undefined;
};
</script>
</body>
</html>

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

@ -653,7 +653,7 @@ skip-if = android_version == '15' # android(bug 1232305)
skip-if = true # bug 475110 - disabled since we don't play Wave files standalone
[test_autoplay.html]
[test_autoplay_contentEditable.html]
skip-if = android_version == '15' || android_version == '22' # android(bug 1232305, bug 1232318)
skip-if = android_version == '15' || android_version == '17' || android_version == '22' # android(bug 1232305, bug 1232318, bug 1372457)
[test_buffered.html]
skip-if = android_version == '15' || android_version == '22' # bug 1308388, android(bug 1232305)
[test_bug448534.html]
@ -784,7 +784,7 @@ skip-if = toolkit == 'android' # android(bug 1232305)
[test_loop.html]
skip-if = toolkit == 'android' # bug 1242112, android(bug 1232305)
[test_media_selection.html]
skip-if = android_version == '15' || android_version == '17' # bug 1330522, android(bug 1232305)
skip-if = toolkit == 'android' # bug 1372457
[test_media_sniffer.html]
skip-if = android_version == '15' || android_version == '17' # android(bug 1232305)
[test_mediarecorder_avoid_recursion.html]
@ -805,7 +805,7 @@ tags=msg
[test_mediarecorder_pause_resume_video.html]
skip-if = toolkit == 'android' # android(bug 1232305)
[test_mediarecorder_playback_can_repeat.html]
skip-if = android_version == '17' # android(bug 1232305)
skip-if = android_version == '17' || android_version == '22' # android(bug 1232305, bug 1372457)
tags=msg
[test_mediarecorder_principals.html]
skip-if = (os == 'linux' && bits == 64) || toolkit == 'android' # See bug 1266345, android(bug 1232305)
@ -886,7 +886,7 @@ skip-if = android_version == '17' # android(bug 1232305)
[test_networkState.html]
skip-if = android_version == '17' # android(bug 1232305)
[test_new_audio.html]
skip-if = android_version == '17' # android(bug 1232305)
skip-if = toolkit == 'android' # bug 1372457
[test_no_load_event.html]
skip-if = android_version == '17' # android(bug 1232305)
[test_paused.html]
@ -1041,15 +1041,15 @@ skip-if = android_version == '17' # android(bug 1232305)
[test_source_write.html]
skip-if = android_version == '17' # android(bug 1232305)
[test_standalone.html]
skip-if = android_version == '17' # android(bug 1232305)
skip-if = toolkit == 'android' # bug 1372457
[test_streams_autoplay.html]
skip-if = android_version == '17' # android(bug 1232305)
skip-if = toolkit == 'android' # bug 1372457
tags=msg capturestream
[test_streams_capture_origin.html]
skip-if = android_version == '15' || android_version == '17' || (android_version == '19' && debug) # bug 1298268, android(bug 1232305)
skip-if = toolkit == 'android' # bug 1372457
tags=msg capturestream
[test_streams_element_capture.html]
skip-if = android_version == '15' || android_version == '17' || (android_version == '19' && debug) # android(bug 1232305)
skip-if = toolkit == 'android' # bug 1372457
tags=msg capturestream
[test_streams_element_capture_createObjectURL.html]
skip-if = android_version == '15' || android_version == '17' || (android_version == '19' && debug) # android(bug 1232305)
@ -1076,13 +1076,13 @@ tags=msg capturestream
skip-if = android_version == '22' # android(bug 1368010)
tags = webvtt
[test_texttrackcue.html]
skip-if = android_version == '22' # android(bug 1368010)
skip-if = android_version == '17' || android_version == '22' # android(bug 1368010, bug 1372457)
tags = webvtt
[test_texttrackcue_moz.html]
skip-if = android_version == '22' # bug 1294111, android(bug 1368010)
tags = webvtt
[test_texttrackevents_video.html]
skip-if = android_version == '22' # android(bug 1368010)
skip-if = android_version == '17' || android_version == '22' # android(bug 1368010, bug 1372457)
tags = webvtt
[test_texttracklist.html]
skip-if = android_version == '22' # android(bug 1368010)
@ -1102,7 +1102,7 @@ skip-if = toolkit == 'android' # bug 1195570, android(bug 1232305)
skip-if = android_version == '22' # bug 1294833, android(bug 1368010)
tags = webvtt
[test_trackelementsrc.html]
skip-if = android_version == '22' # android(bug 1368010)
skip-if = android_version == '17' || android_version == '22' # android(bug 1368010, bug 1372457)
tags = webvtt
[test_trackevent.html]
skip-if = android_version == '22' # android(bug 1368010)
@ -1113,7 +1113,7 @@ tags = webvtt
[test_video_to_canvas.html]
skip-if = toolkit == 'android' # android(bug 1232305), bugs 1320418,1347953,1347954,1348140,1348386
[test_video_in_audio_element.html]
skip-if = android_version == '15' || android_version == '17' # bug 1320417, 1326326, android(bug 1232323, bug 1232305)
skip-if = toolkit == 'android' # bug 1372457
[test_videoDocumentTitle.html]
skip-if = toolkit == 'android' # android(bug 1232305)
[test_VideoPlaybackQuality.html]
@ -1126,7 +1126,7 @@ skip-if = toolkit == 'android' # android(bug 1232305)
skip-if = android_version == '22' # android(bug 1368010)
tags = webvtt
[test_webvtt_empty_displaystate.html]
skip-if = android_version == '22' # android(bug 1368010)
skip-if = android_version == '17' || android_version == '22' # android(bug 1368010, bug 1372457)
tags = webvtt
[test_webvtt_positionalign.html]
skip-if = android_version == '22' # android(bug 1368010)
@ -1159,7 +1159,7 @@ tags = suspend
skip-if = toolkit == 'android' # bug 1346705
tags = suspend
[test_background_video_resume_after_end_show_last_frame.html]
skip-if = toolkit == 'android' || (os == "win" && debug) # bug 1346705, win bug 1360452
skip-if = toolkit == 'android' # bug 1346705
tags = suspend
[test_background_video_suspend.html]
skip-if = toolkit == 'android' # android(bug 1304480)

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

@ -59,17 +59,71 @@ function testSameContent(video1, video2) {
ok(true, `${video1.token} video1 and video2 have identical content.`);
}
function waitUntilSeekToLastFrame(video) {
Log(video.token, "Waiting for seeking to the last frame");
function callSeekToNextFrame() {
video.seekToNextFrame().then(
() => {
if (!video.seenEnded) {
callSeekToNextFrame();
}
},
() => {
// When seek reaches the end, the promise is resolved before 'ended'
// is fired. The resolver calls callSeekToNextFrame() to schedule
// another seek and then the 'ended' handler calls finish() to shut
// down the MediaDecoder which will reject the seek promise. So we don't
// raise an error in this case.
ok(video.seenEnded, "seekToNextFrame() failed.");
}
);
}
return new Promise(function(resolve, reject) {
video.seenEnded = false;
video.addEventListener("ended", () => {
video.seenEnded = true;
resolve();
}, true);
callSeekToNextFrame(video);
});
}
function appendVideoToDocWithoutLoad(token, width, height) {
// Default size of (160, 120) is used by other media tests.
if (width === undefined) { width = 160; }
if (height === undefined) { height = 3*width/4; }
let v = document.createElement('video');
v.token = token;
document.body.appendChild(v);
v.width = width;
v.height = height;
return v;
}
function loadAndWaitUntilLoadedmetadata(video, url, preloadType = "metadata") {
return new Promise((resolve, reject) => {
video.preload = preloadType;
video.addEventListener("loadedmetadata", () => { resolve(); }, true);
video.src = url;
});
}
startTest({
desc: "Test resume an ended video shows the last frame.",
prefs: [
[ "media.test.video-suspend", true ],
[ "media.suspend-bkgnd-video.enabled", true ],
[ "media.suspend-bkgnd-video.delay-ms", 100 ]
[ "media.suspend-bkgnd-video.delay-ms", 100 ],
[ "media.dormant-on-pause-timeout-ms", -1],
[ "media.decoder.skip-to-next-key-frame.enabled", false]
],
tests: gDecodeSuspendTests,
runTest: (test, token) => {
let v = appendVideoToDoc(test.name, token);
let vReference = appendVideoToDoc(test.name, token);
let vReference = appendVideoToDocWithoutLoad(token+"-ref");
manager.started(token);
/*
@ -77,10 +131,10 @@ startTest({
* resuming video decoder should seek to the last frame.
* This issue was found in bug 1358057.
*/
Promise.all([waitUntilPlaying(v), waitUntilPlaying(vReference)])
Promise.all([waitUntilPlaying(v), loadAndWaitUntilLoadedmetadata(vReference, test.name, "auto")])
.then(() => testVideoSuspendsWhenHidden(v))
.then(() => {
return Promise.all([waitUntilEnded(v), waitUntilEnded(vReference)]);
return Promise.all([waitUntilEnded(v), waitUntilSeekToLastFrame(vReference)]);
})
.then(() => testVideoOnlySeekCompletedWhenShown(v))
.then(() => {

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

@ -696,6 +696,8 @@ private:
DECL_GFX_PREF(Live, "webrender.blob-images", WebRenderBlobImages, bool, false);
DECL_GFX_PREF(Live, "webrender.highlight-painted-layers", WebRenderHighlightPaintedLayers, bool, false);
DECL_GFX_PREF(Live, "widget.window-transforms.disabled", WindowTransformsDisabled, bool, false);
// WARNING:
// Please make sure that you've added your new preference to the list above in alphabetical order.
// Please do not just append it to the end of the list.

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

@ -18,7 +18,7 @@ interface mozIJSSubScriptLoader : nsISupports
* In JS, the signature looks like:
* rv loadSubScript (url [, obj] [, charset]);
* @param url the url of the sub-script, it MUST be either a file:,
* resource:, or chrome: url, and MUST be local.
* resource:, blob:, or chrome: url, and MUST be local.
* @param obj an optional object to evaluate the script onto, it
* defaults to the global object of the caller.
* @param charset optionally specifies the character encoding of
@ -34,7 +34,7 @@ interface mozIJSSubScriptLoader : nsISupports
* In JS, the signature looks like:
* rv = loadSubScript (url, optionsObject)
* @param url the url of the sub-script, it MUST be either a file:,
* resource:, or chrome: url, and MUST be local.
* resource:, blob:, or chrome: url, and MUST be local.
* @param optionsObject an object with parameters. Valid parameters are:
* - charset: specifying the character encoding of the file (default: ASCII)
* - target: an object to evaluate onto (default: global object of the caller)

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

@ -620,10 +620,6 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
JSAutoCompartment ac(cx, targetObj);
// Suppress caching if we're compiling as content.
bool ignoreCache = options.ignoreCache || principal != mSystemPrincipal;
StartupCache* cache = ignoreCache ? nullptr : StartupCache::GetSingleton();
nsCOMPtr<nsIIOService> serv = do_GetService(NS_IOSERVICE_CONTRACTID);
if (!serv) {
ReportError(cx, NS_LITERAL_CSTRING(LOAD_ERROR_NOSERVICE));
@ -655,7 +651,8 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
return NS_OK;
}
if (!scheme.EqualsLiteral("chrome") && !scheme.EqualsLiteral("app")) {
if (!scheme.EqualsLiteral("chrome") && !scheme.EqualsLiteral("app") &&
!scheme.EqualsLiteral("blob")) {
// This might be a URI to a local file, though!
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(innerURI);
@ -673,6 +670,12 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
uriStr = tmp;
}
// Suppress caching if we're compiling as content or if we're loading a
// blob: URI.
bool ignoreCache = options.ignoreCache || principal != mSystemPrincipal ||
scheme.EqualsLiteral("blob");
StartupCache* cache = ignoreCache ? nullptr : StartupCache::GetSingleton();
JSVersion version = JS_GetVersion(cx);
nsAutoCString cachePath;
cachePath.AppendPrintf("jssubloader/%d", version);

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

@ -58,5 +58,13 @@ Services.scriptloader.loadSubScriptWithOptions(resolvedBase + "utf8_subscript.js
{target: ns, charset: "UTF-8", ignoreCache: true});
src = ns.f.toSource();
isnot(src.indexOf("return 42;"), -1, "encoded subscript should have correct source");
ns = {};
let b = new Blob([
"var Exported = 17;"
]);
var blobUrl = URL.createObjectURL(b);
Services.scriptloader.loadSubScript(blobUrl, ns);
is(ns.Exported, 17, "subscript from a blob URL should work");
]]></script>
</window>

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

@ -6377,7 +6377,7 @@ PresShell::Paint(nsView* aViewToPaint,
props = Move(LayerProperties::CloneFrom(layerManager->GetRoot()));
}
MaybeSetupTransactionIdAllocator(layerManager, aViewToPaint);
MaybeSetupTransactionIdAllocator(layerManager, presContext);
if (layerManager->EndEmptyTransaction((aFlags & PAINT_COMPOSITE) ?
LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE)) {
@ -6444,7 +6444,7 @@ PresShell::Paint(nsView* aViewToPaint,
root->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(bounds));
layerManager->SetRoot(root);
}
MaybeSetupTransactionIdAllocator(layerManager, aViewToPaint);
MaybeSetupTransactionIdAllocator(layerManager, presContext);
layerManager->EndTransaction(nullptr, nullptr, (aFlags & PAINT_COMPOSITE) ?
LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE);
}

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

@ -459,6 +459,7 @@ RestyleManager::ChangeHintToString(nsChangeHint aHint)
"ReflowChangesSizeOrPosition", "UpdateComputedBSize",
"UpdateUsesOpacity", "UpdateBackgroundPosition",
"AddOrRemoveTransform", "CSSOverflowChange",
"UpdateWidgetProperties"
};
static_assert(nsChangeHint_AllHints == (1 << ArrayLength(names)) - 1,
"Name list doesn't match change hints.");
@ -1713,6 +1714,9 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
presContext->PresShell()->SynthesizeMouseMove(false);
didUpdateCursor = true;
}
if (hint & nsChangeHint_UpdateWidgetProperties) {
frame->UpdateWidgetProperties();
}
}
}

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

@ -232,6 +232,12 @@ enum nsChangeHint : uint32_t {
*/
nsChangeHint_CSSOverflowChange = 1 << 28,
/**
* Indicates that nsIFrame::UpdateWidgetProperties needs to be called.
* This is used for -moz-window-* properties.
*/
nsChangeHint_UpdateWidgetProperties = 1 << 29,
// IMPORTANT NOTE: When adding a new hint, you will need to add it to
// one of:
//
@ -247,7 +253,7 @@ enum nsChangeHint : uint32_t {
/**
* Dummy hint value for all hints. It exists for compile time check.
*/
nsChangeHint_AllHints = (1 << 29) - 1,
nsChangeHint_AllHints = (1 << 30) - 1,
};
// Redefine these operators to return nothing. This will catch any use
@ -349,7 +355,8 @@ inline nsChangeHint operator^=(nsChangeHint& aLeft, nsChangeHint aRight)
nsChangeHint_UpdatePostTransformOverflow | \
nsChangeHint_UpdateTransformLayer | \
nsChangeHint_UpdateUsesOpacity | \
nsChangeHint_AddOrRemoveTransform \
nsChangeHint_AddOrRemoveTransform | \
nsChangeHint_UpdateWidgetProperties \
)
// The change hints that are sometimes considered to be handled for descendants.

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

@ -8602,14 +8602,14 @@ void StrokeLineWithSnapping(const nsPoint& aP1, const nsPoint& aP2,
namespace layout {
void
MaybeSetupTransactionIdAllocator(layers::LayerManager* aManager, nsView* aView)
MaybeSetupTransactionIdAllocator(layers::LayerManager* aManager,
nsPresContext* aPresContext)
{
if (aManager->GetBackendType() == LayersBackend::LAYERS_CLIENT ||
aManager->GetBackendType() == LayersBackend::LAYERS_WR) {
nsRefreshDriver *refresh = aView->GetViewManager()->GetPresShell()->GetPresContext()->RefreshDriver();
aManager->SetTransactionIdAllocator(refresh);
auto backendType = aManager->GetBackendType();
if (backendType == LayersBackend::LAYERS_CLIENT ||
backendType == LayersBackend::LAYERS_WR) {
aManager->SetTransactionIdAllocator(aPresContext->RefreshDriver());
}
}

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

@ -3076,7 +3076,8 @@ void StrokeLineWithSnapping(const nsPoint& aP1, const nsPoint& aP2,
bool mOldValue;
};
void MaybeSetupTransactionIdAllocator(layers::LayerManager* aManager, nsView* aView);
void MaybeSetupTransactionIdAllocator(layers::LayerManager* aManager,
nsPresContext* aPresContext);
} // namespace layout
} // namespace mozilla

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

@ -10479,6 +10479,86 @@ nsIFrame::IsScrolledOutOfView()
return IsFrameScrolledOutOfView(this);
}
gfx::Matrix
nsIFrame::ComputeWidgetTransform()
{
const nsStyleUIReset* uiReset = StyleUIReset();
if (!uiReset->mSpecifiedWindowTransform) {
return gfx::Matrix();
}
nsStyleTransformMatrix::TransformReferenceBox refBox;
refBox.Init(GetSize());
nsPresContext* presContext = PresContext();
int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
RuleNodeCacheConditions dummy;
bool dummyBool;
gfx::Matrix4x4 matrix =
nsStyleTransformMatrix::ReadTransforms(uiReset->mSpecifiedWindowTransform->mHead,
StyleContext(),
presContext,
dummy,
refBox,
float(appUnitsPerDevPixel),
&dummyBool);
// Apply the -moz-window-transform-origin translation to the matrix.
Point transformOrigin =
nsStyleTransformMatrix::Convert2DPosition(uiReset->mWindowTransformOrigin,
refBox, appUnitsPerDevPixel);
matrix.ChangeBasis(Point3D(transformOrigin.x, transformOrigin.y, 0));
gfx::Matrix result2d;
if (!matrix.CanDraw2D(&result2d)) {
// FIXME: It would be preferable to reject non-2D transforms at parse time.
NS_WARNING("-moz-window-transform does not describe a 2D transform, "
"but only 2d transforms are supported");
return gfx::Matrix();
}
return result2d;
}
static already_AddRefed<nsIWidget>
GetWindowWidget(nsPresContext* aPresContext)
{
// We want to obtain the widget for the window. We can't use any of these
// methods: nsPresContext::GetRootWidget, nsPresContext::GetNearestWidget,
// nsIFrame::GetNearestWidget because those deal with child widgets and
// there is no parent widget connection between child widgets and the
// window widget that contains them.
nsCOMPtr<nsISupports> container = aPresContext->Document()->GetContainer();
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
if (!baseWindow) {
return nullptr;
}
nsCOMPtr<nsIWidget> mainWidget;
baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
return mainWidget.forget();
}
void
nsIFrame::UpdateWidgetProperties()
{
nsPresContext* presContext = PresContext();
if (presContext->IsRoot() || !presContext->IsChrome()) {
// Don't do anything for documents that aren't the root chrome document.
return;
}
nsIFrame* rootFrame =
presContext->FrameConstructor()->GetRootElementStyleFrame();
if (this != rootFrame) {
// Only the window's root style frame is relevant for widget properties.
return;
}
if (nsCOMPtr<nsIWidget> widget = GetWindowWidget(presContext)) {
widget->SetWindowOpacity(StyleUIReset()->mWindowOpacity);
widget->SetWindowTransform(ComputeWidgetTransform());
}
}
void
nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoStyleSet& aStyleSet,
nsStyleChangeList& aChangeList,

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

@ -3934,6 +3934,19 @@ public:
*/
bool IsScrolledOutOfView();
/**
* Computes a 2D matrix from the -moz-window-transform and
* -moz-window-transform-origin properties on aFrame.
* Values that don't result in a 2D matrix will be ignored and an identity
* matrix will be returned instead.
*/
Matrix ComputeWidgetTransform();
/**
* Applies the values from the -moz-window-* properties to the widget.
*/
virtual void UpdateWidgetProperties();
/**
* @return true iff this frame has one or more associated image requests.
* @see mozilla::css::ImageLoader.

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

@ -770,6 +770,7 @@ PropertySupportsVariant(nsCSSPropertyID aPropertyID, uint32_t aVariant)
case eCSSProperty__moz_outline_radius_topright:
case eCSSProperty__moz_outline_radius_bottomleft:
case eCSSProperty__moz_outline_radius_bottomright:
case eCSSProperty__moz_window_transform_origin:
supported = VARIANT_LP;
break;

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

@ -2285,7 +2285,7 @@ already_AddRefed<LayerManager> nsDisplayList::PaintRoot(nsDisplayListBuilder* aB
rootPresContext->CollectPluginGeometryUpdates(layerManager);
}
MaybeSetupTransactionIdAllocator(layerManager, view);
MaybeSetupTransactionIdAllocator(layerManager, presContext);
layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
aBuilder, flags);
@ -7073,47 +7073,18 @@ nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
TransformReferenceBox refBox(cbFrame);
/* Allows us to access named variables by index. */
Point3D perspectiveOrigin;
gfx::Float* coords[2] = {&perspectiveOrigin.x, &perspectiveOrigin.y};
TransformReferenceBox::DimensionGetter dimensionGetter[] =
{ &TransformReferenceBox::Width, &TransformReferenceBox::Height };
/* For both of the coordinates, if the value of perspective-origin is a
* percentage, it's relative to the size of the frame. Otherwise, if it's
* a distance, it's already computed for us!
*/
for (uint8_t index = 0; index < 2; ++index) {
/* If the -transform-origin specifies a percentage, take the percentage
* of the size of the box.
*/
const nsStyleCoord &coord = cbDisplay->mPerspectiveOrigin[index];
if (coord.GetUnit() == eStyleUnit_Calc) {
const nsStyleCoord::Calc *calc = coord.GetCalcValue();
*coords[index] =
NSAppUnitsToFloatPixels((refBox.*dimensionGetter[index])(), aAppUnitsPerPixel) *
calc->mPercent +
NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
} else if (coord.GetUnit() == eStyleUnit_Percent) {
*coords[index] =
NSAppUnitsToFloatPixels((refBox.*dimensionGetter[index])(), aAppUnitsPerPixel) *
coord.GetPercentValue();
} else {
MOZ_ASSERT(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
*coords[index] =
NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
}
}
Point perspectiveOrigin =
nsStyleTransformMatrix::Convert2DPosition(cbDisplay->mPerspectiveOrigin,
refBox, aAppUnitsPerPixel);
/* GetOffsetTo computes the offset required to move from 0,0 in cbFrame to 0,0
* in aFrame. Although we actually want the inverse of this, it's faster to
* compute this way.
*/
nsPoint frameToCbOffset = -aFrame->GetOffsetTo(cbFrame);
Point3D frameToCbGfxOffset(
Point frameToCbGfxOffset(
NSAppUnitsToFloatPixels(frameToCbOffset.x, aAppUnitsPerPixel),
NSAppUnitsToFloatPixels(frameToCbOffset.y, aAppUnitsPerPixel),
0.0f);
NSAppUnitsToFloatPixels(frameToCbOffset.y, aAppUnitsPerPixel));
/* Move the perspective origin to be relative to aFrame, instead of relative
* to the containing block which is how it was specified in the style system.
@ -7123,7 +7094,7 @@ nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame,
aOutMatrix._34 =
-1.0 / NSAppUnitsToFloatPixels(perspective, aAppUnitsPerPixel);
aOutMatrix.ChangeBasis(perspectiveOrigin);
aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
return true;
}

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

@ -27,87 +27,97 @@ class ServoSpecifiedValues;
// This provides a common interface for attribute mappers (MapAttributesIntoRule)
// to use regardless of the style backend. If the style backend is Gecko,
// this will contain an nsRuleData. If it is Servo, it will be a PropertyDeclarationBlock.
class GenericSpecifiedValues {
class GenericSpecifiedValues
{
protected:
explicit GenericSpecifiedValues(StyleBackendType aType, nsPresContext* aPresContext,
uint32_t aSIDs)
: mType(aType), mPresContext(aPresContext), mSIDs(aSIDs) {}
explicit GenericSpecifiedValues(StyleBackendType aType,
nsPresContext* aPresContext,
uint32_t aSIDs)
: mType(aType)
, mPresContext(aPresContext)
, mSIDs(aSIDs)
{}
public:
MOZ_DECL_STYLO_METHODS(nsRuleData, ServoSpecifiedValues)
MOZ_DECL_STYLO_METHODS(nsRuleData, ServoSpecifiedValues)
// Check if we already contain a certain longhand
inline bool PropertyIsSet(nsCSSPropertyID aId);
// Check if we are able to hold longhands from a given
// style struct. Pass the result of NS_STYLE_INHERIT_BIT to this
// function. Can accept multiple inherit bits or'd together.
inline bool ShouldComputeStyleStruct(uint64_t aInheritBits) {
return aInheritBits & mSIDs;
}
// Check if we already contain a certain longhand
inline bool PropertyIsSet(nsCSSPropertyID aId);
// Check if we are able to hold longhands from a given
// style struct. Pass the result of NS_STYLE_INHERIT_BIT to this
// function. Can accept multiple inherit bits or'd together.
inline bool ShouldComputeStyleStruct(uint64_t aInheritBits)
{
return aInheritBits & mSIDs;
}
inline nsPresContext* PresContext() {
return mPresContext;
}
inline nsPresContext* PresContext() { return mPresContext; }
// Set a property to an identifier (string)
inline void SetIdentStringValue(nsCSSPropertyID aId, const nsString& aValue);
inline void SetIdentStringValueIfUnset(nsCSSPropertyID aId, const nsString& aValue);
// Set a property to an identifier (string)
inline void SetIdentStringValue(nsCSSPropertyID aId, const nsString& aValue);
inline void SetIdentStringValueIfUnset(nsCSSPropertyID aId,
const nsString& aValue);
// Set a property to a keyword (usually NS_STYLE_* or StyleFoo::*)
inline void SetKeywordValue(nsCSSPropertyID aId, int32_t aValue);
inline void SetKeywordValueIfUnset(nsCSSPropertyID aId, int32_t aValue);
inline void SetIdentAtomValue(nsCSSPropertyID aId, nsIAtom* aValue);
inline void SetIdentAtomValueIfUnset(nsCSSPropertyID aId, nsIAtom* aValue);
template<typename T,
typename = typename std::enable_if<std::is_enum<T>::value>::type>
void SetKeywordValue(nsCSSPropertyID aId, T aValue) {
static_assert(mozilla::EnumTypeFitsWithin<T, int32_t>::value,
"aValue must be an enum that fits within 32 bits");
SetKeywordValue(aId, static_cast<int32_t>(aValue));
}
template<typename T,
typename = typename std::enable_if<std::is_enum<T>::value>::type>
void SetKeywordValueIfUnset(nsCSSPropertyID aId, T aValue) {
static_assert(mozilla::EnumTypeFitsWithin<T, int32_t>::value,
"aValue must be an enum that fits within 32 bits");
SetKeywordValueIfUnset(aId, static_cast<int32_t>(aValue));
}
// Set a property to a keyword (usually NS_STYLE_* or StyleFoo::*)
inline void SetKeywordValue(nsCSSPropertyID aId, int32_t aValue);
inline void SetKeywordValueIfUnset(nsCSSPropertyID aId, int32_t aValue);
// Set a property to an integer value
inline void SetIntValue(nsCSSPropertyID aId, int32_t aValue);
// Set a property to a pixel value
inline void SetPixelValue(nsCSSPropertyID aId, float aValue);
inline void SetPixelValueIfUnset(nsCSSPropertyID aId, float aValue);
template<typename T,
typename = typename std::enable_if<std::is_enum<T>::value>::type>
void SetKeywordValue(nsCSSPropertyID aId, T aValue)
{
static_assert(mozilla::EnumTypeFitsWithin<T, int32_t>::value,
"aValue must be an enum that fits within 32 bits");
SetKeywordValue(aId, static_cast<int32_t>(aValue));
}
template<typename T,
typename = typename std::enable_if<std::is_enum<T>::value>::type>
void SetKeywordValueIfUnset(nsCSSPropertyID aId, T aValue)
{
static_assert(mozilla::EnumTypeFitsWithin<T, int32_t>::value,
"aValue must be an enum that fits within 32 bits");
SetKeywordValueIfUnset(aId, static_cast<int32_t>(aValue));
}
inline void SetLengthValue(nsCSSPropertyID aId, nsCSSValue aValue);
// Set a property to an integer value
inline void SetIntValue(nsCSSPropertyID aId, int32_t aValue);
// Set a property to a pixel value
inline void SetPixelValue(nsCSSPropertyID aId, float aValue);
inline void SetPixelValueIfUnset(nsCSSPropertyID aId, float aValue);
// Set a property to a number value
inline void SetNumberValue(nsCSSPropertyID aId, float aValue);
inline void SetLengthValue(nsCSSPropertyID aId, nsCSSValue aValue);
// Set a property to a percent value
inline void SetPercentValue(nsCSSPropertyID aId, float aValue);
inline void SetPercentValueIfUnset(nsCSSPropertyID aId, float aValue);
// Set a property to a number value
inline void SetNumberValue(nsCSSPropertyID aId, float aValue);
// Set a property to `auto`
inline void SetAutoValue(nsCSSPropertyID aId);
inline void SetAutoValueIfUnset(nsCSSPropertyID aId);
// Set a property to a percent value
inline void SetPercentValue(nsCSSPropertyID aId, float aValue);
inline void SetPercentValueIfUnset(nsCSSPropertyID aId, float aValue);
// Set a property to `currentcolor`
inline void SetCurrentColor(nsCSSPropertyID aId);
inline void SetCurrentColorIfUnset(nsCSSPropertyID aId);
// Set a property to `auto`
inline void SetAutoValue(nsCSSPropertyID aId);
inline void SetAutoValueIfUnset(nsCSSPropertyID aId);
// Set a property to an RGBA nscolor value
inline void SetColorValue(nsCSSPropertyID aId, nscolor aValue);
inline void SetColorValueIfUnset(nsCSSPropertyID aId, nscolor aValue);
// Set a property to `currentcolor`
inline void SetCurrentColor(nsCSSPropertyID aId);
inline void SetCurrentColorIfUnset(nsCSSPropertyID aId);
// Set font-family to a string
inline void SetFontFamily(const nsString& aValue);
// Add a quirks-mode override to the decoration color of elements nested in <a>
inline void SetTextDecorationColorOverride();
inline void SetBackgroundImage(nsAttrValue& value);
// Set a property to an RGBA nscolor value
inline void SetColorValue(nsCSSPropertyID aId, nscolor aValue);
inline void SetColorValueIfUnset(nsCSSPropertyID aId, nscolor aValue);
const mozilla::StyleBackendType mType;
nsPresContext* const mPresContext;
const uint32_t mSIDs;
// Set font-family to a string
inline void SetFontFamily(const nsString& aValue);
// Add a quirks-mode override to the decoration color of elements nested in <a>
inline void SetTextDecorationColorOverride();
inline void SetBackgroundImage(nsAttrValue& value);
const mozilla::StyleBackendType mType;
nsPresContext* const mPresContext;
const uint32_t mSIDs;
};
} // namespace mozilla

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

@ -20,7 +20,9 @@
namespace mozilla {
MOZ_DEFINE_STYLO_METHODS(GenericSpecifiedValues, nsRuleData, ServoSpecifiedValues)
MOZ_DEFINE_STYLO_METHODS(GenericSpecifiedValues,
nsRuleData,
ServoSpecifiedValues)
bool
GenericSpecifiedValues::PropertyIsSet(nsCSSPropertyID aId)
@ -29,15 +31,34 @@ GenericSpecifiedValues::PropertyIsSet(nsCSSPropertyID aId)
}
void
GenericSpecifiedValues::SetIdentStringValue(nsCSSPropertyID aId, const nsString& aValue)
GenericSpecifiedValues::SetIdentStringValue(nsCSSPropertyID aId,
const nsString& aValue)
{
MOZ_STYLO_FORWARD(SetIdentStringValue, (aId, aValue))
}
void
GenericSpecifiedValues::SetIdentStringValueIfUnset(nsCSSPropertyID aId, const nsString& aValue)
GenericSpecifiedValues::SetIdentStringValueIfUnset(nsCSSPropertyID aId,
const nsString& aValue)
{
MOZ_STYLO_FORWARD(SetIdentStringValueIfUnset, (aId, aValue))
if (!PropertyIsSet(aId)) {
SetIdentStringValue(aId, aValue);
}
}
void
GenericSpecifiedValues::SetIdentAtomValue(nsCSSPropertyID aId, nsIAtom* aValue)
{
MOZ_STYLO_FORWARD(SetIdentAtomValue, (aId, aValue))
}
void
GenericSpecifiedValues::SetIdentAtomValueIfUnset(nsCSSPropertyID aId,
nsIAtom* aValue)
{
if (!PropertyIsSet(aId)) {
SetIdentAtomValue(aId, aValue);
}
}
void
@ -47,12 +68,14 @@ GenericSpecifiedValues::SetKeywordValue(nsCSSPropertyID aId, int32_t aValue)
// won't work with the overloaded SetKeywordValue function,
// so we copy its expansion and use SetIntValue for decltype
// instead
static_assert(!mozilla::IsSame<decltype(&MOZ_STYLO_THIS_TYPE::SetIntValue),
decltype(&MOZ_STYLO_GECKO_TYPE::SetKeywordValue)>
::value, "Gecko subclass should define its own SetKeywordValue");
static_assert(!mozilla::IsSame<decltype(&MOZ_STYLO_THIS_TYPE::SetIntValue),
decltype(&MOZ_STYLO_SERVO_TYPE::SetKeywordValue)>
::value, "Servo subclass should define its own SetKeywordValue");
static_assert(
!mozilla::IsSame<decltype(&MOZ_STYLO_THIS_TYPE::SetIntValue),
decltype(&MOZ_STYLO_GECKO_TYPE::SetKeywordValue)>::value,
"Gecko subclass should define its own SetKeywordValue");
static_assert(
!mozilla::IsSame<decltype(&MOZ_STYLO_THIS_TYPE::SetIntValue),
decltype(&MOZ_STYLO_SERVO_TYPE::SetKeywordValue)>::value,
"Servo subclass should define its own SetKeywordValue");
if (IsServo()) {
return AsServo()->SetKeywordValue(aId, aValue);
@ -61,23 +84,12 @@ GenericSpecifiedValues::SetKeywordValue(nsCSSPropertyID aId, int32_t aValue)
}
void
GenericSpecifiedValues::SetKeywordValueIfUnset(nsCSSPropertyID aId, int32_t aValue)
GenericSpecifiedValues::SetKeywordValueIfUnset(nsCSSPropertyID aId,
int32_t aValue)
{
// there are some static asserts in MOZ_STYLO_FORWARD which
// won't work with the overloaded SetKeywordValue function,
// so we copy its expansion and use SetIntValue for decltype
// instead
static_assert(!mozilla::IsSame<decltype(&MOZ_STYLO_THIS_TYPE::SetIntValue),
decltype(&MOZ_STYLO_GECKO_TYPE::SetKeywordValueIfUnset)>
::value, "Gecko subclass should define its own SetKeywordValueIfUnset");
static_assert(!mozilla::IsSame<decltype(&MOZ_STYLO_THIS_TYPE::SetIntValue),
decltype(&MOZ_STYLO_SERVO_TYPE::SetKeywordValueIfUnset)>
::value, "Servo subclass should define its own SetKeywordValueIfUnset");
if (IsServo()) {
return AsServo()->SetKeywordValueIfUnset(aId, aValue);
if (!PropertyIsSet(aId)) {
SetKeywordValue(aId, aValue);
}
return AsGecko()->SetKeywordValueIfUnset(aId, aValue);
}
void
@ -95,7 +107,9 @@ GenericSpecifiedValues::SetPixelValue(nsCSSPropertyID aId, float aValue)
void
GenericSpecifiedValues::SetPixelValueIfUnset(nsCSSPropertyID aId, float aValue)
{
MOZ_STYLO_FORWARD(SetPixelValueIfUnset, (aId, aValue))
if (!PropertyIsSet(aId)) {
SetPixelValue(aId, aValue);
}
}
void
@ -117,9 +131,12 @@ GenericSpecifiedValues::SetPercentValue(nsCSSPropertyID aId, float aValue)
}
void
GenericSpecifiedValues::SetPercentValueIfUnset(nsCSSPropertyID aId, float aValue)
GenericSpecifiedValues::SetPercentValueIfUnset(nsCSSPropertyID aId,
float aValue)
{
MOZ_STYLO_FORWARD(SetPercentValueIfUnset, (aId, aValue))
if (!PropertyIsSet(aId)) {
SetPercentValue(aId, aValue);
}
}
void
@ -131,7 +148,9 @@ GenericSpecifiedValues::SetAutoValue(nsCSSPropertyID aId)
void
GenericSpecifiedValues::SetAutoValueIfUnset(nsCSSPropertyID aId)
{
MOZ_STYLO_FORWARD(SetAutoValueIfUnset, (aId))
if (!PropertyIsSet(aId)) {
SetAutoValue(aId);
}
}
void
@ -143,7 +162,9 @@ GenericSpecifiedValues::SetCurrentColor(nsCSSPropertyID aId)
void
GenericSpecifiedValues::SetCurrentColorIfUnset(nsCSSPropertyID aId)
{
MOZ_STYLO_FORWARD(SetCurrentColorIfUnset, (aId))
if (!PropertyIsSet(aId)) {
SetCurrentColor(aId);
}
}
void
@ -153,9 +174,12 @@ GenericSpecifiedValues::SetColorValue(nsCSSPropertyID aId, nscolor aValue)
}
void
GenericSpecifiedValues::SetColorValueIfUnset(nsCSSPropertyID aId, nscolor aValue)
GenericSpecifiedValues::SetColorValueIfUnset(nsCSSPropertyID aId,
nscolor aValue)
{
MOZ_STYLO_FORWARD(SetColorValueIfUnset, (aId, aValue))
if (!PropertyIsSet(aId)) {
SetColorValue(aId, aValue);
}
}
void

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

@ -821,24 +821,24 @@ Gecko_MatchLang(RawGeckoElementBorrowed aElement,
aValue, aElement->OwnerDoc());
}
if (aOverrideLang) {
nsDependentAtomString overrideLang(aOverrideLang);
return nsCSSRuleProcessor::LangPseudoMatches(aElement, &overrideLang, true,
aValue, aElement->OwnerDoc());
}
return nsCSSRuleProcessor::LangPseudoMatches(aElement, nullptr, true,
return nsCSSRuleProcessor::LangPseudoMatches(aElement, aOverrideLang, true,
aValue, aElement->OwnerDoc());
}
nsIAtom*
Gecko_GetXMLLangValue(RawGeckoElementBorrowed aElement)
{
nsString string;
if (aElement->GetAttr(kNameSpaceID_XML, nsGkAtoms::lang, string)) {
return NS_Atomize(string).take();
const nsAttrValue* attr =
aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
if (!attr) {
return nullptr;
}
return nullptr;
MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
nsCOMPtr<nsIAtom> atom = attr->GetAtomValue();
return atom.forget().take();
}
template <typename Implementor>
@ -853,6 +853,7 @@ template <typename Implementor>
static nsIAtom*
LangValue(Implementor* aElement)
{
// TODO(emilio): Deduplicate a bit with nsIContent::GetLang().
const nsAttrValue* attr =
aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
if (!attr && aElement->SupportsLangAttr()) {
@ -863,9 +864,9 @@ LangValue(Implementor* aElement)
return nullptr;
}
nsString lang;
attr->ToString(lang);
return NS_Atomize(lang).take();
MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
nsCOMPtr<nsIAtom> atom = attr->GetAtomValue();
return atom.forget().take();
}
template <typename Implementor, typename MatchFn>

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

@ -11,7 +11,7 @@ namespace {
#define STYLE_STRUCT(name, checkdata_cb) | NS_STYLE_INHERIT_BIT(name)
const uint64_t ALL_SIDS = 0
#include "nsStyleStructList.h"
;
;
#undef STYLE_STRUCT
} // anonymous namespace
@ -22,10 +22,8 @@ ServoSpecifiedValues::ServoSpecifiedValues(nsPresContext* aContext,
RawServoDeclarationBlock* aDecl)
: GenericSpecifiedValues(StyleBackendType::Servo, aContext, ALL_SIDS)
, mDecl(aDecl)
{
}
, mDecl(aDecl)
{}
bool
ServoSpecifiedValues::PropertyIsSet(nsCSSPropertyID aId)
@ -38,7 +36,8 @@ ServoSpecifiedValues::PropertyIsSet(nsCSSPropertyID aId)
// in debug mode (this is O(n^2) behavior since Servo will traverse
// the array each time you add a new property)
MOZ_ASSERT(!Servo_DeclarationBlock_PropertyIsSet(mDecl, aId),
"Presentation attribute mappers should never attempt to set the same property twice");
"Presentation attribute mappers should never attempt to set the "
"same property twice");
return false;
}
@ -47,11 +46,17 @@ ServoSpecifiedValues::SetIdentStringValue(nsCSSPropertyID aId,
const nsString& aValue)
{
nsCOMPtr<nsIAtom> atom = NS_Atomize(aValue);
Servo_DeclarationBlock_SetIdentStringValue(mDecl, aId, atom);
SetIdentAtomValue(aId, atom);
}
void
ServoSpecifiedValues::SetIdentAtomValue(nsCSSPropertyID aId, nsIAtom* aValue)
{
Servo_DeclarationBlock_SetIdentStringValue(mDecl, aId, aValue);
if (aId == eCSSProperty__x_lang) {
// This forces the lang prefs result to be cached
// so that we can access them off main thread during traversal
mPresContext->ForceCacheLang(atom);
mPresContext->ForceCacheLang(aValue);
}
}
@ -77,7 +82,8 @@ void
ServoSpecifiedValues::SetLengthValue(nsCSSPropertyID aId, nsCSSValue aValue)
{
MOZ_ASSERT(aValue.IsLengthUnit());
Servo_DeclarationBlock_SetLengthValue(mDecl, aId, aValue.GetFloatValue(), aValue.GetUnit());
Servo_DeclarationBlock_SetLengthValue(
mDecl, aId, aValue.GetFloatValue(), aValue.GetUnit());
}
void
@ -127,6 +133,6 @@ ServoSpecifiedValues::SetBackgroundImage(nsAttrValue& aValue)
{
nsAutoString str;
aValue.ToString(str);
Servo_DeclarationBlock_SetBackgroundImage(mDecl, str,
mPresContext->Document()->DefaultStyleAttrURLData());
}
Servo_DeclarationBlock_SetBackgroundImage(
mDecl, str, mPresContext->Document()->DefaultStyleAttrURLData());
}

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

@ -17,90 +17,36 @@
namespace mozilla {
class ServoSpecifiedValues final: public GenericSpecifiedValues
class ServoSpecifiedValues final : public GenericSpecifiedValues
{
public:
ServoSpecifiedValues(nsPresContext* aContext, RawServoDeclarationBlock* aDecl);
ServoSpecifiedValues(nsPresContext* aContext,
RawServoDeclarationBlock* aDecl);
// GenericSpecifiedValues overrides
bool PropertyIsSet(nsCSSPropertyID aId);
void SetIdentStringValue(nsCSSPropertyID aId,
const nsString& aValue);
void SetIdentStringValue(nsCSSPropertyID aId, const nsString& aValue);
void SetIdentStringValueIfUnset(nsCSSPropertyID aId,
const nsString& aValue) {
if (!PropertyIsSet(aId)) {
SetIdentStringValue(aId, aValue);
}
}
void SetIdentAtomValue(nsCSSPropertyID aId, nsIAtom* aValue);
void SetKeywordValue(nsCSSPropertyID aId,
int32_t aValue);
void SetKeywordValue(nsCSSPropertyID aId, int32_t aValue);
void SetKeywordValueIfUnset(nsCSSPropertyID aId,
int32_t aValue) {
if (!PropertyIsSet(aId)) {
SetKeywordValue(aId, aValue);
}
}
void SetIntValue(nsCSSPropertyID aId, int32_t aValue);
void SetPixelValue(nsCSSPropertyID aId, float aValue);
void SetIntValue(nsCSSPropertyID aId,
int32_t aValue);
void SetLengthValue(nsCSSPropertyID aId, nsCSSValue aValue);
void SetPixelValue(nsCSSPropertyID aId,
float aValue);
void SetNumberValue(nsCSSPropertyID aId, float aValue);
void SetPixelValueIfUnset(nsCSSPropertyID aId,
float aValue) {
if (!PropertyIsSet(aId)) {
SetPixelValue(aId, aValue);
}
}
void SetLengthValue(nsCSSPropertyID aId,
nsCSSValue aValue);
void SetNumberValue(nsCSSPropertyID aId,
float aValue);
void SetPercentValue(nsCSSPropertyID aId,
float aValue);
void SetPercentValue(nsCSSPropertyID aId, float aValue);
void SetAutoValue(nsCSSPropertyID aId);
void SetAutoValueIfUnset(nsCSSPropertyID aId) {
if (!PropertyIsSet(aId)) {
SetAutoValue(aId);
}
}
void SetPercentValueIfUnset(nsCSSPropertyID aId,
float aValue) {
if (!PropertyIsSet(aId)) {
SetPercentValue(aId, aValue);
}
}
void SetCurrentColor(nsCSSPropertyID aId);
void SetCurrentColorIfUnset(nsCSSPropertyID aId) {
if (!PropertyIsSet(aId)) {
SetCurrentColor(aId);
}
}
void SetColorValue(nsCSSPropertyID aId,
nscolor aValue);
void SetColorValueIfUnset(nsCSSPropertyID aId,
nscolor aValue) {
if (!PropertyIsSet(aId)) {
SetColorValue(aId, aValue);
}
}
void SetColorValue(nsCSSPropertyID aId, nscolor aValue);
void SetFontFamily(const nsString& aValue);
void SetTextDecorationColorOverride();

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

@ -4317,6 +4317,21 @@ StyleAnimationValue::ExtractComputedValue(nsCSSPropertyID aProperty,
break;
}
case eCSSProperty__moz_window_transform_origin: {
const nsStyleUIReset *styleUIReset =
static_cast<const nsStyleUIReset*>(styleStruct);
nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
if (!StyleCoordToCSSValue(styleUIReset->mWindowTransformOrigin[0],
pair->mXValue) ||
!StyleCoordToCSSValue(styleUIReset->mWindowTransformOrigin[1],
pair->mYValue)) {
return false;
}
aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
eUnit_CSSValuePair);
break;
}
case eCSSProperty_stroke_dasharray: {
const nsStyleSVG *svg = static_cast<const nsStyleSVG*>(styleStruct);
if (!svg->mStrokeDasharray.IsEmpty()) {
@ -4594,6 +4609,31 @@ StyleAnimationValue::ExtractComputedValue(nsCSSPropertyID aProperty,
break;
}
case eCSSProperty__moz_window_transform: {
const nsStyleUIReset *uiReset =
static_cast<const nsStyleUIReset*>(styleStruct);
nsAutoPtr<nsCSSValueList> result;
if (uiReset->mSpecifiedWindowTransform) {
// Clone, and convert all lengths (not percents) to pixels.
nsCSSValueList **resultTail = getter_Transfers(result);
for (const nsCSSValueList *l = uiReset->mSpecifiedWindowTransform->mHead;
l; l = l->mNext) {
nsCSSValueList *clone = new nsCSSValueList;
*resultTail = clone;
resultTail = &clone->mNext;
SubstitutePixelValues(aStyleContext, l->mValue, clone->mValue);
}
} else {
result = new nsCSSValueList();
result->mValue.SetNoneValue();
}
aComputedValue.SetTransformValue(
new nsCSSValueSharedList(result.forget()));
break;
}
case eCSSProperty_font_variation_settings: {
auto font = static_cast<const nsStyleFont*>(styleStruct);
UniquePtr<nsCSSValuePairList> result;

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

@ -39,7 +39,7 @@ STYLE_STRUCTS = [("INHERITED",) + x for x in [
("TextReset", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
("Display", "nullptr", NORMAL_DEP + LENGTH_DEP),
("Content", "nullptr", NORMAL_DEP + LENGTH_DEP),
("UIReset", "nullptr", NORMAL_DEP),
("UIReset", "nullptr", NORMAL_DEP + LENGTH_DEP),
("Table", "nullptr", NORMAL_DEP),
("Margin", "nullptr", NORMAL_DEP + LENGTH_DEP),
("Padding", "nullptr", NORMAL_DEP + LENGTH_DEP),

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

@ -1042,7 +1042,8 @@ protected:
bool ParseListStyleType(nsCSSValue& aValue);
bool ParseMargin();
bool ParseClipPath(nsCSSValue& aValue);
bool ParseTransform(bool aIsPrefixed, bool aDisallowRelativeValues = false);
bool ParseTransform(bool aIsPrefixed, nsCSSPropertyID aProperty,
bool aDisallowRelativeValues = false);
bool ParseObjectPosition();
bool ParseOutline();
bool ParseOverflow();
@ -1385,7 +1386,7 @@ protected:
InfallibleTArray<nsCSSValue>& aOutput);
/* Functions for transform-origin/perspective-origin Parsing */
bool ParseTransformOrigin(bool aPerspective);
bool ParseTransformOrigin(nsCSSPropertyID aProperty);
/* Functions for filter parsing */
bool ParseFilter();
@ -1946,7 +1947,8 @@ CSSParserImpl::ParseTransformProperty(const nsAString& aPropValue,
css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr);
InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
bool parsedOK = ParseTransform(false, aDisallowRelativeValues);
bool parsedOK = ParseTransform(false, eCSSProperty_transform,
aDisallowRelativeValues);
// We should now be at EOF
if (parsedOK && GetToken(true)) {
parsedOK = false;
@ -11867,13 +11869,14 @@ CSSParserImpl::ParsePropertyByFunction(nsCSSPropertyID aPropID)
case eCSSProperty_will_change:
return ParseWillChange();
case eCSSProperty_transform:
return ParseTransform(false);
case eCSSProperty__moz_window_transform:
return ParseTransform(false, aPropID);
case eCSSProperty__moz_transform:
return ParseTransform(true);
return ParseTransform(true, eCSSProperty_transform);
case eCSSProperty_transform_origin:
return ParseTransformOrigin(false);
case eCSSProperty_perspective_origin:
return ParseTransformOrigin(true);
case eCSSProperty__moz_window_transform_origin:
return ParseTransformOrigin(aPropID);
case eCSSProperty_transition:
return ParseTransition();
case eCSSProperty_animation:
@ -16232,9 +16235,15 @@ CSSParserImpl::ParseSingleTransform(bool aIsPrefixed,
/* Parses a transform property list by continuously reading in properties
* and constructing a matrix from it.
* aProperty can be transform or -moz-window-transform.
* FIXME: For -moz-window-transform, it would be nice to reject non-2d
* transforms at parse time, because the implementation only supports 2d
* transforms. Instead, at the moment, non-2d transforms are treated as the
* identity transform very late in the pipeline.
*/
bool
CSSParserImpl::ParseTransform(bool aIsPrefixed, bool aDisallowRelativeValues)
CSSParserImpl::ParseTransform(bool aIsPrefixed, nsCSSPropertyID aProperty,
bool aDisallowRelativeValues)
{
nsCSSValue value;
// 'inherit', 'initial', 'unset' and 'none' must be alone
@ -16256,7 +16265,7 @@ CSSParserImpl::ParseTransform(bool aIsPrefixed, bool aDisallowRelativeValues)
cur = cur->mNext;
}
}
AppendValue(eCSSProperty_transform, value);
AppendValue(aProperty, value);
return true;
}
@ -16531,17 +16540,12 @@ CSSParserImpl::ParseShapeOutside(nsCSSValue& aValue)
aValue, nsCSSProps::kShapeOutsideShapeBoxKTable);
}
bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
bool CSSParserImpl::ParseTransformOrigin(nsCSSPropertyID aProperty)
{
nsCSSValuePair position;
if (!ParseBoxPositionValues(position, true))
return false;
nsCSSPropertyID prop = eCSSProperty_transform_origin;
if (aPerspective) {
prop = eCSSProperty_perspective_origin;
}
// Unlike many other uses of pairs, this position should always be stored
// as a pair, even if the values are the same, so it always serializes as
// a pair, and to keep the computation code simple.
@ -16550,10 +16554,10 @@ bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
position.mXValue.GetUnit() == eCSSUnit_Unset) {
MOZ_ASSERT(position.mXValue == position.mYValue,
"inherit/initial/unset only half?");
AppendValue(prop, position.mXValue);
AppendValue(aProperty, position.mXValue);
} else {
nsCSSValue value;
if (aPerspective) {
if (aProperty != eCSSProperty_transform_origin) {
value.SetPairValue(position.mXValue, position.mYValue);
} else {
nsCSSValue depth;
@ -16567,7 +16571,7 @@ bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
value.SetTripletValue(position.mXValue, position.mYValue, depth);
}
AppendValue(prop, value);
AppendValue(aProperty, value);
}
return true;
}

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

@ -4494,6 +4494,41 @@ CSS_PROP_UIRESET(
kWindowShadowKTable,
CSS_PROP_NO_OFFSET,
eStyleAnimType_None)
CSS_PROP_UIRESET(
-moz-window-opacity,
_moz_window_opacity,
CSS_PROP_DOMPROP_PREFIXED(WindowOpacity),
CSS_PROPERTY_INTERNAL | CSS_PROPERTY_PARSE_VALUE,
"",
VARIANT_HN,
nullptr,
offsetof(nsStyleUIReset, mWindowOpacity),
eStyleAnimType_float)
CSS_PROP_UIRESET(
-moz-window-transform,
_moz_window_transform,
CSS_PROP_DOMPROP_PREFIXED(WindowTransform),
CSS_PROPERTY_INTERNAL |
CSS_PROPERTY_PARSE_FUNCTION |
CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
"",
0,
nullptr,
offsetof(nsStyleUIReset, mSpecifiedWindowTransform),
eStyleAnimType_Custom)
CSS_PROP_UIRESET(
-moz-window-transform-origin,
_moz_window_transform_origin,
CSS_PROP_DOMPROP_PREFIXED(WindowTransformOrigin),
CSS_PROPERTY_INTERNAL |
CSS_PROPERTY_PARSE_FUNCTION |
CSS_PROPERTY_STORES_CALC |
CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
"",
0,
kImageLayerPositionKTable,
CSS_PROP_NO_OFFSET,
eStyleAnimType_Custom)
#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL
CSS_PROP_TEXT(
word-break,

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

@ -1678,7 +1678,7 @@ IsSignificantChildMaybeThreadSafe(const nsIContent* aContent,
/* static */ bool
nsCSSRuleProcessor::LangPseudoMatches(const mozilla::dom::Element* aElement,
const nsAString* aOverrideLang,
const nsIAtom* aOverrideLang,
bool aHasOverrideLang,
const char16_t* aString,
const nsIDocument* aDocument)
@ -1692,52 +1692,42 @@ nsCSSRuleProcessor::LangPseudoMatches(const mozilla::dom::Element* aElement,
// this is currently no property and since the language is inherited
// from the parent we have to be prepared to look at all parent
// nodes. The language itself is encoded in the LANG attribute.
bool haveLanguage = false;
nsAutoString language;
if (aHasOverrideLang) {
if (aOverrideLang) {
language = *aOverrideLang;
haveLanguage = true;
}
} else {
haveLanguage = aElement->GetLang(language);
}
if (haveLanguage) {
return nsStyleUtil::DashMatchCompare(language,
if (auto* language = aHasOverrideLang ? aOverrideLang : aElement->GetLang()) {
return nsStyleUtil::DashMatchCompare(nsDependentAtomString(language),
nsDependentString(aString),
nsASCIICaseInsensitiveStringComparator());
}
if (aDocument) {
// Try to get the language from the HTTP header or if this
// is missing as well from the preferences.
// The content language can be a comma-separated list of
// language codes.
aDocument->GetContentLanguage(language);
nsDependentString langString(aString);
language.StripWhitespace();
int32_t begin = 0;
int32_t len = language.Length();
while (begin < len) {
int32_t end = language.FindChar(char16_t(','), begin);
if (end == kNotFound) {
end = len;
}
if (nsStyleUtil::DashMatchCompare(Substring(language, begin,
end-begin),
langString,
nsASCIICaseInsensitiveStringComparator())) {
return true;
}
begin = end + 1;
}
if (begin < len) {
return true;
}
if (!aDocument) {
return false;
}
// Try to get the language from the HTTP header or if this
// is missing as well from the preferences.
// The content language can be a comma-separated list of
// language codes.
nsAutoString language;
aDocument->GetContentLanguage(language);
nsDependentString langString(aString);
language.StripWhitespace();
int32_t begin = 0;
int32_t len = language.Length();
while (begin < len) {
int32_t end = language.FindChar(char16_t(','), begin);
if (end == kNotFound) {
end = len;
}
if (nsStyleUtil::DashMatchCompare(Substring(language, begin, end - begin),
langString,
nsASCIICaseInsensitiveStringComparator())) {
return true;
}
begin = end + 1;
}
if (begin < len) {
return true;
}
return false;
}

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

@ -172,7 +172,7 @@ public:
bool* const aDependence = nullptr);
static bool LangPseudoMatches(const mozilla::dom::Element* aElement,
const nsAString* aOverrideLang,
const nsIAtom* aOverrideLang,
bool aHasOverrideLang,
const char16_t* aString,
const nsIDocument* aDocument);

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

@ -1536,57 +1536,11 @@ nsComputedDOMStyle::DoGetTransformStyle()
return val.forget();
}
/* If the property is "none", hand back "none" wrapped in a value.
* Otherwise, compute the aggregate transform matrix and hands it back in a
* "matrix" wrapper.
*/
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTransform()
{
/* First, get the display data. We'll need it. */
const nsStyleDisplay* display = StyleDisplay();
/* If there are no transforms, then we should construct a single-element
* entry and hand it back.
*/
if (!display->mSpecifiedTransform) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
/* Set it to "none." */
val->SetIdent(eCSSKeyword_none);
return val.forget();
}
/* Otherwise, we need to compute the current value of the transform matrix,
* store it in a string, and hand it back to the caller.
*/
/* Use the inner frame for the reference box. If we don't have an inner
* frame we use empty dimensions to allow us to continue (and percentage
* values in the transform will simply give broken results).
* TODO: There is no good way for us to represent the case where there's no
* frame, which is problematic. The reason is that when we have percentage
* transforms, there are a total of four stored matrix entries that influence
* the transform based on the size of the element. However, this poses a
* problem, because only two of these values can be explicitly referenced
* using the named transforms. Until a real solution is found, we'll just
* use this approach.
*/
nsStyleTransformMatrix::TransformReferenceBox refBox(mInnerFrame,
nsSize(0, 0));
RuleNodeCacheConditions dummy;
bool dummyBool;
gfx::Matrix4x4 matrix =
nsStyleTransformMatrix::ReadTransforms(display->mSpecifiedTransform->mHead,
mStyleContext,
mStyleContext->PresContext(),
dummy,
refBox,
float(mozilla::AppUnitsPerCSSPixel()),
&dummyBool);
return MatrixToCSSValue(matrix);
return GetTransformValue(display->mSpecifiedTransform);
}
already_AddRefed<CSSValue>
@ -4226,6 +4180,41 @@ nsComputedDOMStyle::DoGetWindowShadow()
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetWindowOpacity()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetNumber(StyleUIReset()->mWindowOpacity);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetWindowTransform()
{
const nsStyleUIReset* uiReset = StyleUIReset();
return GetTransformValue(uiReset->mSpecifiedWindowTransform);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetWindowTransformOrigin()
{
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
const nsStyleUIReset* uiReset = StyleUIReset();
RefPtr<nsROCSSPrimitiveValue> originX = new nsROCSSPrimitiveValue;
SetValueToCoord(originX, uiReset->mWindowTransformOrigin[0], false,
&nsComputedDOMStyle::GetFrameBoundsWidthForTransform);
valueList->AppendCSSValue(originX.forget());
RefPtr<nsROCSSPrimitiveValue> originY = new nsROCSSPrimitiveValue;
SetValueToCoord(originY, uiReset->mWindowTransformOrigin[1], false,
&nsComputedDOMStyle::GetFrameBoundsHeightForTransform);
valueList->AppendCSSValue(originY.forget());
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetWordBreak()
{
@ -5863,6 +5852,56 @@ nsComputedDOMStyle::GetSVGPaintFor(bool aFill)
return val.forget();
}
/* If the property is "none", hand back "none" wrapped in a value.
* Otherwise, compute the aggregate transform matrix and hands it back in a
* "matrix" wrapper.
*/
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetTransformValue(nsCSSValueSharedList* aSpecifiedTransform)
{
/* If there are no transforms, then we should construct a single-element
* entry and hand it back.
*/
if (!aSpecifiedTransform) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
/* Set it to "none." */
val->SetIdent(eCSSKeyword_none);
return val.forget();
}
/* Otherwise, we need to compute the current value of the transform matrix,
* store it in a string, and hand it back to the caller.
*/
/* Use the inner frame for the reference box. If we don't have an inner
* frame we use empty dimensions to allow us to continue (and percentage
* values in the transform will simply give broken results).
* TODO: There is no good way for us to represent the case where there's no
* frame, which is problematic. The reason is that when we have percentage
* transforms, there are a total of four stored matrix entries that influence
* the transform based on the size of the element. However, this poses a
* problem, because only two of these values can be explicitly referenced
* using the named transforms. Until a real solution is found, we'll just
* use this approach.
*/
nsStyleTransformMatrix::TransformReferenceBox refBox(mInnerFrame,
nsSize(0, 0));
RuleNodeCacheConditions dummy;
bool dummyBool;
gfx::Matrix4x4 matrix =
nsStyleTransformMatrix::ReadTransforms(aSpecifiedTransform->mHead,
mStyleContext,
mStyleContext->PresContext(),
dummy,
refBox,
float(mozilla::AppUnitsPerCSSPixel()),
&dummyBool);
return MatrixToCSSValue(matrix);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetFill()
{

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

@ -214,6 +214,8 @@ private:
already_AddRefed<CSSValue> GetSVGPaintFor(bool aFill);
already_AddRefed<CSSValue> GetTransformValue(nsCSSValueSharedList* aSpecifiedTransform);
// Appends all aLineNames (may be empty) space-separated to aResult.
void AppendGridLineNames(nsString& aResult,
const nsTArray<nsString>& aLineNames);
@ -528,6 +530,9 @@ private:
already_AddRefed<CSSValue> DoGetUserModify();
already_AddRefed<CSSValue> DoGetUserSelect();
already_AddRefed<CSSValue> DoGetWindowDragging();
already_AddRefed<CSSValue> DoGetWindowOpacity();
already_AddRefed<CSSValue> DoGetWindowTransform();
already_AddRefed<CSSValue> DoGetWindowTransformOrigin();
/* Column properties */
already_AddRefed<CSSValue> DoGetColumnCount();

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

@ -305,6 +305,9 @@ COMPUTED_STYLE_PROP(_moz_user_modify, UserModify)
COMPUTED_STYLE_PROP(_moz_user_select, UserSelect)
COMPUTED_STYLE_PROP(_moz_window_dragging, WindowDragging)
COMPUTED_STYLE_PROP(_moz_window_shadow, WindowShadow)
COMPUTED_STYLE_PROP(_moz_window_opacity, WindowOpacity)
COMPUTED_STYLE_PROP(_moz_window_transform, WindowTransform)
COMPUTED_STYLE_PROP(_moz_window_transform_origin, WindowTransformOrigin)
/* ********************************** *\
* Implementations of -webkit- styles *

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

@ -156,7 +156,8 @@ nsHTMLStyleSheet::LangRule::MapRuleInfoInto(nsRuleData* aRuleData)
if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) {
nsCSSValue* lang = aRuleData->ValueForLang();
if (lang->GetUnit() == eCSSUnit_Null) {
lang->SetStringValue(mLang, eCSSUnit_Ident);
nsCOMPtr<nsIAtom> langAtom = mLang;
lang->SetAtomIdentValue(langAtom.forget());
}
}
}
@ -184,7 +185,7 @@ nsHTMLStyleSheet::LangRule::List(FILE* out, int32_t aIndent) const
str.AppendLiteral(" ");
}
str.AppendLiteral("[lang rule] { language: \"");
AppendUTF16toUTF8(mLang, str);
AppendUTF16toUTF8(nsDependentAtomString(mLang), str);
str.AppendLiteral("\" }\n");
fprintf_stderr(out, "%s", str.get());
}
@ -242,8 +243,8 @@ struct LangRuleTableEntry : public PLDHashEntryHdr {
static PLDHashNumber
LangRuleTable_HashKey(const void *key)
{
const nsString *lang = static_cast<const nsString*>(key);
return HashString(*lang);
auto* lang = static_cast<const nsIAtom*>(key);
return lang->hash();
}
static void
@ -258,21 +259,21 @@ LangRuleTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
static bool
LangRuleTable_MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
{
const nsString *lang = static_cast<const nsString*>(key);
auto* lang = static_cast<const nsIAtom*>(key);
const LangRuleTableEntry *entry = static_cast<const LangRuleTableEntry*>(hdr);
return entry->mRule->mLang == *lang;
return entry->mRule->mLang == lang;
}
static void
LangRuleTable_InitEntry(PLDHashEntryHdr *hdr, const void *key)
{
const nsString *lang = static_cast<const nsString*>(key);
auto* lang = static_cast<const nsIAtom*>(key);
LangRuleTableEntry *entry = new (KnownNotNull, hdr) LangRuleTableEntry();
LangRuleTableEntry* entry = new (KnownNotNull, hdr) LangRuleTableEntry();
// Create the unique rule for this language
entry->mRule = new nsHTMLStyleSheet::LangRule(*lang);
entry->mRule = new nsHTMLStyleSheet::LangRule(const_cast<nsIAtom*>(lang));
}
static const PLDHashTableOps LangRuleTable_Ops = {
@ -350,16 +351,17 @@ nsHTMLStyleSheet::RulesMatching(ElementRuleProcessorData* aData)
// http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#language
// says that the xml:lang attribute overrides HTML's lang attribute,
// so we need to do this after WalkContentStyleRules.
nsString lang;
if (aData->mElement->GetAttr(kNameSpaceID_XML, nsGkAtoms::lang, lang)) {
ruleWalker->Forward(LangRuleFor(lang));
const nsAttrValue* langAttr =
aData->mElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
if (langAttr) {
MOZ_ASSERT(langAttr->Type() == nsAttrValue::eAtom);
ruleWalker->Forward(LangRuleFor(langAttr->GetAtomValue()));
}
// Set the language to "x-math" on the <math> element, so that appropriate
// font settings are used for MathML.
if (aData->mElement->IsMathMLElement(nsGkAtoms::math)) {
nsGkAtoms::x_math->ToString(lang);
ruleWalker->Forward(LangRuleFor(lang));
ruleWalker->Forward(LangRuleFor(nsGkAtoms::x_math));
}
}
@ -589,10 +591,10 @@ nsHTMLStyleSheet::CalculateMappedServoDeclarations(nsPresContext* aPresContext)
}
nsIStyleRule*
nsHTMLStyleSheet::LangRuleFor(const nsString& aLanguage)
nsHTMLStyleSheet::LangRuleFor(const nsIAtom* aLanguage)
{
auto entry =
static_cast<LangRuleTableEntry*>(mLangRuleTable.Add(&aLanguage, fallible));
static_cast<LangRuleTableEntry*>(mLangRuleTable.Add(aLanguage, fallible));
if (!entry) {
NS_ASSERTION(false, "out of memory");
return nullptr;

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

@ -14,6 +14,7 @@
#include "nsColor.h"
#include "nsCOMPtr.h"
#include "nsIAtom.h"
#include "nsIStyleRule.h"
#include "nsIStyleRuleProcessor.h"
#include "PLDHashTable.h"
@ -78,9 +79,9 @@ public:
// and converting the ruledata to Servo specified values.
void CalculateMappedServoDeclarations(nsPresContext* aPresContext);
nsIStyleRule* LangRuleFor(const nsString& aLanguage);
nsIStyleRule* LangRuleFor(const nsIAtom* aLanguage);
private:
private:
nsHTMLStyleSheet(const nsHTMLStyleSheet& aCopy) = delete;
nsHTMLStyleSheet& operator=(const nsHTMLStyleSheet& aCopy) = delete;
@ -169,7 +170,7 @@ public: // for mLangRuleTable structures only
private:
~LangRule() {}
public:
explicit LangRule(const nsSubstring& aLang) : mLang(aLang) {}
explicit LangRule(nsIAtom* aLang) : mLang(aLang) {}
NS_DECL_ISUPPORTS
@ -182,7 +183,7 @@ public: // for mLangRuleTable structures only
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
#endif
nsString mLang;
nsCOMPtr<nsIAtom> mLang;
};
private:

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

@ -22,18 +22,19 @@ nsRuleData::GetPoisonOffset()
"expect uintptr_t and size_t to be the same size");
static_assert(uintptr_t(-1) > uintptr_t(0),
"expect uintptr_t to be unsigned");
static_assert(size_t(-1) > size_t(0),
"expect size_t to be unsigned");
static_assert(size_t(-1) > size_t(0), "expect size_t to be unsigned");
uintptr_t framePoisonValue = mozPoisonValue();
return size_t(framePoisonValue - uintptr_t(mValueStorage)) /
sizeof(nsCSSValue);
}
nsRuleData::nsRuleData(uint32_t aSIDs, nsCSSValue* aValueStorage,
nsPresContext* aContext, nsStyleContext* aStyleContext)
: GenericSpecifiedValues(StyleBackendType::Gecko, aContext, aSIDs),
mStyleContext(aStyleContext),
mValueStorage(aValueStorage)
nsRuleData::nsRuleData(uint32_t aSIDs,
nsCSSValue* aValueStorage,
nsPresContext* aContext,
nsStyleContext* aStyleContext)
: GenericSpecifiedValues(StyleBackendType::Gecko, aContext, aSIDs)
, mStyleContext(aStyleContext)
, mValueStorage(aValueStorage)
{
#ifndef MOZ_VALGRIND
size_t framePoisonOffset = GetPoisonOffset();

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

@ -26,7 +26,7 @@ struct nsRuleData;
typedef void (*nsPostResolveFunc)(void* aStyleStruct, nsRuleData* aData);
struct nsRuleData final: mozilla::GenericSpecifiedValues
struct nsRuleData final : mozilla::GenericSpecifiedValues
{
mozilla::RuleNodeCacheConditions mConditions;
bool mIsImportantRule;
@ -49,8 +49,10 @@ struct nsRuleData final: mozilla::GenericSpecifiedValues
nsAutoPtr<mozilla::CSSVariableDeclarations> mVariables;
nsRuleData(uint32_t aSIDs, nsCSSValue* aValueStorage,
nsPresContext* aContext, nsStyleContext* aStyleContext);
nsRuleData(uint32_t aSIDs,
nsCSSValue* aValueStorage,
nsPresContext* aContext,
nsStyleContext* aStyleContext);
#ifdef DEBUG
~nsRuleData();
@ -77,13 +79,13 @@ struct nsRuleData final: mozilla::GenericSpecifiedValues
// include that here since it includes us.
MOZ_ASSERT(mSIDs & (1 << sid),
"calling nsRuleData::ValueFor on property not in mSIDs");
MOZ_ASSERT(indexInStruct != size_t(-1),
"logical property");
MOZ_ASSERT(indexInStruct != size_t(-1), "logical property");
return mValueStorage + mValueOffsets[sid] + indexInStruct;
}
const nsCSSValue* ValueFor(nsCSSPropertyID aProperty) const {
const nsCSSValue* ValueFor(nsCSSPropertyID aProperty) const
{
return const_cast<nsRuleData*>(this)->ValueFor(aProperty);
}
@ -120,65 +122,50 @@ struct nsRuleData final: mozilla::GenericSpecifiedValues
#undef CSS_PROP_PUBLIC_OR_PRIVATE
// GenericSpecifiedValues overrides
bool PropertyIsSet(nsCSSPropertyID aId) {
bool PropertyIsSet(nsCSSPropertyID aId)
{
return ValueFor(aId)->GetUnit() != eCSSUnit_Null;
}
void SetIdentStringValue(nsCSSPropertyID aId,
const nsString& aValue) {
void SetIdentStringValue(nsCSSPropertyID aId, const nsString& aValue)
{
ValueFor(aId)->SetStringValue(aValue, eCSSUnit_Ident);
}
void SetIdentStringValueIfUnset(nsCSSPropertyID aId,
const nsString& aValue) {
if (!PropertyIsSet(aId)) {
SetIdentStringValue(aId, aValue);
}
void SetIdentAtomValue(nsCSSPropertyID aId, nsIAtom* aValue)
{
nsCOMPtr<nsIAtom> atom = aValue;
ValueFor(aId)->SetAtomIdentValue(atom.forget());
}
void SetKeywordValue(nsCSSPropertyID aId,
int32_t aValue) {
void SetKeywordValue(nsCSSPropertyID aId, int32_t aValue)
{
ValueFor(aId)->SetIntValue(aValue, eCSSUnit_Enumerated);
}
void SetKeywordValueIfUnset(nsCSSPropertyID aId,
int32_t aValue) {
if (!PropertyIsSet(aId)) {
SetKeywordValue(aId, aValue);
}
}
void SetIntValue(nsCSSPropertyID aId,
int32_t aValue) {
void SetIntValue(nsCSSPropertyID aId, int32_t aValue)
{
ValueFor(aId)->SetIntValue(aValue, eCSSUnit_Integer);
}
void SetPixelValue(nsCSSPropertyID aId,
float aValue) {
void SetPixelValue(nsCSSPropertyID aId, float aValue)
{
ValueFor(aId)->SetFloatValue(aValue, eCSSUnit_Pixel);
}
void SetPixelValueIfUnset(nsCSSPropertyID aId,
float aValue) {
if (!PropertyIsSet(aId)) {
SetPixelValue(aId, aValue);
}
}
void SetLengthValue(nsCSSPropertyID aId,
nsCSSValue aValue) {
void SetLengthValue(nsCSSPropertyID aId, nsCSSValue aValue)
{
nsCSSValue* val = ValueFor(aId);
*val = aValue;
}
void SetNumberValue(nsCSSPropertyID aId,
float aValue) {
void SetNumberValue(nsCSSPropertyID aId, float aValue)
{
ValueFor(aId)->SetFloatValue(aValue, eCSSUnit_Number);
}
void SetPercentValue(nsCSSPropertyID aId,
float aValue) {
void SetPercentValue(nsCSSPropertyID aId, float aValue)
{
ValueFor(aId)->SetPercentValue(aValue);
}
@ -186,48 +173,22 @@ struct nsRuleData final: mozilla::GenericSpecifiedValues
ValueFor(aId)->SetAutoValue();
}
void SetAutoValueIfUnset(nsCSSPropertyID aId) {
if (!PropertyIsSet(aId)) {
SetAutoValue(aId);
}
}
void SetPercentValueIfUnset(nsCSSPropertyID aId,
float aValue) {
if (!PropertyIsSet(aId)) {
SetPercentValue(aId, aValue);
}
}
void SetCurrentColor(nsCSSPropertyID aId) {
void SetCurrentColor(nsCSSPropertyID aId)
{
ValueFor(aId)->SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
}
void SetCurrentColorIfUnset(nsCSSPropertyID aId) {
if (!PropertyIsSet(aId)) {
SetCurrentColor(aId);
}
}
void SetColorValue(nsCSSPropertyID aId,
nscolor aValue) {
void SetColorValue(nsCSSPropertyID aId, nscolor aValue)
{
ValueFor(aId)->SetColorValue(aValue);
}
void SetColorValueIfUnset(nsCSSPropertyID aId,
nscolor aValue) {
if (!PropertyIsSet(aId)) {
SetColorValue(aId, aValue);
}
}
void SetFontFamily(const nsString& aValue);
void SetTextDecorationColorOverride();
void SetBackgroundImage(nsAttrValue& aValue);
private:
inline size_t GetPoisonOffset();
};
#endif

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

@ -1740,6 +1740,47 @@ SetFactor(const nsCSSValue& aValue, float& aField, RuleNodeCacheConditions& aCon
NS_NOTREACHED("SetFactor: inappropriate unit");
}
static void
SetTransformValue(const nsCSSValue& aValue,
RefPtr<nsCSSValueSharedList>& aField,
RuleNodeCacheConditions& aConditions,
nsCSSValueSharedList* const aParentValue)
{
/* Convert the nsCSSValueList into an nsTArray<nsTransformFunction *>. */
switch (aValue.GetUnit()) {
case eCSSUnit_Null:
break;
case eCSSUnit_Initial:
case eCSSUnit_Unset:
case eCSSUnit_None:
aField = nullptr;
break;
case eCSSUnit_Inherit:
aField = aParentValue;
aConditions.SetUncacheable();
break;
case eCSSUnit_SharedList: {
nsCSSValueSharedList* list = aValue.GetSharedListValue();
nsCSSValueList* head = list->mHead;
MOZ_ASSERT(head, "transform list must have at least one item");
// can get a _None in here from transform animation
if (head->mValue.GetUnit() == eCSSUnit_None) {
MOZ_ASSERT(head->mNext == nullptr, "none must be alone");
aField = nullptr;
} else {
aField = list;
}
break;
}
default:
MOZ_ASSERT(false, "unrecognized transform unit");
}
}
void*
nsRuleNode::operator new(size_t sz, nsPresContext* aPresContext)
{
@ -3565,12 +3606,8 @@ nsRuleNode::SetFont(nsPresContext* aPresContext, GeckoStyleContext* aContext,
// -x-lang: string, inherit
// This is not a real CSS property, it is an HTML attribute mapped to CSS.
const nsCSSValue* langValue = aRuleData->ValueForLang();
if (eCSSUnit_Ident == langValue->GetUnit()) {
nsAutoString lang;
langValue->GetStringValue(lang);
nsContentUtils::ASCIIToLower(lang);
aFont->mLanguage = NS_Atomize(lang);
if (eCSSUnit_AtomIdent == langValue->GetUnit()) {
aFont->mLanguage = langValue->GetAtomValue();
aFont->mExplicitLanguage = true;
}
@ -5275,6 +5312,34 @@ nsRuleNode::ComputeUIResetData(void* aStartStruct,
parentUI->mWindowShadow,
NS_STYLE_WINDOW_SHADOW_DEFAULT);
// -moz-window-opacity: factor, inherit, initial
SetFactor(*aRuleData->ValueForWindowOpacity(),
ui->mWindowOpacity, conditions,
parentUI->mWindowOpacity, 1.0f,
SETFCT_OPACITY | SETFCT_UNSET_INITIAL);
// -moz-window-transform
SetTransformValue(*aRuleData->ValueForWindowTransform(),
ui->mSpecifiedWindowTransform, conditions,
parentUI->mSpecifiedWindowTransform);
// -moz-window-transform-origin
const nsCSSValue* windowTransformOriginValue =
aRuleData->ValueForWindowTransformOrigin();
if (windowTransformOriginValue->GetUnit() != eCSSUnit_Null) {
mozilla::DebugOnly<bool> result =
SetPairCoords(*windowTransformOriginValue,
ui->mWindowTransformOrigin[0],
ui->mWindowTransformOrigin[1],
parentUI->mWindowTransformOrigin[0],
parentUI->mWindowTransformOrigin[1],
SETCOORD_LPH | SETCOORD_INITIAL_HALF |
SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC |
SETCOORD_UNSET_INITIAL,
aContext, mPresContext, conditions);
NS_ASSERTION(result, "Malformed -moz-window-transform-origin parse!");
}
COMPUTE_END_RESET(UIReset, ui)
}
@ -6342,40 +6407,9 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct,
}
}
/* Convert the nsCSSValueList into an nsTArray<nsTransformFunction *>. */
const nsCSSValue* transformValue = aRuleData->ValueForTransform();
switch (transformValue->GetUnit()) {
case eCSSUnit_Null:
break;
case eCSSUnit_Initial:
case eCSSUnit_Unset:
case eCSSUnit_None:
display->mSpecifiedTransform = nullptr;
break;
case eCSSUnit_Inherit:
display->mSpecifiedTransform = parentDisplay->mSpecifiedTransform;
conditions.SetUncacheable();
break;
case eCSSUnit_SharedList: {
nsCSSValueSharedList* list = transformValue->GetSharedListValue();
nsCSSValueList* head = list->mHead;
MOZ_ASSERT(head, "transform list must have at least one item");
// can get a _None in here from transform animation
if (head->mValue.GetUnit() == eCSSUnit_None) {
MOZ_ASSERT(head->mNext == nullptr, "none must be alone");
display->mSpecifiedTransform = nullptr;
} else {
display->mSpecifiedTransform = list;
}
break;
}
default:
MOZ_ASSERT(false, "unrecognized transform unit");
}
SetTransformValue(*aRuleData->ValueForTransform(),
display->mSpecifiedTransform, conditions,
parentDisplay->mSpecifiedTransform);
/* Convert the nsCSSValueList into a will-change bitfield for fast lookup */
const nsCSSValue* willChangeValue = aRuleData->ValueForWillChange();

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

@ -4326,6 +4326,10 @@ nsStyleUIReset::nsStyleUIReset(const nsPresContext* aContext)
, mIMEMode(NS_STYLE_IME_MODE_AUTO)
, mWindowDragging(StyleWindowDragging::Default)
, mWindowShadow(NS_STYLE_WINDOW_SHADOW_DEFAULT)
, mWindowOpacity(1.0)
, mSpecifiedWindowTransform(nullptr)
, mWindowTransformOrigin{ {0.5f, eStyleUnit_Percent}, // Transform is centered on origin
{0.5f, eStyleUnit_Percent} }
{
MOZ_COUNT_CTOR(nsStyleUIReset);
}
@ -4336,6 +4340,10 @@ nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
, mIMEMode(aSource.mIMEMode)
, mWindowDragging(aSource.mWindowDragging)
, mWindowShadow(aSource.mWindowShadow)
, mWindowOpacity(aSource.mWindowOpacity)
, mSpecifiedWindowTransform(aSource.mSpecifiedWindowTransform)
, mWindowTransformOrigin{ aSource.mWindowTransformOrigin[0],
aSource.mWindowTransformOrigin[1] }
{
MOZ_COUNT_CTOR(nsStyleUIReset);
}
@ -4343,34 +4351,62 @@ nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
nsStyleUIReset::~nsStyleUIReset()
{
MOZ_COUNT_DTOR(nsStyleUIReset);
// See the nsStyleDisplay destructor for why we're doing this.
if (mSpecifiedWindowTransform && ServoStyleSet::IsInServoTraversal()) {
bool alwaysProxy =
#ifdef DEBUG
true;
#else
false;
#endif
NS_ReleaseOnMainThread(mSpecifiedWindowTransform.forget(), alwaysProxy);
}
}
nsChangeHint
nsStyleUIReset::CalcDifference(const nsStyleUIReset& aNewData) const
{
// ignore mIMEMode
nsChangeHint hint = nsChangeHint(0);
if (mForceBrokenImageIcon != aNewData.mForceBrokenImageIcon) {
return nsChangeHint_ReconstructFrame;
hint |= nsChangeHint_ReconstructFrame;
}
if (mWindowShadow != aNewData.mWindowShadow) {
// We really need just an nsChangeHint_SyncFrameView, except
// on an ancestor of the frame, so we get that by doing a
// reflow.
return NS_STYLE_HINT_REFLOW;
hint |= NS_STYLE_HINT_REFLOW;
}
if (mUserSelect != aNewData.mUserSelect) {
return NS_STYLE_HINT_VISUAL;
hint |= NS_STYLE_HINT_VISUAL;
}
if (mWindowDragging != aNewData.mWindowDragging) {
return nsChangeHint_SchedulePaint;
hint |= nsChangeHint_SchedulePaint;
}
if (mIMEMode != aNewData.mIMEMode) {
return nsChangeHint_NeutralChange;
if (mWindowOpacity != aNewData.mWindowOpacity ||
!mSpecifiedWindowTransform != !aNewData.mSpecifiedWindowTransform ||
(mSpecifiedWindowTransform &&
*mSpecifiedWindowTransform != *aNewData.mSpecifiedWindowTransform)) {
hint |= nsChangeHint_UpdateWidgetProperties;
} else {
for (uint8_t index = 0; index < 2; ++index) {
if (mWindowTransformOrigin[index] !=
aNewData.mWindowTransformOrigin[index]) {
hint |= nsChangeHint_UpdateWidgetProperties;
break;
}
}
}
return nsChangeHint(0);
if (!hint &&
mIMEMode != aNewData.mIMEMode) {
hint |= nsChangeHint_NeutralChange;
}
return hint;
}
//-----------------------

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

@ -3228,6 +3228,9 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset
uint8_t mIMEMode; // [reset]
mozilla::StyleWindowDragging mWindowDragging; // [reset]
uint8_t mWindowShadow; // [reset]
float mWindowOpacity; // [reset]
RefPtr<nsCSSValueSharedList> mSpecifiedWindowTransform; // [reset]
nsStyleCoord mWindowTransformOrigin[2]; // [reset] percent, coord, calc
};
struct nsCursorImage

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

@ -1007,6 +1007,39 @@ ReadTransforms(const nsCSSValueList* aList,
return result;
}
Point
Convert2DPosition(nsStyleCoord const (&aValue)[2],
TransformReferenceBox& aRefBox,
int32_t aAppUnitsPerDevPixel)
{
float position[2];
nsStyleTransformMatrix::TransformReferenceBox::DimensionGetter dimensionGetter[] =
{ &nsStyleTransformMatrix::TransformReferenceBox::Width,
&nsStyleTransformMatrix::TransformReferenceBox::Height };
for (uint8_t index = 0; index < 2; ++index) {
const nsStyleCoord& value = aValue[index];
if (value.GetUnit() == eStyleUnit_Calc) {
const nsStyleCoord::Calc *calc = value.GetCalcValue();
position[index] =
NSAppUnitsToFloatPixels((aRefBox.*dimensionGetter[index])(), aAppUnitsPerDevPixel) *
calc->mPercent +
NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerDevPixel);
} else if (value.GetUnit() == eStyleUnit_Percent) {
position[index] =
NSAppUnitsToFloatPixels((aRefBox.*dimensionGetter[index])(), aAppUnitsPerDevPixel) *
value.GetPercentValue();
} else {
MOZ_ASSERT(value.GetUnit() == eStyleUnit_Coord,
"unexpected unit");
position[index] =
NSAppUnitsToFloatPixels(value.GetCoordValue(),
aAppUnitsPerDevPixel);
}
}
return Point(position[0], position[1]);
}
/*
* The relevant section of the transitions specification:
* http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-

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

@ -205,6 +205,14 @@ namespace nsStyleTransformMatrix {
float aAppUnitsPerMatrixUnit,
bool* aContains3dTransform);
/**
* Given two nsStyleCoord values, compute the 2d position with respect to the
* given TransformReferenceBox that these values describe, in device pixels.
*/
mozilla::gfx::Point Convert2DPosition(nsStyleCoord const (&aValue)[2],
TransformReferenceBox& aRefBox,
int32_t aAppUnitsPerDevPixel);
// Shear type for decomposition.
enum class ShearType {
XYSHEAR,

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

@ -115,6 +115,9 @@ const char *gInaccessibleProperties[] = {
"-moz-math-display", // parsed by UA sheets only
"-moz-top-layer", // parsed by UA sheets only
"-moz-min-font-size-ratio", // parsed by UA sheets only
"-moz-window-opacity", // chrome-only internal properties
"-moz-window-transform", // chrome-only internal properties
"-moz-window-transform-origin", // chrome-only internal properties
"-moz-window-shadow" // chrome-only internal properties
};

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

@ -7905,6 +7905,111 @@ if (false) {
invalid_values: []
};
gCSSProperties["-moz-window-opacity"] = {
// domProp: "MozWindowOpacity",
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "1", "17", "397.376", "3e1", "3e+1", "3e0", "3e+0", "3e-0" ],
other_values: [ "0", "0.4", "0.0000", "-3", "3e-1" ],
invalid_values: [ "0px", "1px", "20%", "default", "auto" ]
};
gCSSProperties["-moz-window-transform"] = {
// domProp: "MozWindowTransform",
inherited: false,
type: CSS_TYPE_LONGHAND,
prerequisites: { "width": "300px", "height": "50px" },
initial_values: [ "none" ],
other_values: [ "translatex(1px)", "translatex(4em)",
"translatex(-4px)", "translatex(3px)",
"translatex(0px) translatex(1px) translatex(2px) translatex(3px) translatex(4px)",
"translatey(4em)", "translate(3px)", "translate(10px, -3px)",
"rotate(45deg)", "rotate(45grad)", "rotate(45rad)",
"rotate(0.25turn)", "rotate(0)", "scalex(10)", "scaley(10)",
"scale(10)", "scale(10, 20)", "skewx(30deg)", "skewx(0)",
"skewy(0)", "skewx(30grad)", "skewx(30rad)", "skewx(0.08turn)",
"skewy(30deg)", "skewy(30grad)", "skewy(30rad)", "skewy(0.08turn)",
"rotate(45deg) scale(2, 1)", "skewx(45deg) skewx(-50grad)",
"translate(0, 0) scale(1, 1) skewx(0) skewy(0) matrix(1, 0, 0, 1, 0, 0)",
"translatex(50%)", "translatey(50%)", "translate(50%)",
"translate(3%, 5px)", "translate(5px, 3%)",
"matrix(1, 2, 3, 4, 5, 6)",
/* valid calc() values */
"translatex(calc(5px + 10%))",
"translatey(calc(0.25 * 5px + 10% / 3))",
"translate(calc(5px - 10% * 3))",
"translate(calc(5px - 3 * 10%), 50px)",
"translate(-50px, calc(5px - 10% * 3))",
"translatez(1px)", "translatez(4em)", "translatez(-4px)",
"translatez(0px)", "translatez(2px) translatez(5px)",
"translate3d(3px, 4px, 5px)", "translate3d(2em, 3px, 1em)",
"translatex(2px) translate3d(4px, 5px, 6px) translatey(1px)",
"scale3d(4, 4, 4)", "scale3d(-2, 3, -7)", "scalez(4)",
"scalez(-6)", "rotate3d(2, 3, 4, 45deg)",
"rotate3d(-3, 7, 0, 12rad)", "rotatex(15deg)", "rotatey(-12grad)",
"rotatez(72rad)", "rotatex(0.125turn)",
"perspective(0px)", "perspective(1000px)",
"matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)",
],
invalid_values: ["1px", "#0000ff", "red", "auto",
"translatex(1)", "translatey(1)", "translate(2)",
"translate(-3, -4)",
"translatex(1px 1px)", "translatex(translatex(1px))",
"translatex(#0000ff)", "translatex(red)", "translatey()",
"matrix(1px, 2px, 3px, 4px, 5px, 6px)", "scale(150%)",
"skewx(red)", "matrix(1%, 0, 0, 0, 0px, 0px)",
"matrix(0, 1%, 2, 3, 4px,5px)", "matrix(0, 1, 2%, 3, 4px, 5px)",
"matrix(0, 1, 2, 3%, 4%, 5%)", "matrix(1, 2, 3, 4, 5px, 6%)",
"matrix(1, 2, 3, 4, 5%, 6px)", "matrix(1, 2, 3, 4, 5%, 6%)",
"matrix(1, 2, 3, 4, 5px, 6em)",
/* invalid calc() values */
"translatey(-moz-min(5px,10%))",
"translatex(-moz-max(5px,10%))",
"translate(10px, calc(min(5px,10%)))",
"translate(calc(max(5px,10%)), 10%)",
"matrix(1, 0, 0, 1, max(5px * 3), calc(10% - 3px))",
"perspective(-10px)", "matrix3d(dinosaur)",
"matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)",
"matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)",
"matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15%, 16)",
"matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16px)",
"rotatey(words)", "rotatex(7)", "translate3d(3px, 4px, 1px, 7px)",
"matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13px, 14em, 15px, 16)",
"matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 20%, 10%, 15, 16)"
],
};
gCSSProperties["-moz-window-transform-origin"] = {
// domProp: "MozWindowTransformOrigin",
inherited: false,
type: CSS_TYPE_LONGHAND,
/* no subproperties */
prerequisites: { "width": "10px", "height": "10px", "display": "block"},
initial_values: [ "50% 50%", "center", "center center" ],
other_values: [ "25% 25%", "6px 5px", "20% 3em", "0 0", "0in 1in",
"top", "bottom","top left", "top right",
"top center", "center left", "center right",
"bottom left", "bottom right", "bottom center",
"20% center", "6px center", "13in bottom",
"left 50px", "right 13%", "center 40px",
"calc(20px)",
"calc(20px) 10px",
"10px calc(20px)",
"calc(20px) 25%",
"25% calc(20px)",
"calc(20px) calc(20px)",
"calc(20px + 1em) calc(20px / 2)",
"calc(20px + 50%) calc(50% - 10px)",
"calc(-20px) calc(-50%)",
"calc(-20%) calc(-50%)"
],
invalid_values: ["red", "auto", "none", "0.5 0.5", "40px #0000ff",
"border", "center red", "right diagonal",
"#00ffff bottom", "0px calc(0px + rubbish)",
"0px 0px calc(0px + rubbish)", "6px 5px 5px",
"top center 10px"]
};
gCSSProperties["-moz-context-properties"] = {
//domProp: "MozContextProperties",
inherited: true,

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

@ -456,6 +456,15 @@ nsMenuPopupFrame::IsLeafDynamic() const
!parentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::sizetopopup));
}
void
nsMenuPopupFrame::UpdateWidgetProperties()
{
if (nsIWidget* widget = GetWidget()) {
widget->SetWindowOpacity(StyleUIReset()->mWindowOpacity);
widget->SetWindowTransform(ComputeWidgetTransform());
}
}
void
nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
nsIFrame* aAnchor, bool aSizedToPopup)

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

@ -248,6 +248,8 @@ public:
virtual bool IsLeafDynamic() const override;
virtual void UpdateWidgetProperties() override;
// layout, position and display the popup as needed
void LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
nsIFrame* aAnchor, bool aSizedToPopup);

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

@ -4779,6 +4779,8 @@ pref("widget.content.allow-gtk-dark-theme", false);
#endif
#endif
pref("widget.window-transforms.disabled", false);
#ifdef XP_WIN
// Whether to disable the automatic detection and use of direct2d.
pref("gfx.direct2d.disabled", false);

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

@ -63,6 +63,10 @@ using namespace mozilla::ipc;
namespace mozilla {
namespace net {
#if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION) || defined(DEBUG)
static bool gIPCSecurityDisabled = false;
#endif
NS_IMPL_ISUPPORTS(InterceptStreamListener,
nsIStreamListener,
nsIRequestObserver,
@ -181,6 +185,15 @@ HttpChannelChild::HttpChannelChild()
mChannelCreationTimestamp = TimeStamp::Now();
mAsyncOpenTime = TimeStamp::Now();
mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this));
#if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION) || defined(DEBUG)
static bool sSecurityPrefChecked = false;
if (!sSecurityPrefChecked) {
Preferences::AddBoolVarCache(&gIPCSecurityDisabled,
"network.disable.ipc.security");
sSecurityPrefChecked = true;
}
#endif
}
HttpChannelChild::~HttpChannelChild()
@ -1825,9 +1838,12 @@ HttpChannelChild::ConnectParent(uint32_t registrarId)
HttpChannelConnectArgs connectArgs(registrarId, mShouldParentIntercept);
PBrowserOrId browser = static_cast<ContentChild*>(gNeckoChild->Manager())
->GetBrowserOrId(tabChild);
IPC::SerializedLoadContext slc(this);
MOZ_DIAGNOSTIC_ASSERT(gIPCSecurityDisabled || slc.IsNotNull(),
"SerializedLoadContext should not be null");
if (!gNeckoChild->
SendPHttpChannelConstructor(this, browser,
IPC::SerializedLoadContext(this),
slc,
connectArgs)) {
return NS_ERROR_FAILURE;
}
@ -2468,8 +2484,11 @@ HttpChannelChild::ContinueAsyncOpen()
AddIPDLReference();
PBrowserOrId browser = cc->GetBrowserOrId(tabChild);
IPC::SerializedLoadContext slc(this);
MOZ_DIAGNOSTIC_ASSERT(gIPCSecurityDisabled || slc.IsNotNull(),
"SerializedLoadContext should not be null");
if (!gNeckoChild->SendPHttpChannelConstructor(this, browser,
IPC::SerializedLoadContext(this),
slc,
openArgs)) {
return NS_ERROR_FAILURE;
}

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

@ -8,9 +8,6 @@ module.exports = {
// Braces only needed for multi-line arrow function blocks
"arrow-body-style": ["error", "as-needed"],
// Commas at the end of the line not the start
"comma-style": "error",
// Verify calls of super() in constructors.
"constructor-super": "error",

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

@ -1561,41 +1561,35 @@ FxAccountsInternal.prototype = {
return Promise.resolve();
},
handleDeviceDisconnection(deviceId) {
return this.currentAccountState.getUserAccountData()
.then(data => data ? data.deviceId : null)
.then(localDeviceId => {
if (!localDeviceId) {
// We've already been logged out (and that logout is probably what
// caused us to get here via push!), so don't make noise here.
log.info(`Push request to disconnect, but we've already disconnected`);
return null;
}
if (deviceId == localDeviceId) {
this.notifyObservers(ON_DEVICE_DISCONNECTED_NOTIFICATION);
return this.signOut(true);
}
return null;
});
async handleDeviceDisconnection(deviceId) {
const accountData = await this.currentAccountState.getUserAccountData();
const localDeviceId = accountData ? accountData.deviceId : null;
const isLocalDevice = (deviceId == localDeviceId);
if (isLocalDevice) {
this.signOut(true);
}
const data = JSON.stringify({ isLocalDevice });
Services.obs.notifyObservers(null, ON_DEVICE_DISCONNECTED_NOTIFICATION, data);
return null;
},
handleAccountDestroyed(uid) {
return this.currentAccountState.getUserAccountData()
.then(data => data ? data.uid : null)
.then(localUid => {
if (!localUid) {
log.info(`Account destroyed push notification received, but we're already logged-out`);
return null;
}
if (uid == localUid) {
this.notifyObservers(ON_DEVICE_DISCONNECTED_NOTIFICATION);
return this.signOut(true);
}
log.info(
`The destroyed account uid doesn't match with the local uid. ` +
`Local: ${localUid}, account uid destroyed: ${uid}`);
return null;
});
async handleAccountDestroyed(uid) {
const accountData = await this.currentAccountState.getUserAccountData();
const localUid = accountData ? accountData.uid : null;
if (!localUid) {
log.info(`Account destroyed push notification received, but we're already logged-out`);
return null;
}
if (uid == localUid) {
const data = JSON.stringify({ isLocalDevice: true });
Services.obs.notifyObservers(null, ON_DEVICE_DISCONNECTED_NOTIFICATION, data);
this.notifyObservers(ON_DEVICE_DISCONNECTED_NOTIFICATION, data);
return this.signOut(true);
}
log.info(
`The destroyed account uid doesn't match with the local uid. ` +
`Local: ${localUid}, account uid destroyed: ${uid}`);
return null;
},
/**

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

@ -171,7 +171,7 @@ add_test(function observePushTopicDeviceConnected() {
pushService.observe(msg, mockPushService.pushTopic, FXA_PUSH_SCOPE_ACCOUNT_UPDATE);
});
add_test(function observePushTopicDeviceDisconnected() {
add_task(async function observePushTopicDeviceDisconnected_current_device() {
const deviceId = "bogusid";
let msg = {
data: {
@ -186,19 +186,69 @@ add_test(function observePushTopicDeviceDisconnected() {
return this;
}
};
let customAccounts = Object.assign(mockFxAccounts, {
handleDeviceDisconnection() {
// checking verification status on push messages without data
run_next_test();
}
let { FxAccounts } = Cu.import("resource://gre/modules/FxAccounts.jsm", {});
const fxAccountsMock = new FxAccounts({});
fxAccountsMock.internal.currentAccountState.getUserAccountData = async () => {
return { deviceId };
};
const deviceDisconnectedNotificationObserved = new Promise(resolve => {
Services.obs.addObserver(function obs(subject, topic, data) {
Services.obs.removeObserver(obs, topic);
equal(data, JSON.stringify({ isLocalDevice: true }));
resolve();
}, ON_DEVICE_DISCONNECTED_NOTIFICATION);
});
let pushService = new FxAccountsPushService({
pushService: mockPushService,
fxAccounts: customAccounts,
fxAccounts: fxAccountsMock,
});
pushService.observe(msg, mockPushService.pushTopic, FXA_PUSH_SCOPE_ACCOUNT_UPDATE);
await deviceDisconnectedNotificationObserved;
});
add_task(async function observePushTopicDeviceDisconnected_another_device() {
const deviceId = "bogusid";
let msg = {
data: {
json: () => ({
command: ON_DEVICE_DISCONNECTED_NOTIFICATION,
data: {
id: deviceId
}
})
},
QueryInterface() {
return this;
}
};
let { FxAccounts } = Cu.import("resource://gre/modules/FxAccounts.jsm", {});
const fxAccountsMock = new FxAccounts({});
fxAccountsMock.internal.currentAccountState.getUserAccountData = async () => {
return { deviceId: "thelocaldevice" };
};
const deviceDisconnectedNotificationObserved = new Promise(resolve => {
Services.obs.addObserver(function obs(subject, topic, data) {
Services.obs.removeObserver(obs, topic);
equal(data, JSON.stringify({ isLocalDevice: false }));
resolve();
}, ON_DEVICE_DISCONNECTED_NOTIFICATION);
});
let pushService = new FxAccountsPushService({
pushService: mockPushService,
fxAccounts: fxAccountsMock,
});
pushService.observe(msg, mockPushService.pushTopic, FXA_PUSH_SCOPE_ACCOUNT_UPDATE);
await deviceDisconnectedNotificationObserved;
});
add_test(function observePushTopicAccountDestroyed() {

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

@ -104,9 +104,10 @@ const UIStateInternal = {
// Builds a new state from scratch.
async refreshState() {
this._state = {};
await this._refreshFxAState();
this._setLastSyncTime(this._state); // We want this in case we change accounts.
const newState = {};
await this._refreshFxAState(newState);
this._setLastSyncTime(newState); // We want this in case we change accounts.
this._state = newState;
this.notifyStateUpdated();
return this.state;
@ -124,17 +125,17 @@ const UIStateInternal = {
Services.obs.notifyObservers(null, ON_UPDATE);
},
async _refreshFxAState() {
async _refreshFxAState(newState) {
let userData = await this._getUserData();
this._populateWithUserData(this._state, userData);
if (this.state.status != STATUS_SIGNED_IN) {
this._populateWithUserData(newState, userData);
if (newState.status != STATUS_SIGNED_IN) {
return;
}
let profile = await this._getProfile();
if (!profile) {
return;
}
this._populateWithProfile(this._state, profile);
this._populateWithProfile(newState, profile);
},
_populateWithUserData(state, userData) {

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

@ -294,6 +294,17 @@ ClientEngine.prototype = {
this._saveCommands(allCommands);
},
updateKnownStaleClients() {
this._log.debug("Updating the known stale clients");
this._refreshKnownStaleClients();
for (let client of Object.values(this._store._remoteClients)) {
if (client.fxaDeviceId && this._knownStaleFxADeviceIds.includes(client.fxaDeviceId)) {
this._log.info(`Hiding stale client ${client.id} - in known stale clients list`);
client.stale = true;
}
}
},
// We assume that clients not present in the FxA Device Manager list have been
// disconnected and so are stale
_refreshKnownStaleClients() {
@ -328,7 +339,8 @@ ClientEngine.prototype = {
this._incomingClients = {};
try {
SyncEngine.prototype._processIncoming.call(this);
// Refresh the known stale clients list once per browser restart
// Refresh the known stale clients list at startup and when we receive
// "device connected/disconnected" push notifications.
if (!this._knownStaleFxADeviceIds) {
this._refreshKnownStaleClients();
}

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

@ -309,6 +309,7 @@ Sync11Service.prototype = {
Svc.Obs.add("weave:service:setup-complete", this);
Svc.Obs.add("sync:collection_changed", this); // Pulled from FxAccountsCommon
Svc.Obs.add("fxaccounts:device_disconnected", this);
Services.prefs.addObserver(PREFS_BRANCH + "engine.", this);
this.scheduler = new SyncScheduler(this);
@ -412,6 +413,12 @@ Sync11Service.prototype = {
this.sync([]); // [] = clients collection only
}
break;
case "fxaccounts:device_disconnected":
data = JSON.parse(data);
if (!data.isLocalDevice) {
this.clientsEngine.updateKnownStaleClients();
}
break;
case "weave:service:setup-complete":
let status = this._checkSetup();
if (status != STATUS_DISABLED && status != CLIENT_NOT_CONFIGURED)

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

@ -1576,6 +1576,59 @@ add_task(async function test_other_clients_notified_on_first_sync() {
}
});
add_task(async function device_disconnected_notification_updates_known_stale_clients() {
const spyUpdate = sinon.spy(engine, "updateKnownStaleClients");
const makeFakeClient = (id) => ({ id, fxaDeviceId: `fxa-${id}` });
const clients = [makeFakeClient("one"), makeFakeClient("two"), makeFakeClient("three")];
const stubRemoteClients = sinon.stub(engine._store, "_remoteClients").get(() => {
return clients;
});
const stubRefresh = sinon.stub(engine, "_refreshKnownStaleClients", () => {
engine._knownStaleFxADeviceIds = ["fxa-one", "fxa-two"];
});
engine._knownStaleFxADeviceIds = null;
Services.obs.notifyObservers(null, "fxaccounts:device_disconnected",
JSON.stringify({ isLocalDevice: false }));
ok(spyUpdate.calledOnce, "updateKnownStaleClients should be called");
ok(clients[0].stale);
ok(clients[1].stale);
ok(!clients[2].stale);
spyUpdate.reset();
ok(engine._knownStaleFxADeviceIds)
Services.obs.notifyObservers(null, "fxaccounts:device_disconnected",
JSON.stringify({ isLocalDevice: false }));
ok(spyUpdate.calledOnce, "updateKnownStaleClients should be called");
spyUpdate.reset();
Services.obs.notifyObservers(null, "fxaccounts:device_disconnected",
JSON.stringify({ isLocalDevice: true }));
ok(spyUpdate.notCalled, "updateKnownStaleClients should not be called");
stubRemoteClients.restore();
spyUpdate.restore();
stubRefresh.restore();
});
add_task(async function process_incoming_refreshes_known_stale_clients() {
const stubProcessIncoming = sinon.stub(SyncEngine.prototype, "_processIncoming");
const stubRefresh = sinon.stub(engine, "_refreshKnownStaleClients", () => {
engine._knownStaleFxADeviceIds = ["one", "two"];
});
engine._knownStaleFxADeviceIds = null;
engine._processIncoming();
ok(stubRefresh.calledOnce, "Should refresh the known stale clients");
stubRefresh.reset();
engine._processIncoming();
ok(stubRefresh.notCalled, "Should not refresh the known stale clients since it's already populated");
stubProcessIncoming.restore();
stubRefresh.restore();
});
function run_test() {
initTestLogging("Trace");
Log.repository.getLogger("Sync.Engine.Clients").level = Log.Level.Trace;

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше