зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to central, a=merge
MozReview-Commit-ID: 4NzLU3zKJOz
This commit is contained in:
Коммит
1e80580419
|
@ -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;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче