Merge mozilla-central to mozilla-inbound

This commit is contained in:
Dorel Luca 2018-03-29 12:57:29 +03:00
Родитель 94b06cd525 57bbc1ac58
Коммит eb1ea97541
272 изменённых файлов: 13585 добавлений и 3721 удалений

57
Cargo.lock сгенерированный
Просмотреть файл

@ -552,6 +552,17 @@ dependencies = [
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "env_logger"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "error-chain"
version = "0.11.0"
@ -689,9 +700,9 @@ dependencies = [
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"malloc_size_of 0.0.1",
"nsstring 0.1.0",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -782,6 +793,14 @@ name = "httparse"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "humantime"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hyper"
version = "0.10.13"
@ -1374,6 +1393,11 @@ dependencies = [
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quick-error"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.3.15"
@ -1520,7 +1544,7 @@ dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1638,7 +1662,7 @@ dependencies = [
"itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"malloc_size_of 0.0.1",
"malloc_size_of_derive 0.0.1",
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1698,11 +1722,11 @@ dependencies = [
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
"geckoservo 0.0.1",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"malloc_size_of 0.0.1",
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.19.0",
@ -1769,6 +1793,14 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termcolor"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.9.0"
@ -2110,6 +2142,14 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wincolor"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winreg"
version = "0.5.0"
@ -2233,6 +2273,7 @@ dependencies = [
"checksum encoding_c 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "93ec52324ca72f423237a413ca0e1c60654c8b3d0934fcd5fd888508dfcc4ba7"
"checksum encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98fd0f24d1fb71a4a6b9330c8ca04cbd4e7cc5d846b54ca74ff376bc7c9f798d"
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
"checksum env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0561146661ae44c579e993456bc76d11ce1e0c7d745e57b2fa7146b6e49fa2ad"
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
"checksum euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)" = "adfe67a9343519c1449d208da5998c6de582de698f7a39c4ac82ffba23d131a5"
"checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909"
@ -2251,6 +2292,7 @@ dependencies = [
"checksum gleam 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "959c818d9bbe9f7b7db55dce0bc44673c4da4f4ee122536c40550f984c3b8017"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
"checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2"
"checksum ident_case 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9826188e666f2ed92071d2dadef6edc430b11b158b5b2b3f4babbcc891eaaa"
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
@ -2306,6 +2348,7 @@ dependencies = [
"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
"checksum proc-macro2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d1cb7aaaa4bf022ec2b14ff2f2ba1643a22f3cee88df014a85e14b392282c61d"
"checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
"checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408"
"checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd"
@ -2340,6 +2383,7 @@ dependencies = [
"checksum synstructure 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "010366096045d8250555904c58da03377289e7f4b2ce7a5b1027e2b532f41000"
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
"checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83"
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf947d192a9be60ef5131cc7a4648886ba89d712f16700ebbf80c8a69d05d48f"
@ -2374,6 +2418,7 @@ dependencies = [
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767"
"checksum winreg 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9338067aba07889a38beaad4dbb77fa2e62e87c423b770824b3bdf412874bd2c"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2"

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

@ -48,29 +48,43 @@ var BrowserPageActions = {
*/
init() {
this.placeAllActions();
this._onPanelShowing = this._onPanelShowing.bind(this);
this.panelNode.addEventListener("popupshowing", this._onPanelShowing);
this.panelNode.addEventListener("popuphiding", () => {
this.mainButtonNode.removeAttribute("open");
});
},
_onPanelShowing() {
this.placeLazyActionsInPanel();
for (let action of PageActions.actionsInPanel(window)) {
let buttonNode = this.panelButtonNodeForActionID(action.id);
action.onShowingInPanel(buttonNode);
}
},
placeLazyActionsInPanel() {
let actions = this._actionsToLazilyPlaceInPanel;
this._actionsToLazilyPlaceInPanel = [];
for (let action of actions) {
this._placeActionInPanelNow(action);
}
},
// Actions placed in the panel aren't actually placed until the panel is
// subsequently opened.
_actionsToLazilyPlaceInPanel: [],
/**
* Places all registered actions.
*/
placeAllActions() {
// Place actions in the panel. Notify of onBeforePlacedInWindow too.
for (let action of PageActions.actions) {
action.onBeforePlacedInWindow(window);
let panelActions = PageActions.actionsInPanel(window);
for (let action of panelActions) {
this.placeActionInPanel(action);
}
// Place actions in the urlbar. Do this in reverse order. The reason is
// subtle. If there were no urlbar nodes already in markup (like the
// bookmark star button), then doing this in forward order would be fine.
// Forward order means that the insert-before relationship is always broken:
// there's never a next-sibling node before which to insert a new node, so
// node.insertBefore() is always passed null, and nodes are always appended.
// That will break the position of nodes that should be inserted before
// nodes that are in markup, which in turn can break other nodes.
let actionsInUrlbar = PageActions.actionsInUrlbar(window);
for (let i = actionsInUrlbar.length - 1; i >= 0; i--) {
let action = actionsInUrlbar[i];
let urlbarActions = PageActions.actionsInUrlbar(window);
for (let action of urlbarActions) {
this.placeActionInUrlbar(action);
}
},
@ -82,7 +96,6 @@ var BrowserPageActions = {
* The action to place.
*/
placeAction(action) {
action.onBeforePlacedInWindow(window);
this.placeActionInPanel(action);
this.placeActionInUrlbar(action);
},
@ -94,32 +107,132 @@ var BrowserPageActions = {
* The action to place.
*/
placeActionInPanel(action) {
if (this.panelNode.state != "closed") {
this._placeActionInPanelNow(action);
} else {
// Lazily place the action in the panel the next time it opens.
this._actionsToLazilyPlaceInPanel.push(action);
}
},
_placeActionInPanelNow(action) {
if (action.shouldShowInPanel(window)) {
this._addActionToPanel(action);
} else {
this._removeActionFromPanel(action);
}
},
_addActionToPanel(action) {
let id = this.panelButtonNodeIDForActionID(action.id);
let node = document.getElementById(id);
if (node) {
return;
}
this._maybeNotifyBeforePlacedInWindow(action);
node = this._makePanelButtonNodeForAction(action);
node.id = id;
let insertBeforeNode = this._getNextNode(action, false);
this.mainViewBodyNode.insertBefore(node, insertBeforeNode);
this.updateAction(action, null, {
panelNode: node,
});
this._updateActionDisabledInPanel(action, node);
action.onPlacedInPanel(node);
this._addOrRemoveSeparatorsInPanel();
},
_removeActionFromPanel(action) {
let lazyIndex =
this._actionsToLazilyPlaceInPanel.findIndex(a => a.id == action.id);
if (lazyIndex >= 0) {
this._actionsToLazilyPlaceInPanel.splice(lazyIndex, 1);
}
let node = this.panelButtonNodeForActionID(action.id);
if (!node) {
let panelViewNode;
[node, panelViewNode] = this._makePanelButtonNodeForAction(action);
node.id = id;
let insertBeforeID = PageActions.nextActionIDInPanel(action);
let insertBeforeNode =
insertBeforeID ? this.panelButtonNodeForActionID(insertBeforeID) :
null;
this.mainViewBodyNode.insertBefore(node, insertBeforeNode);
this.updateAction(action);
this._updateActionDisabledInPanel(action);
action.onPlacedInPanel(node);
return;
}
node.remove();
if (action.getWantsSubview(window)) {
let panelViewNodeID = this._panelViewNodeIDForActionID(action.id, false);
let panelViewNode = document.getElementById(panelViewNodeID);
if (panelViewNode) {
action.subview.onPlaced(panelViewNode);
panelViewNode.remove();
}
}
this._addOrRemoveSeparatorsInPanel();
},
_addOrRemoveSeparatorsInPanel() {
let actions = PageActions.actionsInPanel(window);
let ids = [
PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
];
for (let id of ids) {
let sep = actions.find(a => a.id == id);
if (sep) {
this._addActionToPanel(sep);
} else {
let node = this.panelButtonNodeForActionID(id);
if (node) {
node.remove();
}
}
}
},
/**
* Returns the node before which an action's node should be inserted.
*
* @param action (PageActions.Action, required)
* The action that will be inserted.
* @param forUrlbar (bool, required)
* True if you're inserting into the urlbar, false if you're inserting
* into the panel.
* @return (DOM node, maybe null) The DOM node before which to insert the
* given action. Null if the action should be inserted at the end.
*/
_getNextNode(action, forUrlbar) {
let actions =
forUrlbar ?
PageActions.actionsInUrlbar(window) :
PageActions.actionsInPanel(window);
let index = actions.findIndex(a => a.id == action.id);
if (index < 0) {
return null;
}
for (let i = index + 1; i < actions.length; i++) {
let node =
forUrlbar ?
this.urlbarButtonNodeForActionID(actions[i].id) :
this.panelButtonNodeForActionID(actions[i].id);
if (node) {
return node;
}
}
return null;
},
_maybeNotifyBeforePlacedInWindow(action) {
if (!this._isActionPlacedInWindow(action)) {
action.onBeforePlacedInWindow(window);
}
},
_isActionPlacedInWindow(action) {
if (this.panelButtonNodeForActionID(action.id)) {
return true;
}
let urlbarNode = this.urlbarButtonNodeForActionID(action.id);
return urlbarNode && !urlbarNode.hidden;
},
_makePanelButtonNodeForAction(action) {
if (action.__isSeparator) {
let node = document.createElement("toolbarseparator");
return [node, null];
return node;
}
let buttonNode = document.createElement("toolbarbutton");
buttonNode.classList.add(
"subviewbutton",
@ -132,16 +245,10 @@ var BrowserPageActions = {
buttonNode.setAttribute(name, action.nodeAttributes[name]);
}
}
let panelViewNode = null;
if (action.subview) {
buttonNode.classList.add("subviewbutton-nav");
panelViewNode = this._makePanelViewNodeForAction(action, false);
this.multiViewNode.appendChild(panelViewNode);
}
buttonNode.addEventListener("command", event => {
this.doCommandForAction(action, event, buttonNode);
});
return [buttonNode, panelViewNode];
return buttonNode;
},
_makePanelViewNodeForAction(action, forUrlbar) {
@ -152,23 +259,6 @@ var BrowserPageActions = {
bodyNode.id = panelViewNode.id + "-body";
bodyNode.classList.add("panel-subview-body");
panelViewNode.appendChild(bodyNode);
for (let button of action.subview.buttons) {
let buttonNode = document.createElement("toolbarbutton");
buttonNode.id =
this._panelViewButtonNodeIDForActionID(action.id, button.id, forUrlbar);
buttonNode.classList.add("subviewbutton", "subviewbutton-iconic");
buttonNode.setAttribute("label", button.title);
if (button.shortcut) {
buttonNode.setAttribute("shortcut", button.shortcut);
}
if (button.disabled) {
buttonNode.setAttribute("disabled", "true");
}
buttonNode.addEventListener("command", event => {
button.onCommand(event, buttonNode);
});
bodyNode.appendChild(buttonNode);
}
return panelViewNode;
},
@ -229,14 +319,10 @@ var BrowserPageActions = {
panelNode.setAttribute("tabspecific", "true");
panelNode.setAttribute("photon", "true");
if (this._disablePanelAnimations) {
panelNode.setAttribute("animate", "false");
}
let panelViewNode = null;
let iframeNode = null;
if (action.subview) {
if (action.getWantsSubview(window)) {
let multiViewNode = document.createElement("panelmultiview");
panelViewNode = this._makePanelViewNodeForAction(action, true);
multiViewNode.setAttribute("mainViewId", panelViewNode.id);
@ -267,28 +353,15 @@ var BrowserPageActions = {
}
if (panelViewNode) {
action.subview.onPlaced(panelViewNode);
action.onSubviewPlaced(panelViewNode);
panelNode.addEventListener("popupshowing", () => {
action.subview.onShowing(panelViewNode);
action.onSubviewShowing(panelViewNode);
}, { once: true });
}
return panelNode;
},
// For tests.
get _disablePanelAnimations() {
return this.__disablePanelAnimations || false;
},
set _disablePanelAnimations(val) {
this.__disablePanelAnimations = val;
if (val) {
this.panelNode.setAttribute("animate", "false");
} else {
this.panelNode.removeAttribute("animate");
}
},
/**
* Returns the node in the urlbar to which popups for the given action should
* be anchored. If the action is null, a sensible anchor is returned.
@ -299,8 +372,7 @@ var BrowserPageActions = {
* This is used to display the feedback panel on the right node when
* the command can be invoked from both the main panel and another
* location, such as an activated action panel or a button.
* @return (DOM node, nonnull) The node to which the action should be
* anchored.
* @return (DOM node) The node to which the action should be anchored.
*/
panelAnchorNodeForAction(action, event) {
if (event && event.target.closest("panel") == this.panelNode) {
@ -362,32 +434,41 @@ var BrowserPageActions = {
let newlyPlaced = false;
if (action.__urlbarNodeInMarkup) {
newlyPlaced = node && node.hidden;
this._maybeNotifyBeforePlacedInWindow(action);
// Allow the consumer to add the node in response to the
// onBeforePlacedInWindow notification.
node = document.getElementById(id);
if (!node) {
return;
}
newlyPlaced = node.hidden;
node.hidden = false;
} else if (!node) {
newlyPlaced = true;
this._maybeNotifyBeforePlacedInWindow(action);
node = this._makeUrlbarButtonNode(action);
node.id = id;
}
if (newlyPlaced) {
let insertBeforeID = PageActions.nextActionIDInUrlbar(window, action);
let insertBeforeNode =
insertBeforeID ? this.urlbarButtonNodeForActionID(insertBeforeID) :
null;
this.mainButtonNode.parentNode.insertBefore(node, insertBeforeNode);
this.updateAction(action);
action.onPlacedInUrlbar(node);
if (!newlyPlaced) {
return;
}
// urlbar buttons should always have tooltips, so if the node doesn't have
// one, then as a last resort use the label of the corresponding panel
// button. Why not set tooltiptext to action.title when the node is
// created? Because the consumer may set a title dynamically.
if (!node.hasAttribute("tooltiptext")) {
let panelNode = this.panelButtonNodeForActionID(action.id);
if (panelNode) {
node.setAttribute("tooltiptext", panelNode.getAttribute("label"));
}
let insertBeforeNode = this._getNextNode(action, true);
this.mainButtonNode.parentNode.insertBefore(node, insertBeforeNode);
this.updateAction(action, null, {
urlbarNode: node,
});
action.onPlacedInUrlbar(node);
// urlbar buttons should always have tooltips, so if the node doesn't have
// one, then as a last resort use the label of the corresponding panel
// button. Why not set tooltiptext to action.title when the node is
// created? Because the consumer may set a title dynamically.
if (!node.hasAttribute("tooltiptext")) {
let panelNode = this.panelButtonNodeForActionID(action.id);
if (panelNode) {
node.setAttribute("tooltiptext", panelNode.getAttribute("label"));
}
}
},
@ -420,32 +501,6 @@ var BrowserPageActions = {
action.onRemovedFromWindow(window);
},
_removeActionFromPanel(action) {
let node = this.panelButtonNodeForActionID(action.id);
if (node) {
node.remove();
}
if (action.subview) {
let panelViewNodeID = this._panelViewNodeIDForActionID(action.id, false);
let panelViewNode = document.getElementById(panelViewNodeID);
if (panelViewNode) {
panelViewNode.remove();
}
}
// If there are now no more non-built-in actions, remove the separator
// between the built-ins and non-built-ins.
if (!PageActions.nonBuiltInActions.length) {
let separator = document.getElementById(
this.panelButtonNodeIDForActionID(
PageActions.ACTION_ID_BUILT_IN_SEPARATOR
)
);
if (separator) {
separator.remove();
}
}
},
_removeActionFromUrlbar(action) {
let node = this.urlbarButtonNodeForActionID(action.id);
if (node) {
@ -462,16 +517,29 @@ var BrowserPageActions = {
* @param propertyName (string, optional)
* The name of the property to update. If not given, then DOM nodes
* will be updated to reflect the current values of all properties.
* @param value (optional)
* If a property name is passed, this argument may contain its
* current value, in order to prevent a further look-up.
* @param opts (object, optional)
* - panelNode: The action's node in the panel to update.
* - urlbarNode: The action's node in the urlbar to update.
* - value: If a property name is passed, this argument may contain
* its current value, in order to prevent a further look-up.
*/
updateAction(action, propertyName = null, value) {
updateAction(action, propertyName = null, opts = {}) {
let anyNodeGiven = "panelNode" in opts || "urlbarNode" in opts;
let panelNode =
anyNodeGiven ?
opts.panelNode || null :
this.panelButtonNodeForActionID(action.id);
let urlbarNode =
anyNodeGiven ?
opts.urlbarNode || null :
this.urlbarButtonNodeForActionID(action.id);
let value = opts.value || undefined;
if (propertyName) {
this[this._updateMethods[propertyName]](action, value);
this[this._updateMethods[propertyName]](action, panelNode, urlbarNode,
value);
} else {
for (let name of ["iconURL", "title", "tooltip"]) {
this[this._updateMethods[name]](action, value);
for (let name of ["iconURL", "title", "tooltip", "wantsSubview"]) {
this[this._updateMethods[name]](action, panelNode, urlbarNode, value);
}
}
},
@ -481,39 +549,44 @@ var BrowserPageActions = {
iconURL: "_updateActionIconURL",
title: "_updateActionTitle",
tooltip: "_updateActionTooltip",
wantsSubview: "_updateActionWantsSubview",
},
_updateActionDisabled(action, disabled) {
this._updateActionDisabledInPanel(action, disabled);
_updateActionDisabled(action, panelNode, urlbarNode,
disabled = action.getDisabled(window)) {
if (action.__transient) {
this.placeActionInPanel(action);
} else {
this._updateActionDisabledInPanel(action, panelNode, disabled);
}
this.placeActionInUrlbar(action);
},
_updateActionDisabledInPanel(action, disabled = action.getDisabled(window)) {
let panelButton = this.panelButtonNodeForActionID(action.id);
if (panelButton) {
_updateActionDisabledInPanel(action, panelNode,
disabled = action.getDisabled(window)) {
if (panelNode) {
if (disabled) {
panelButton.setAttribute("disabled", "true");
panelNode.setAttribute("disabled", "true");
} else {
panelButton.removeAttribute("disabled");
panelNode.removeAttribute("disabled");
}
}
},
_updateActionIconURL(action, properties = action.getIconProperties(window)) {
let panelButton = this.panelButtonNodeForActionID(action.id);
let urlbarButton = this.urlbarButtonNodeForActionID(action.id);
_updateActionIconURL(action, panelNode, urlbarNode,
properties = action.getIconProperties(window)) {
for (let [prop, value] of Object.entries(properties)) {
if (panelButton) {
panelButton.style.setProperty(prop, value);
if (panelNode) {
panelNode.style.setProperty(prop, value);
}
if (urlbarButton) {
urlbarButton.style.setProperty(prop, value);
if (urlbarNode) {
urlbarNode.style.setProperty(prop, value);
}
}
},
_updateActionTitle(action, title = action.getTitle(window)) {
_updateActionTitle(action, panelNode, urlbarNode,
title = action.getTitle(window)) {
if (!title) {
// `title` is a required action property, but the bookmark action's is an
// empty string since its actual title is set via
@ -521,28 +594,49 @@ var BrowserPageActions = {
// return is to ignore that empty title.
return;
}
let panelButton = this.panelButtonNodeForActionID(action.id);
if (panelButton) {
panelButton.setAttribute("label", title);
if (panelNode) {
panelNode.setAttribute("label", title);
}
let urlbarButton = this.urlbarButtonNodeForActionID(action.id);
if (urlbarButton) {
urlbarButton.setAttribute("aria-label", title);
// tooltiptext falls back to the title, so update it, too.
this._updateActionTooltip(action, undefined, title, urlbarButton);
if (urlbarNode) {
urlbarNode.setAttribute("aria-label", title);
// tooltiptext falls back to the title, so update it too if necessary.
let tooltip = action.getTooltip(window);
if (!tooltip && title) {
urlbarNode.setAttribute("tooltiptext", title);
}
}
},
_updateActionTooltip(action, tooltip = action.getTooltip(window),
title,
node = this.urlbarButtonNodeForActionID(action.id)) {
if (node) {
if (!tooltip && title === undefined) {
title = action.getTitle(window);
_updateActionTooltip(action, panelNode, urlbarNode,
tooltip = action.getTooltip(window)) {
if (urlbarNode) {
if (!tooltip) {
tooltip = action.getTitle(window);
}
node.setAttribute("tooltiptext", tooltip || title);
if (tooltip) {
urlbarNode.setAttribute("tooltiptext", tooltip);
}
}
},
_updateActionWantsSubview(action, panelNode, urlbarNode,
wantsSubview = action.getWantsSubview(window)) {
if (!panelNode) {
return;
}
let panelViewID = this._panelViewNodeIDForActionID(action.id, false);
let panelViewNode = document.getElementById(panelViewID);
panelNode.classList.toggle("subviewbutton-nav", wantsSubview);
if (!wantsSubview) {
if (panelViewNode) {
panelViewNode.remove();
}
return;
}
if (!panelViewNode) {
panelViewNode = this._makePanelViewNodeForAction(action, false);
this.multiViewNode.appendChild(panelViewNode);
action.onSubviewPlaced(panelViewNode);
}
},
@ -554,26 +648,25 @@ var BrowserPageActions = {
// If we're in the panel, open a subview inside the panel:
// Note that we can't use this.panelNode.contains(buttonNode) here
// because of XBL boundaries breaking Element.contains.
if (action.subview &&
if (action.getWantsSubview(window) &&
buttonNode &&
buttonNode.closest("panel") == this.panelNode) {
let panelViewNodeID = this._panelViewNodeIDForActionID(action.id, false);
let panelViewNode = document.getElementById(panelViewNodeID);
action.subview.onShowing(panelViewNode);
action.onSubviewShowing(panelViewNode);
this.multiViewNode.showSubView(panelViewNode, buttonNode);
return;
}
// Otherwise, hide the main popup in case it was open:
PanelMultiView.hidePopup(this.panelNode);
// Toggle the activated action's panel if necessary
if (action.subview || action.wantsIframe) {
this.togglePanelForAction(action);
return;
let aaPanelNode = this.activatedActionPanelNode;
if (!aaPanelNode || aaPanelNode.getAttribute("actionID") != action.id) {
action.onCommand(event, buttonNode);
}
if (action.getWantsSubview(window) || action.wantsIframe) {
this.togglePanelForAction(action);
}
// Otherwise, run the action.
action.onCommand(event, buttonNode);
},
/**
@ -661,12 +754,6 @@ var BrowserPageActions = {
return `pageAction-${placementID}-${actionID}-subview`;
},
// The ID of the given button in the given action's panelview.
_panelViewButtonNodeIDForActionID(actionID, buttonID, forUrlbar) {
let placementID = forUrlbar ? "urlbar" : "panel";
return `pageAction-${placementID}-${actionID}-${buttonID}`;
},
// The ID of the action corresponding to the given top-level button in the
// panel or button in the urlbar.
_actionIDForNodeID(nodeID) {
@ -723,15 +810,7 @@ var BrowserPageActions = {
* if the user clicked something to open the panel)
*/
showPanel(event = null) {
for (let action of PageActions.actions) {
let buttonNode = this.panelButtonNodeForActionID(action.id);
action.onShowingInPanel(buttonNode);
}
this.panelNode.hidden = false;
this.panelNode.addEventListener("popuphiding", () => {
this.mainButtonNode.removeAttribute("open");
}, {once: true});
this.mainButtonNode.setAttribute("open", "true");
PanelMultiView.openPopup(this.panelNode, this.mainButtonNode, {
position: "bottomcenter topright",
@ -886,10 +965,29 @@ var BrowserPageActionFeedback = {
return this.feedbackLabel = document.getElementById("pageActionFeedbackMessage");
},
show(action, event, textContentOverride) {
this.feedbackLabel.textContent = this.panelNode.getAttribute((textContentOverride || action.id) + "Feedback");
/**
* Shows the feedback popup for an action.
*
* @param action (PageActions.Action, required)
* The action associated with the feedback.
* @param opts (object, optional)
* An object with the following optional properties:
* - event (DOM event): The event that triggered the feedback.
* - textAttributeOverride (string): Normally the feedback text is
* taken from an attribute on the feedback panel. The attribute's
* name is `${action.id}Feedback`. Use this to override the
* action.id part of the name.
* - text (string): The text string. If not given, an attribute on
* panel is assumed to contain the text, as described above.
*/
show(action, opts = {}) {
this.feedbackLabel.textContent =
opts.text ||
this.panelNode.getAttribute((opts.textAttributeOverride || action.id) +
"Feedback");
this.panelNode.hidden = false;
let event = opts.event || null;
let anchor = BrowserPageActions.panelAnchorNodeForAction(action, event);
PanelMultiView.openPopup(this.panelNode, anchor, {
position: "bottomcenter topright",
@ -940,7 +1038,9 @@ BrowserPageActions.copyURL = {
.getService(Ci.nsIClipboardHelper)
.copyString(gURLBar.makeURIReadable(gBrowser.selectedBrowser.currentURI).displaySpec);
let action = PageActions.actionForID("copyURL");
BrowserPageActionFeedback.show(action, event);
BrowserPageActionFeedback.show(action, {
event,
});
},
};
@ -966,6 +1066,15 @@ BrowserPageActions.sendToDevice = {
onSubviewPlaced(panelViewNode) {
let bodyNode = panelViewNode.querySelector(".panel-subview-body");
let notReady = document.createElement("toolbarbutton");
notReady.classList.add(
"subviewbutton",
"subviewbutton-iconic",
"pageAction-sendToDevice-notReady"
);
notReady.setAttribute("label", "sendToDevice-notReadyTitle");
notReady.setAttribute("disabled", "true");
bodyNode.appendChild(notReady);
for (let node of bodyNode.childNodes) {
BrowserPageActions.takeNodeAttributeFromPanel(node, "title");
BrowserPageActions.takeNodeAttributeFromPanel(node, "shortcut");
@ -1008,8 +1117,11 @@ BrowserPageActions.sendToDevice = {
// in", "Learn about Sync", etc. Device items will be .sendtab-target.
if (event.target.classList.contains("sendtab-target")) {
let action = PageActions.actionForID("sendToDevice");
let textOverride = gSync.offline && "sendToDeviceOffline";
BrowserPageActionFeedback.show(action, event, textOverride);
let textAttributeOverride = gSync.offline && "sendToDeviceOffline";
BrowserPageActionFeedback.show(action, {
event,
textAttributeOverride,
});
}
});
return item;
@ -1032,3 +1144,118 @@ BrowserPageActions.sendToDevice = {
}
},
};
// add search engine
BrowserPageActions.addSearchEngine = {
get action() {
return PageActions.actionForID("addSearchEngine");
},
get engines() {
return gBrowser.selectedBrowser.engines || [];
},
get strings() {
delete this.strings;
let uri = "chrome://browser/locale/search.properties";
return this.strings = Services.strings.createBundle(uri);
},
updateEngines() {
// As a slight optimization, if the action isn't in the urlbar, don't do
// anything here except disable it. The action's panel nodes are updated
// when the panel is shown.
this.action.setDisabled(!this.engines.length, window);
if (this.action.shouldShowInUrlbar(window)) {
this._updateTitleAndIcon();
}
},
_updateTitleAndIcon() {
if (!this.engines.length) {
return;
}
let title =
this.engines.length == 1 ?
this.strings.formatStringFromName("searchAddFoundEngine",
[this.engines[0].title], 1) :
this.strings.GetStringFromName("searchAddFoundEngineMenu");
this.action.setTitle(title, window);
this.action.setIconURL(this.engines[0].icon, window);
},
onShowingInPanel() {
this._updateTitleAndIcon();
this.action.setWantsSubview(this.engines.length > 1, window);
let button = BrowserPageActions.panelButtonNodeForActionID(this.action.id);
button.classList.add("badged-button");
button.setAttribute("image", this.engines[0].icon);
button.setAttribute("uri", this.engines[0].uri);
button.setAttribute("crop", "center");
},
onSubviewShowing(panelViewNode) {
let body = panelViewNode.querySelector(".panel-subview-body");
while (body.firstChild) {
body.firstChild.remove();
}
for (let engine of this.engines) {
let button = document.createElement("toolbarbutton");
button.classList.add("subviewbutton", "subviewbutton-iconic");
button.setAttribute("label", engine.title);
button.setAttribute("image", engine.icon);
button.setAttribute("uri", engine.uri);
button.addEventListener("command", event => {
PanelMultiView.hidePopup(BrowserPageActions.panelNode);
this._handleClickOnEngineButton(button);
});
body.appendChild(button);
}
},
onCommand(event, buttonNode) {
if (!buttonNode.closest("panel")) {
// The urlbar button was clicked. It should have a subview if there are
// many engines.
let manyEngines = this.engines.length > 1;
this.action.setWantsSubview(manyEngines, window);
if (manyEngines) {
return;
}
}
this._handleClickOnEngineButton(buttonNode);
},
_handleClickOnEngineButton(button) {
this._installEngine(button.getAttribute("uri"),
button.getAttribute("image"));
},
_installEngine(uri, image) {
Services.search.addEngine(uri, null, image, false, {
onSuccess: engine => {
BrowserPageActionFeedback.show(this.action, {
text: this.strings.GetStringFromName("searchAddedFoundEngine"),
});
},
onError(errorCode) {
if (errorCode != Ci.nsISearchInstallCallback.ERROR_DUPLICATE_ENGINE) {
// Download error is shown by the search service
return;
}
const kSearchBundleURI = "chrome://global/locale/search/search.properties";
let searchBundle = Services.strings.createBundle(kSearchBundleURI);
let brandBundle = document.getElementById("bundle_brand");
let brandName = brandBundle.getString("brandShortName");
let title = searchBundle.GetStringFromName("error_invalid_engine_title");
let text = searchBundle.formatStringFromName("error_duplicate_engine_msg",
[brandName, uri], 2);
Services.prompt.QueryInterface(Ci.nsIPromptFactory);
let prompt = Services.prompt.getPrompt(gBrowser.contentWindow, Ci.nsIPrompt);
prompt.QueryInterface(Ci.nsIWritablePropertyBag2);
prompt.setPropertyAsBool("allowTabModal", true);
prompt.alert(title, text);
},
});
},
};

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

@ -8,6 +8,7 @@
var TabsInTitlebar = {
init() {},
whenWindowLayoutReady() {},
uninit() {},
allowedBy() {},
update() {},

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

@ -45,7 +45,13 @@ var TabsInTitlebar = {
gDragSpaceObserver.init();
this.update(true);
this._initialized = true;
this.update();
},
whenWindowLayoutReady() {
this._windowLayoutReady = true;
this.update();
},
allowedBy(condition, allow) {
@ -102,11 +108,6 @@ var TabsInTitlebar = {
}
},
onDOMContentLoaded() {
this._domLoaded = true;
this.update();
},
_onMenuMutate(aMutations) {
for (let mutation of aMutations) {
if (mutation.attributeName == "inactive" ||
@ -117,149 +118,49 @@ var TabsInTitlebar = {
}
},
_initialized: false,
_windowLayoutReady: false,
_disallowed: {},
_prefName: "browser.tabs.drawInTitlebar",
_lastSizeMode: null,
_domLoaded: false,
_readPref() {
this.allowedBy("pref",
Services.prefs.getBoolPref(this._prefName));
},
update(aFromInit = false) {
let $ = id => document.getElementById(id);
let rect = ele => ele.getBoundingClientRect();
let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
if (window.fullScreen)
return;
// We want to do this from initialization anyway, so that the
// "chromemargin" attributes are all correctly setup, but we don't want to
// do this before the DOM loads again, to prevent spurious reflows that
// will be superseded by the one on onDOMContentLoaded.
if (!this._domLoaded && !aFromInit) {
update() {
if (!this._initialized || window.fullScreen) {
return;
}
let allowed = (Object.keys(this._disallowed)).length == 0;
let titlebar = $("titlebar");
let titlebarContent = $("titlebar-content");
let titlebarButtons = $("titlebar-buttonbox");
let menubar = $("toolbar-menubar");
if (allowed) {
// We set the tabsintitlebar attribute first so that our CSS for
// tabsintitlebar manifests before we do our measurements.
document.documentElement.setAttribute("tabsintitlebar", "true");
updateTitlebarDisplay();
// Reset the custom titlebar height if the menubar is shown,
// because we will want to calculate its original height.
let buttonsShouldMatchTabHeight =
AppConstants.isPlatformAndVersionAtLeast("win", "10.0") ||
AppConstants.platform == "linux";
if (buttonsShouldMatchTabHeight &&
(menubar.getAttribute("inactive") != "true" ||
menubar.getAttribute("autohide") != "true")) {
titlebarButtons.style.removeProperty("height");
}
// Try to avoid reflows in this code by calculating dimensions first and
// then later set the properties affecting layout together in a batch.
// Get the height of the tabs toolbar:
let fullTabsHeight = rect($("TabsToolbar")).height;
// Buttons first:
let captionButtonsBoxWidth = rect(titlebarButtons).width;
let secondaryButtonsWidth, menuHeight, fullMenuHeight, menuStyles;
if (AppConstants.platform == "macosx") {
secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
// No need to look up the menubar stuff on OS X:
menuHeight = 0;
fullMenuHeight = 0;
} else {
// Otherwise, get the height and margins separately for the menubar
menuHeight = rect(menubar).height;
menuStyles = window.getComputedStyle(menubar);
fullMenuHeight = verticalMargins(menuStyles) + menuHeight;
}
// And get the height of what's in the titlebar:
let titlebarContentHeight = rect(titlebarContent).height;
// Begin setting CSS properties which will cause a reflow
// Adjust the window controls to span the entire
// tab strip height if we're not showing a menu bar.
if (buttonsShouldMatchTabHeight && !menuHeight) {
titlebarContentHeight = fullTabsHeight;
titlebarButtons.style.height = titlebarContentHeight + "px";
}
// If the menubar is around (menuHeight is non-zero), try to adjust
// its full height (i.e. including margins) to match the titlebar,
// by changing the menubar's bottom padding
if (menuHeight) {
// Calculate the difference between the titlebar's height and that of the menubar
let menuTitlebarDelta = titlebarContentHeight - fullMenuHeight;
let paddingBottom;
// The titlebar is bigger:
if (menuTitlebarDelta > 0) {
fullMenuHeight += menuTitlebarDelta;
// If there is already padding on the menubar, we need to add that
// to the difference so the total padding is correct:
if ((paddingBottom = menuStyles.paddingBottom)) {
menuTitlebarDelta += parseFloat(paddingBottom);
}
menubar.style.paddingBottom = menuTitlebarDelta + "px";
// The menubar is bigger, but has bottom padding we can remove:
} else if (menuTitlebarDelta < 0 && (paddingBottom = menuStyles.paddingBottom)) {
let existingPadding = parseFloat(paddingBottom);
// menuTitlebarDelta is negative; work out what's left, but don't set negative padding:
let desiredPadding = Math.max(0, existingPadding + menuTitlebarDelta);
menubar.style.paddingBottom = desiredPadding + "px";
// We've changed the menu height now:
fullMenuHeight += desiredPadding - existingPadding;
}
}
// Next, we calculate how much we need to stretch the titlebar down to
// go all the way to the bottom of the tab strip, if necessary.
let tabAndMenuHeight = fullTabsHeight + fullMenuHeight;
if (tabAndMenuHeight > titlebarContentHeight) {
// We need to increase the titlebar content's outer height (ie including margins)
// to match the tab and menu height:
let extraMargin = tabAndMenuHeight - titlebarContentHeight;
if (AppConstants.platform != "macosx") {
titlebarContent.style.marginBottom = extraMargin + "px";
}
titlebarContentHeight += extraMargin;
} else {
titlebarContent.style.removeProperty("margin-bottom");
}
// Then add a negative margin to the titlebar, so that the following elements
// will overlap it by the greater of the titlebar height or the tabstrip+menu.
let maxTitlebarOrTabsHeight = Math.max(titlebarContentHeight, tabAndMenuHeight);
titlebar.style.marginBottom = "-" + maxTitlebarOrTabsHeight + "px";
// Finally, size the placeholders:
if (AppConstants.platform == "macosx") {
this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
}
this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
} else {
document.documentElement.removeAttribute("tabsintitlebar");
updateTitlebarDisplay();
}
updateTitlebarDisplay();
this._layOutTitlebar(allowed);
ToolbarIconColor.inferFromText("tabsintitlebar", allowed);
},
_layOutTitlebar(drawInTitlebar) {
if (!this._windowLayoutReady) {
return;
}
let $ = id => document.getElementById(id);
let rect = ele => ele.getBoundingClientRect();
let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
let titlebar = $("titlebar");
let menubar = $("toolbar-menubar");
if (!drawInTitlebar) {
if (AppConstants.platform == "macosx") {
let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
@ -268,9 +169,111 @@ var TabsInTitlebar = {
// Reset styles that might have been modified:
titlebar.style.marginBottom = "";
menubar.style.paddingBottom = "";
return;
}
ToolbarIconColor.inferFromText("tabsintitlebar", TabsInTitlebar.enabled);
let titlebarContent = $("titlebar-content");
let titlebarButtons = $("titlebar-buttonbox");
// Reset the custom titlebar height if the menubar is shown,
// because we will want to calculate its original height.
let buttonsShouldMatchTabHeight =
AppConstants.isPlatformAndVersionAtLeast("win", "10.0") ||
AppConstants.platform == "linux";
if (buttonsShouldMatchTabHeight &&
(menubar.getAttribute("inactive") != "true" ||
menubar.getAttribute("autohide") != "true")) {
titlebarButtons.style.removeProperty("height");
}
// Try to avoid reflows in this code by calculating dimensions first and
// then later set the properties affecting layout together in a batch.
// Get the height of the tabs toolbar:
let fullTabsHeight = rect($("TabsToolbar")).height;
// Buttons first:
let captionButtonsBoxWidth = rect(titlebarButtons).width;
let secondaryButtonsWidth, menuHeight, fullMenuHeight, menuStyles;
if (AppConstants.platform == "macosx") {
secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
// No need to look up the menubar stuff on OS X:
menuHeight = 0;
fullMenuHeight = 0;
} else {
// Otherwise, get the height and margins separately for the menubar
menuHeight = rect(menubar).height;
menuStyles = window.getComputedStyle(menubar);
fullMenuHeight = verticalMargins(menuStyles) + menuHeight;
}
// And get the height of what's in the titlebar:
let titlebarContentHeight = rect(titlebarContent).height;
// Begin setting CSS properties which will cause a reflow
// Adjust the window controls to span the entire
// tab strip height if we're not showing a menu bar.
if (buttonsShouldMatchTabHeight && !menuHeight) {
titlebarContentHeight = fullTabsHeight;
titlebarButtons.style.height = titlebarContentHeight + "px";
}
// If the menubar is around (menuHeight is non-zero), try to adjust
// its full height (i.e. including margins) to match the titlebar,
// by changing the menubar's bottom padding
if (menuHeight) {
// Calculate the difference between the titlebar's height and that of the menubar
let menuTitlebarDelta = titlebarContentHeight - fullMenuHeight;
let paddingBottom;
// The titlebar is bigger:
if (menuTitlebarDelta > 0) {
fullMenuHeight += menuTitlebarDelta;
// If there is already padding on the menubar, we need to add that
// to the difference so the total padding is correct:
if ((paddingBottom = menuStyles.paddingBottom)) {
menuTitlebarDelta += parseFloat(paddingBottom);
}
menubar.style.paddingBottom = menuTitlebarDelta + "px";
// The menubar is bigger, but has bottom padding we can remove:
} else if (menuTitlebarDelta < 0 && (paddingBottom = menuStyles.paddingBottom)) {
let existingPadding = parseFloat(paddingBottom);
// menuTitlebarDelta is negative; work out what's left, but don't set negative padding:
let desiredPadding = Math.max(0, existingPadding + menuTitlebarDelta);
menubar.style.paddingBottom = desiredPadding + "px";
// We've changed the menu height now:
fullMenuHeight += desiredPadding - existingPadding;
}
}
// Next, we calculate how much we need to stretch the titlebar down to
// go all the way to the bottom of the tab strip, if necessary.
let tabAndMenuHeight = fullTabsHeight + fullMenuHeight;
if (tabAndMenuHeight > titlebarContentHeight) {
// We need to increase the titlebar content's outer height (ie including margins)
// to match the tab and menu height:
let extraMargin = tabAndMenuHeight - titlebarContentHeight;
if (AppConstants.platform != "macosx") {
titlebarContent.style.marginBottom = extraMargin + "px";
}
titlebarContentHeight += extraMargin;
} else {
titlebarContent.style.removeProperty("margin-bottom");
}
// Then add a negative margin to the titlebar, so that the following elements
// will overlap it by the greater of the titlebar height or the tabstrip+menu.
let maxTitlebarOrTabsHeight = Math.max(titlebarContentHeight, tabAndMenuHeight);
titlebar.style.marginBottom = "-" + maxTitlebarOrTabsHeight + "px";
// Finally, size the placeholders:
if (AppConstants.platform == "macosx") {
this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
}
this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
},
_sizePlaceholder(type, width) {

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

@ -1386,8 +1386,8 @@ toolbarpaletteitem[place="palette"][hidden] {
}
/* Page action panel */
#pageAction-panel-sendToDevice-subview-body:not([state="notready"]) > #pageAction-panel-sendToDevice-notReady,
#pageAction-urlbar-sendToDevice-subview-body:not([state="notready"]) > #pageAction-urlbar-sendToDevice-notReady {
#pageAction-panel-sendToDevice-subview-body:not([state="notready"]) > .pageAction-sendToDevice-notReady,
#pageAction-urlbar-sendToDevice-subview-body:not([state="notready"]) > .pageAction-sendToDevice-notReady {
display: none;
}

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

@ -1203,11 +1203,12 @@ var gBrowserInit = {
}
}
new LightweightThemeConsumer(document);
CompactTheme.init();
// Update the chromemargin attribute so the window can be sized correctly.
window.TabBarVisibility.update();
TabsInTitlebar.init();
new LightweightThemeConsumer(document);
CompactTheme.init();
if (window.matchMedia("(-moz-os-version: windows-win8)").matches &&
window.matchMedia("(-moz-windows-default-theme)").matches) {
let windowFrameColor = new Color(...ChromeUtils.import("resource:///modules/Windows8WindowFrameColor.jsm", {})
@ -1267,8 +1268,6 @@ var gBrowserInit = {
remoteType, sameProcessAsFrameLoader
});
gUIDensity.init();
// Hack to ensure that the about:home favicon is loaded
// instantaneously, to avoid flickering and improve perceived performance.
this._callWithURIToLoad(uriToLoad => {
@ -1281,8 +1280,9 @@ var gBrowserInit = {
this._setInitialFocus();
window.TabBarVisibility.update();
TabsInTitlebar.onDOMContentLoaded();
// Update the UI density before TabsInTitlebar lays out the titlbar.
gUIDensity.init();
TabsInTitlebar.whenWindowLayoutReady();
},
onLoad() {
@ -1346,6 +1346,7 @@ var gBrowserInit = {
TabletModeUpdater.init();
CombinedStopReload.ensureInitialized();
gPrivateBrowsingUI.init();
BrowserSearch.init();
BrowserPageActions.init();
gAccessibilityServiceIndicator.init();
@ -1876,6 +1877,8 @@ var gBrowserInit = {
LanguagePrompt.uninit();
BrowserSearch.uninit();
// Now either cancel delayedStartup, or clean up the services initialized from
// it.
if (this._boundDelayedStartup) {
@ -3747,6 +3750,83 @@ const DOMEventHandler = {
};
const BrowserSearch = {
init() {
Services.obs.addObserver(this, "browser-search-engine-modified");
},
uninit() {
Services.obs.removeObserver(this, "browser-search-engine-modified");
},
observe(engine, topic, data) {
// There are two kinds of search engine objects, nsISearchEngine objects and
// plain { uri, title, icon } objects. `engine` in this method is the
// former. The browser.engines and browser.hiddenEngines arrays are the
// latter, and they're the engines offered by the the page in the browser.
//
// The two types of engines are currently related by their titles/names,
// although that may change; see bug 335102.
let engineName = engine.wrappedJSObject.name;
switch (data) {
case "engine-removed":
// An engine was removed from the search service. If a page is offering
// the engine, then the engine needs to be added back to the corresponding
// browser's offered engines.
this._addMaybeOfferedEngine(engineName);
break;
case "engine-added":
// An engine was added to the search service. If a page is offering the
// engine, then the engine needs to be removed from the corresponding
// browser's offered engines.
this._removeMaybeOfferedEngine(engineName);
break;
}
},
_addMaybeOfferedEngine(engineName) {
let selectedBrowserOffersEngine = false;
for (let browser of gBrowser.browsers) {
for (let i = 0; i < (browser.hiddenEngines || []).length; i++) {
if (browser.hiddenEngines[i].title == engineName) {
if (!browser.engines) {
browser.engines = [];
}
browser.engines.push(browser.hiddenEngines[i]);
browser.hiddenEngines.splice(i, 1);
if (browser == gBrowser.selectedBrowser) {
selectedBrowserOffersEngine = true;
}
break;
}
}
}
if (selectedBrowserOffersEngine) {
this.updateOpenSearchBadge();
}
},
_removeMaybeOfferedEngine(engineName) {
let selectedBrowserOffersEngine = false;
for (let browser of gBrowser.browsers) {
for (let i = 0; i < (browser.engines || []).length; i++) {
if (browser.engines[i].title == engineName) {
if (!browser.hiddenEngines) {
browser.hiddenEngines = [];
}
browser.hiddenEngines.push(browser.engines[i]);
browser.engines.splice(i, 1);
if (browser == gBrowser.selectedBrowser) {
selectedBrowserOffersEngine = true;
}
break;
}
}
}
if (selectedBrowserOffersEngine) {
this.updateOpenSearchBadge();
}
},
addEngine(browser, engine, uri) {
// Check to see whether we've already added an engine with this title
if (browser.engines) {
@ -3784,6 +3864,8 @@ const BrowserSearch = {
* has search engines.
*/
updateOpenSearchBadge() {
BrowserPageActions.addSearchEngine.updateEngines();
var searchBar = this.searchBar;
if (!searchBar)
return;

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

@ -4711,7 +4711,8 @@ var TabBarVisibility = {
update() {
let toolbar = document.getElementById("TabsToolbar");
let collapse = false;
if (gBrowser.tabs.length - gBrowser._removingTabs.length == 1) {
if (!gBrowser /* gBrowser isn't initialized yet */ ||
gBrowser.tabs.length - gBrowser._removingTabs.length == 1) {
collapse = !window.toolbar.visible;
}

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

@ -23,9 +23,9 @@ if (Services.appinfo.OS == "WINNT") {
{
stack: [
"verticalMargins@chrome://browser/content/browser-tabsintitlebar.js",
"_layOutTitlebar@chrome://browser/content/browser-tabsintitlebar.js",
"update@chrome://browser/content/browser-tabsintitlebar.js",
"onDOMContentLoaded@chrome://browser/content/browser-tabsintitlebar.js",
"onDOMContentLoaded@chrome://browser/content/browser.js",
"whenWindowLayoutReady@chrome://browser/content/browser-tabsintitlebar.js",
],
maxCount: 2, // This number should only ever go down - never up.
},
@ -37,9 +37,9 @@ if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") {
{
stack: [
"rect@chrome://browser/content/browser-tabsintitlebar.js",
"_layOutTitlebar@chrome://browser/content/browser-tabsintitlebar.js",
"update@chrome://browser/content/browser-tabsintitlebar.js",
"onDOMContentLoaded@chrome://browser/content/browser-tabsintitlebar.js",
"onDOMContentLoaded@chrome://browser/content/browser.js",
"whenWindowLayoutReady@chrome://browser/content/browser-tabsintitlebar.js",
],
// These numbers should only ever go down - never up.
maxCount: Services.appinfo.OS == "WINNT" ? 5 : 4,
@ -122,6 +122,7 @@ add_task(async function() {
{
stack: [
"rect@chrome://browser/content/browser-tabsintitlebar.js",
"_layOutTitlebar@chrome://browser/content/browser-tabsintitlebar.js",
"update@chrome://browser/content/browser-tabsintitlebar.js",
"handleEvent@chrome://browser/content/browser-tabsintitlebar.js",
],
@ -130,6 +131,7 @@ add_task(async function() {
{
stack: [
"verticalMargins@chrome://browser/content/browser-tabsintitlebar.js",
"_layOutTitlebar@chrome://browser/content/browser-tabsintitlebar.js",
"update@chrome://browser/content/browser-tabsintitlebar.js",
"handleEvent@chrome://browser/content/browser-tabsintitlebar.js",
],

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

@ -42,6 +42,14 @@ skip-if = true # Bug 1315887
[browser_moz_action_link.js]
[browser_new_tab_urlbar_reset.js]
[browser_page_action_menu.js]
[browser_page_action_menu_add_search_engine.js]
support-files =
page_action_menu_add_search_engine_one.html
page_action_menu_add_search_engine_many.html
page_action_menu_add_search_engine_same_names.html
page_action_menu_add_search_engine_0.xml
page_action_menu_add_search_engine_1.xml
page_action_menu_add_search_engine_2.xml
[browser_page_action_menu_clipboard.js]
subsuite = clipboard
[browser_pasteAndGo.js]

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

@ -14,15 +14,6 @@ const mockRemoteClients = [
{ id: "2", name: "baz", type: "mobile", serverLastModified: lastModifiedFixture },
];
add_task(async function init() {
// Disable panel animations. They cause intermittent timeouts on Linux when
// the test tries to synthesize clicks on items in newly opened panels.
BrowserPageActions._disablePanelAnimations = true;
registerCleanupFunction(() => {
BrowserPageActions._disablePanelAnimations = false;
});
});
add_task(async function bookmark() {
// Open a unique page.
let url = "http://example.com/browser_page_action_menu";
@ -236,7 +227,7 @@ add_task(async function sendToDevice_syncNotReady_other_states() {
let expectedItems = [
{
id: "pageAction-panel-sendToDevice-notReady",
className: "pageAction-sendToDevice-notReady",
display: "none",
disabled: true,
},
@ -309,7 +300,7 @@ add_task(async function sendToDevice_syncNotReady_configured() {
// "Syncing devices" should be shown.
checkSendToDeviceItems([
{
id: "pageAction-panel-sendToDevice-notReady",
className: "pageAction-sendToDevice-notReady",
disabled: true,
},
]);
@ -317,7 +308,7 @@ add_task(async function sendToDevice_syncNotReady_configured() {
// The devices should be shown in the subview.
let expectedItems = [
{
id: "pageAction-panel-sendToDevice-notReady",
className: "pageAction-sendToDevice-notReady",
display: "none",
disabled: true,
},
@ -373,7 +364,7 @@ add_task(async function sendToDevice_notSignedIn() {
let expectedItems = [
{
id: "pageAction-panel-sendToDevice-notReady",
className: "pageAction-sendToDevice-notReady",
display: "none",
disabled: true,
},
@ -434,7 +425,7 @@ add_task(async function sendToDevice_noDevices() {
let expectedItems = [
{
id: "pageAction-panel-sendToDevice-notReady",
className: "pageAction-sendToDevice-notReady",
display: "none",
disabled: true,
},
@ -500,7 +491,7 @@ add_task(async function sendToDevice_devices() {
// The devices should be shown in the subview.
let expectedItems = [
{
id: "pageAction-panel-sendToDevice-notReady",
className: "pageAction-sendToDevice-notReady",
display: "none",
disabled: true,
},
@ -570,7 +561,7 @@ add_task(async function sendToDevice_inUrlbar() {
// The devices should be shown in the subview.
let expectedItems = [
{
id: "pageAction-urlbar-sendToDevice-notReady",
className: "pageAction-sendToDevice-notReady",
display: "none",
disabled: true,
},
@ -772,6 +763,13 @@ function checkSendToDeviceItems(expectedItems, forUrlbar = false) {
if ("id" in expected) {
Assert.equal(actual.id, expected.id);
}
if ("className" in expected) {
let expectedNames = expected.className.split(/\s+/);
for (let name of expectedNames) {
Assert.ok(actual.classList.contains(name),
`classList contains: ${name}`);
}
}
let display = "display" in expected ? expected.display : "-moz-box";
Assert.equal(getComputedStyle(actual).display, display);
let disabled = "disabled" in expected ? expected.disabled : false;

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

@ -0,0 +1,305 @@
"use strict";
// Checks a page that doesn't offer any engines.
add_task(async function none() {
let url = "http://mochi.test:8888/";
await BrowserTestUtils.withNewTab(url, async () => {
// Open the panel.
await promisePageActionPanelOpen();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
// The action should not be present.
let actions = PageActions.actionsInPanel(window);
Assert.ok(!actions.some(a => a.id == "addSearchEngine"),
"Action should not be present in panel");
let button =
BrowserPageActions.panelButtonNodeForActionID("addSearchEngine");
Assert.ok(!button, "Action button should not be in panel");
});
});
// Checks a page that offers one engine.
add_task(async function one() {
let url = getRootDirectory(gTestPath) + "page_action_menu_add_search_engine_one.html";
await BrowserTestUtils.withNewTab(url, async () => {
// Open the panel.
await promisePageActionPanelOpen();
// The action should be present.
let actions = PageActions.actionsInPanel(window);
let action = actions.find(a => a.id == "addSearchEngine");
Assert.ok(action, "Action should be present in panel");
let expectedTitle =
"Add \u{201C}page_action_menu_add_search_engine_0\u{201D} to One-Click Search";
Assert.equal(action.getTitle(window), expectedTitle, "Action title");
let button =
BrowserPageActions.panelButtonNodeForActionID("addSearchEngine");
Assert.ok(button, "Button should be in panel");
Assert.equal(button.label, expectedTitle, "Button label");
Assert.equal(button.classList.contains("subviewbutton-nav"), false,
"Button should not expand into a subview");
// Click the action's button.
let enginePromise =
promiseEngine("engine-added", "page_action_menu_add_search_engine_0");
let hiddenPromise = promisePageActionPanelHidden();
let feedbackPromise = promiseFeedbackPanelHidden();
EventUtils.synthesizeMouseAtCenter(button, {});
await hiddenPromise;
let engine = await enginePromise;
let feedbackText = await feedbackPromise;
Assert.equal(feedbackText, "Added to Search Dropdown");
// Open the panel again.
await promisePageActionPanelOpen();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
// The action should be gone.
actions = PageActions.actionsInPanel(window);
action = actions.find(a => a.id == "addSearchEngine");
Assert.ok(!action, "Action should not be present in panel");
button = BrowserPageActions.panelButtonNodeForActionID("addSearchEngine");
Assert.ok(!button, "Action button should not be in panel");
// Remove the engine.
enginePromise =
promiseEngine("engine-removed", "page_action_menu_add_search_engine_0");
Services.search.removeEngine(engine);
await enginePromise;
// Open the panel again.
await promisePageActionPanelOpen();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
// The action should be present again.
actions = PageActions.actionsInPanel(window);
action = actions.find(a => a.id == "addSearchEngine");
Assert.ok(action, "Action should be present in panel");
Assert.equal(action.getTitle(window), expectedTitle, "Action title");
button = BrowserPageActions.panelButtonNodeForActionID("addSearchEngine");
Assert.ok(button, "Action button should be in panel");
Assert.equal(button.label, expectedTitle, "Button label");
Assert.equal(button.classList.contains("subviewbutton-nav"), false,
"Button should not expand into a subview");
});
});
// Checks a page that offers many engines.
add_task(async function many() {
let url = getRootDirectory(gTestPath) + "page_action_menu_add_search_engine_many.html";
await BrowserTestUtils.withNewTab(url, async () => {
// Open the panel.
await promisePageActionPanelOpen();
// The action should be present.
let actions = PageActions.actionsInPanel(window);
let action = actions.find(a => a.id == "addSearchEngine");
Assert.ok(action, "Action should be present in panel");
let expectedTitle = "Add One-Click Search Engine";
Assert.equal(action.getTitle(window), expectedTitle, "Action title");
let button =
BrowserPageActions.panelButtonNodeForActionID("addSearchEngine");
Assert.ok(button, "Action button should be in panel");
Assert.equal(button.label, expectedTitle, "Button label");
Assert.equal(button.classList.contains("subviewbutton-nav"), true,
"Button should expand into a subview");
// Click the action's button. The subview should be shown.
let viewPromise = promisePageActionViewShown();
EventUtils.synthesizeMouseAtCenter(button, {});
let view = await viewPromise;
let viewID =
BrowserPageActions._panelViewNodeIDForActionID("addSearchEngine", false);
Assert.equal(view.id, viewID, "View ID");
let bodyID = viewID + "-body";
let body = document.getElementById(bodyID);
Assert.deepEqual(
Array.map(body.childNodes, n => n.label),
[
"page_action_menu_add_search_engine_0",
"page_action_menu_add_search_engine_1",
"page_action_menu_add_search_engine_2",
],
"Subview children"
);
// Click the first engine to install it.
let enginePromise =
promiseEngine("engine-added", "page_action_menu_add_search_engine_0");
let hiddenPromise = promisePageActionPanelHidden();
let feedbackPromise = promiseFeedbackPanelHidden();
EventUtils.synthesizeMouseAtCenter(body.childNodes[0], {});
await hiddenPromise;
let engines = [];
let engine = await enginePromise;
engines.push(engine);
let feedbackText = await feedbackPromise;
Assert.equal(feedbackText, "Added to Search Dropdown", "Feedback text");
// Open the panel and show the subview again. The installed engine should
// be gone.
await promisePageActionPanelOpen();
viewPromise = promisePageActionViewShown();
EventUtils.synthesizeMouseAtCenter(button, {});
await viewPromise;
Assert.deepEqual(
Array.map(body.childNodes, n => n.label),
[
"page_action_menu_add_search_engine_1",
"page_action_menu_add_search_engine_2",
],
"Subview children"
);
// Click the next engine to install it.
enginePromise =
promiseEngine("engine-added", "page_action_menu_add_search_engine_1");
hiddenPromise = promisePageActionPanelHidden();
feedbackPromise = promiseFeedbackPanelHidden();
EventUtils.synthesizeMouseAtCenter(body.childNodes[0], {});
await hiddenPromise;
engine = await enginePromise;
engines.push(engine);
feedbackText = await feedbackPromise;
Assert.equal(feedbackText, "Added to Search Dropdown", "Feedback text");
// Open the panel again. This time the action button should show the one
// remaining engine.
await promisePageActionPanelOpen();
actions = PageActions.actionsInPanel(window);
action = actions.find(a => a.id == "addSearchEngine");
Assert.ok(action, "Action should be present in panel");
expectedTitle =
"Add \u{201C}page_action_menu_add_search_engine_2\u{201D} to One-Click Search";
Assert.equal(action.getTitle(window), expectedTitle, "Action title");
button = BrowserPageActions.panelButtonNodeForActionID("addSearchEngine");
Assert.ok(button, "Button should be present in panel");
Assert.equal(button.label, expectedTitle, "Button label");
Assert.equal(button.classList.contains("subviewbutton-nav"), false,
"Button should not expand into a subview");
// Click the button.
enginePromise =
promiseEngine("engine-added", "page_action_menu_add_search_engine_2");
hiddenPromise = promisePageActionPanelHidden();
feedbackPromise = promiseFeedbackPanelHidden();
EventUtils.synthesizeMouseAtCenter(button, {});
await hiddenPromise;
engine = await enginePromise;
engines.push(engine);
feedbackText = await feedbackPromise;
Assert.equal(feedbackText, "Added to Search Dropdown", "Feedback text");
// All engines are installed at this point. Open the panel and make sure
// the action is gone.
await promisePageActionPanelOpen();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
actions = PageActions.actionsInPanel(window);
action = actions.find(a => a.id == "addSearchEngine");
Assert.ok(!action, "Action should be gone");
button = BrowserPageActions.panelButtonNodeForActionID("addSearchEngine");
Assert.ok(!button, "Button should not be in panel");
// Remove the first engine.
enginePromise =
promiseEngine("engine-removed", "page_action_menu_add_search_engine_0");
Services.search.removeEngine(engines.shift());
await enginePromise;
// Open the panel again. The action should be present and showing the first
// engine.
await promisePageActionPanelOpen();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
actions = PageActions.actionsInPanel(window);
action = actions.find(a => a.id == "addSearchEngine");
Assert.ok(action, "Action should be present in panel");
expectedTitle =
"Add \u{201C}page_action_menu_add_search_engine_0\u{201D} to One-Click Search";
Assert.equal(action.getTitle(window), expectedTitle, "Action title");
button = BrowserPageActions.panelButtonNodeForActionID("addSearchEngine");
Assert.ok(button, "Button should be present in panel");
Assert.equal(button.label, expectedTitle, "Button label");
Assert.equal(button.classList.contains("subviewbutton-nav"), false,
"Button should not expand into a subview");
// Remove the second engine.
enginePromise =
promiseEngine("engine-removed", "page_action_menu_add_search_engine_1");
Services.search.removeEngine(engines.shift());
await enginePromise;
// Open the panel again and check the subview. The subview should be
// present now that there are two offerred engines again.
await promisePageActionPanelOpen();
actions = PageActions.actionsInPanel(window);
action = actions.find(a => a.id == "addSearchEngine");
Assert.ok(action, "Action should be present in panel");
expectedTitle = "Add One-Click Search Engine";
Assert.equal(action.getTitle(window), expectedTitle, "Action title");
button = BrowserPageActions.panelButtonNodeForActionID("addSearchEngine");
Assert.ok(button, "Button should be in panel");
Assert.equal(button.label, expectedTitle, "Button label");
Assert.equal(button.classList.contains("subviewbutton-nav"), true,
"Button should expand into a subview");
viewPromise = promisePageActionViewShown();
EventUtils.synthesizeMouseAtCenter(button, {});
await viewPromise;
body = document.getElementById(bodyID);
Assert.deepEqual(
Array.map(body.childNodes, n => n.label),
[
"page_action_menu_add_search_engine_0",
"page_action_menu_add_search_engine_1",
],
"Subview children"
);
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
// Remove the third engine.
enginePromise =
promiseEngine("engine-removed", "page_action_menu_add_search_engine_2");
Services.search.removeEngine(engines.shift());
await enginePromise;
// Open the panel again and check the subview.
await promisePageActionPanelOpen();
viewPromise = promisePageActionViewShown();
EventUtils.synthesizeMouseAtCenter(button, {});
await viewPromise;
Assert.deepEqual(
Array.map(body.childNodes, n => n.label),
[
"page_action_menu_add_search_engine_0",
"page_action_menu_add_search_engine_1",
"page_action_menu_add_search_engine_2",
],
"Subview children"
);
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
});
});
function promiseEngine(expectedData, expectedEngineName) {
return TestUtils.topicObserved("browser-search-engine-modified", (engine, data) => {
return expectedData == data &&
expectedEngineName == engine.wrappedJSObject.name;
}).then(([engine, data]) => engine);
}
function promiseFeedbackPanelHidden() {
return new Promise(resolve => {
BrowserPageActionFeedback.panelNode.addEventListener("popuphidden", event => {
resolve(BrowserPageActionFeedback.feedbackLabel.textContent);
}, {once: true});
});
}

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

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>page_action_menu_add_search_engine_0</ShortName>
<Url type="text/html" method="GET" template="http://mochi.test:8888/" rel="searchform">
<Param name="terms" value="{searchTerms}"/>
</Url>
</SearchPlugin>

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

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>page_action_menu_add_search_engine_1</ShortName>
<Url type="text/html" method="GET" template="http://mochi.test:8888/" rel="searchform">
<Param name="terms" value="{searchTerms}"/>
</Url>
</SearchPlugin>

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

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>page_action_menu_add_search_engine_2</ShortName>
<Url type="text/html" method="GET" template="http://mochi.test:8888/" rel="searchform">
<Param name="terms" value="{searchTerms}"/>
</Url>
</SearchPlugin>

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

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="search" type="application/opensearchdescription+xml" title="page_action_menu_add_search_engine_0" href="http://mochi.test:8888/browser/browser/base/content/test/urlbar/page_action_menu_add_search_engine_0.xml">
<link rel="search" type="application/opensearchdescription+xml" title="page_action_menu_add_search_engine_1" href="http://mochi.test:8888/browser/browser/base/content/test/urlbar/page_action_menu_add_search_engine_1.xml">
<link rel="search" type="application/opensearchdescription+xml" title="page_action_menu_add_search_engine_2" href="http://mochi.test:8888/browser/browser/base/content/test/urlbar/page_action_menu_add_search_engine_2.xml">
</head>
<body></body>
</html>

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

@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="search" type="application/opensearchdescription+xml" title="page_action_menu_add_search_engine_0" href="http://mochi.test:8888/browser/browser/base/content/test/urlbar/page_action_menu_add_search_engine_0.xml">
</head>
<body></body>
</html>

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

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="search" type="application/opensearchdescription+xml" title="page_action_menu_add_search_engine_0" href="http://mochi.test:8888/browser/browser/base/content/test/urlbar/page_action_menu_add_search_engine_0.xml">
<link rel="search" type="application/opensearchdescription+xml" title="page_action_menu_add_search_engine_1" href="http://mochi.test:8888/browser/browser/base/content/test/urlbar/page_action_menu_add_search_engine_0.xml">
</head>
<body></body>
</html>

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

@ -411,6 +411,11 @@ var Policies = {
},
"SearchEngines": {
onBeforeUIStartup(manager, param) {
if (param.PreventInstalls) {
manager.disallowFeature("installSearchEngine", true);
}
},
onAllWindowsRestored(manager, param) {
Services.search.init(() => {
if (param.Add) {
@ -460,9 +465,6 @@ var Policies = {
}
});
}
if (param.PreventInstalls) {
manager.disallowFeature("installSearchEngine");
}
});
}
}

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

@ -72,9 +72,46 @@ add_task(async function test_install_and_set_default() {
// Clean up
Services.search.removeEngine(Services.search.currentEngine);
EnterprisePolicyTesting.resetRunOnceState();
});
// Same as the last test, but with "PreventInstalls" set to true to make sure
// it does not prevent search engines from being installed properly
add_task(async function test_install_and_set_default_prevent_installs() {
isnot(Services.search.currentEngine.name, "MozSearch",
"Default search engine should not be MozSearch when test starts");
is(Services.search.getEngineByName("Foo"), null,
"Engine \"Foo\" should not be present when test starts");
await setupPolicyEngineWithJson({
"policies": {
"SearchEngines": {
"Add": [
{
"Name": "MozSearch",
"URLTemplate": "http://example.com/?q={searchTerms}"
}
],
"Default": "MozSearch",
"PreventInstalls": true
}
}
});
is(Services.search.currentEngine.name, "MozSearch",
"Specified search engine should be the default");
// Clean up
Services.search.removeEngine(Services.search.currentEngine);
EnterprisePolicyTesting.resetRunOnceState();
});
add_task(async function test_opensearch_works() {
// Clear out policies so we can test with no policies applied
await setupPolicyEngineWithJson({
"policies": {
}
});
// Ensure that opensearch works before we make sure that it can be properly
// disabled
await test_opensearch(true);

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

@ -188,18 +188,6 @@
<parameter name="aVerb"/>
<body><![CDATA[
if (aTopic == "browser-search-engine-modified") {
switch (aVerb) {
case "engine-removed":
this.offerNewEngine(aEngine);
break;
case "engine-added":
this.hideNewEngine(aEngine);
break;
case "engine-changed":
// An engine was removed (or hidden) or added, or an icon was
// changed. Do nothing special.
}
// Make sure the engine list is refetched next time it's needed
this._engines = null;
@ -210,75 +198,6 @@
]]></body>
</method>
<!-- There are two seaprate lists of search engines, whose uses intersect
in this file. The search service (nsIBrowserSearchService and
nsSearchService.js) maintains a list of Engine objects which is used to
populate the searchbox list of available engines and to perform queries.
That list is accessed here via this.SearchService, and it's that sort of
Engine that is passed to this binding's observer as aEngine.
In addition, browser.js fills two lists of autodetected search engines
(browser.engines and browser.hiddenEngines) as properties of
selectedBrowser. Those lists contain unnamed JS objects of the form
{ uri:, title:, icon: }, and that's what the searchbar uses to determine
whether to show any "Add <EngineName>" menu items in the drop-down.
The two types of engines are currently related by their identifying
titles (the Engine object's 'name'), although that may change; see bug
335102. -->
<!-- If the engine that was just removed from the searchbox list was
autodetected on this page, move it to each browser's active list so it
will be offered to be added again. -->
<method name="offerNewEngine">
<parameter name="aEngine"/>
<body><![CDATA[
for (let browser of gBrowser.browsers) {
if (browser.hiddenEngines) {
// XXX This will need to be changed when engines are identified by
// URL rather than title; see bug 335102.
var removeTitle = aEngine.wrappedJSObject.name;
for (var i = 0; i < browser.hiddenEngines.length; i++) {
if (browser.hiddenEngines[i].title == removeTitle) {
if (!browser.engines)
browser.engines = [];
browser.engines.push(browser.hiddenEngines[i]);
browser.hiddenEngines.splice(i, 1);
break;
}
}
}
}
BrowserSearch.updateOpenSearchBadge();
]]></body>
</method>
<!-- If the engine that was just added to the searchbox list was
autodetected on this page, move it to each browser's hidden list so it is
no longer offered to be added. -->
<method name="hideNewEngine">
<parameter name="aEngine"/>
<body><![CDATA[
for (let browser of gBrowser.browsers) {
if (browser.engines) {
// XXX This will need to be changed when engines are identified by
// URL rather than title; see bug 335102.
var removeTitle = aEngine.wrappedJSObject.name;
for (var i = 0; i < browser.engines.length; i++) {
if (browser.engines[i].title == removeTitle) {
if (!browser.hiddenEngines)
browser.hiddenEngines = [];
browser.hiddenEngines.push(browser.engines[i]);
browser.engines.splice(i, 1);
break;
}
}
}
}
BrowserSearch.updateOpenSearchBadge();
]]></body>
</method>
<method name="setIcon">
<parameter name="element"/>
<parameter name="uri"/>

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

@ -158,6 +158,7 @@ var UITour = {
if (node && !node.hidden) {
return node;
}
aDocument.ownerGlobal.BrowserPageActions.placeLazyActionsInPanel();
return aDocument.getElementById("pageAction-panel-pocket");
},
}],
@ -224,29 +225,34 @@ var UITour = {
if (node && !node.hidden) {
return node;
}
aDocument.ownerGlobal.BrowserPageActions.placeLazyActionsInPanel();
return aDocument.getElementById("pageAction-panel-bookmark");
},
}],
["pageAction-copyURL", {
query: (aDocument) => {
aDocument.ownerGlobal.BrowserPageActions.placeLazyActionsInPanel();
return aDocument.getElementById("pageAction-urlbar-copyURL") ||
aDocument.getElementById("pageAction-panel-copyURL");
},
}],
["pageAction-emailLink", {
query: (aDocument) => {
aDocument.ownerGlobal.BrowserPageActions.placeLazyActionsInPanel();
return aDocument.getElementById("pageAction-urlbar-emailLink") ||
aDocument.getElementById("pageAction-panel-emailLink");
},
}],
["pageAction-sendToDevice", {
query: (aDocument) => {
aDocument.ownerGlobal.BrowserPageActions.placeLazyActionsInPanel();
return aDocument.getElementById("pageAction-urlbar-sendToDevice") ||
aDocument.getElementById("pageAction-panel-sendToDevice");
},
}],
["screenshots", {
query: (aDocument) => {
aDocument.ownerGlobal.BrowserPageActions.placeLazyActionsInPanel();
return aDocument.getElementById("pageAction-urlbar-screenshots") ||
aDocument.getElementById("pageAction-panel-screenshots");
},

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

@ -24,8 +24,6 @@ registerCleanupFunction(async function() {
var tests = [
taskify(async function test_highlight_accountStatus_loggedOut() {
let userData = await fxAccounts.getSignedInUser();
is(userData, null, "Not logged in initially");
await showMenuPromise("appMenu");
await showHighlightPromise("accountStatus");
let highlight = document.getElementById("UITourHighlightContainer");
@ -33,9 +31,6 @@ var tests = [
}),
taskify(async function test_highlight_accountStatus_loggedIn() {
await setSignedInUser();
let userData = await fxAccounts.getSignedInUser();
isnot(userData, null, "Logged in now");
gSync.updateAllUI({ status: UIState.STATUS_SIGNED_IN, lastSync: new Date(), email: "foo@example.com" });
await showMenuPromise("appMenu");
await showHighlightPromise("accountStatus");
@ -46,25 +41,6 @@ var tests = [
}),
];
// Watch out - these will fire observers which if you aren't careful, may
// interfere with the tests.
function setSignedInUser(data) {
if (!data) {
data = {
email: "foo@example.com",
uid: "1234@lcip.org",
assertion: "foobar",
sessionToken: "dead",
kSync: "beef",
kXCS: "cafe",
kExtSync: "bacon",
kExtKbHash: "cheese",
verified: true
};
}
return fxAccounts.setSignedInUser(data);
}
function signOut() {
// we always want a "localOnly" signout here...
return fxAccounts.signOut(true);

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

@ -8,6 +8,67 @@ function checkElements(expectPresent, l) {
}
}
async function togglePageActionPanel() {
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelEvent("popuphidden");
}
function promiseOpenPageActionPanel() {
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return BrowserTestUtils.waitForCondition(() => {
// Wait for the main page action button to become visible. It's hidden for
// some URIs, so depending on when this is called, it may not yet be quite
// visible. It's up to the caller to make sure it will be visible.
info("Waiting for main page action button to have non-0 size");
let bounds = dwu.getBoundsWithoutFlushing(BrowserPageActions.mainButtonNode);
return bounds.width > 0 && bounds.height > 0;
}).then(() => {
// Wait for the panel to become open, by clicking the button if necessary.
info("Waiting for main page action panel to be open");
if (BrowserPageActions.panelNode.state == "open") {
return Promise.resolve();
}
let shownPromise = promisePageActionPanelEvent("popupshown");
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
return shownPromise;
}).then(() => {
// Wait for items in the panel to become visible.
return promisePageActionViewChildrenVisible(BrowserPageActions.mainViewNode);
});
}
function promisePageActionPanelEvent(eventType) {
return new Promise(resolve => {
let panel = BrowserPageActions.panelNode;
if ((eventType == "popupshown" && panel.state == "open") ||
(eventType == "popuphidden" && panel.state == "closed")) {
executeSoon(resolve);
return;
}
panel.addEventListener(eventType, () => {
executeSoon(resolve);
}, { once: true });
});
}
function promisePageActionViewChildrenVisible(panelViewNode) {
info("promisePageActionViewChildrenVisible waiting for a child node to be visible");
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return BrowserTestUtils.waitForCondition(() => {
let bodyNode = panelViewNode.firstChild;
for (let childNode of bodyNode.childNodes) {
let bounds = dwu.getBoundsWithoutFlushing(childNode);
if (bounds.width > 0 && bounds.height > 0) {
return true;
}
}
return false;
});
}
add_task(async function() {
await promiseScreenshotsEnabled();
@ -15,10 +76,16 @@ add_task(async function() {
await promiseScreenshotsReset();
});
// Toggle the page action panel to get it to rebuild itself. An actionable
// page must be opened first.
let url = "http://example.com/browser_screenshots_ui_check";
await BrowserTestUtils.withNewTab(url, async () => {
await togglePageActionPanel();
await BrowserTestUtils.waitForCondition(
() => document.getElementById(BUTTON_ID),
"Screenshots button should be present", 100, 100);
await BrowserTestUtils.waitForCondition(
() => document.getElementById(BUTTON_ID),
"Screenshots button should be present", 100, 100);
checkElements(true, [BUTTON_ID]);
checkElements(true, [BUTTON_ID]);
});
});

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

@ -6,18 +6,15 @@ const NONREPORTABLE_PAGE = "about:mozilla";
on page load, or TabSelect, and disabled for everything else. */
add_task(async function test_button_state_disabled() {
let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, REPORTABLE_PAGE);
openPageActions();
await BrowserTestUtils.waitForEvent(BrowserPageActions.panelNode, "popupshown");
await openPageActions();
is(isButtonDisabled(), false, "Check that button is enabled for reportable schemes on tab load");
let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, NONREPORTABLE_PAGE);
openPageActions();
await BrowserTestUtils.waitForEvent(BrowserPageActions.panelNode, "popupshown");
await openPageActions();
is(isButtonDisabled(), true, "Check that button is disabled for non-reportable schemes on tab load");
let tab3 = await BrowserTestUtils.openNewForegroundTab(gBrowser, REPORTABLE_PAGE2);
openPageActions();
await BrowserTestUtils.waitForEvent(BrowserPageActions.panelNode, "popupshown");
await openPageActions();
is(isButtonDisabled(), false, "Check that button is enabled for reportable schemes on tab load");
BrowserTestUtils.removeTab(tab1);

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

@ -8,6 +8,7 @@ add_task(async function test_opened_page() {
let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
await openPageActions();
let webcompatButton = document.getElementById(WC_PAGE_ACTION_ID);
ok(webcompatButton, "Report Site Issue button exists.");
@ -20,7 +21,6 @@ add_task(async function test_opened_page() {
resolve(tab);
}, { once: true });
});
openPageActions();
webcompatButton.click();
let tab2 = await newTabPromise;
await screenshotPromise;

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

@ -17,8 +17,56 @@ function isURLButtonEnabled() {
}
function openPageActions() {
var event = new MouseEvent("click");
BrowserPageActions.mainButtonClicked(event);
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return BrowserTestUtils.waitForCondition(() => {
// Wait for the main page action button to become visible. It's hidden for
// some URIs, so depending on when this is called, it may not yet be quite
// visible. It's up to the caller to make sure it will be visible.
info("Waiting for main page action button to have non-0 size");
let bounds = dwu.getBoundsWithoutFlushing(BrowserPageActions.mainButtonNode);
return bounds.width > 0 && bounds.height > 0;
}).then(() => {
// Wait for the panel to become open, by clicking the button if necessary.
info("Waiting for main page action panel to be open");
if (BrowserPageActions.panelNode.state == "open") {
return Promise.resolve();
}
let shownPromise = promisePageActionPanelShown();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
return shownPromise;
}).then(() => {
// Wait for items in the panel to become visible.
return promisePageActionViewChildrenVisible(BrowserPageActions.mainViewNode);
});
}
function promisePageActionPanelShown() {
return new Promise(resolve => {
if (BrowserPageActions.panelNode.state == "open") {
executeSoon(resolve);
return;
}
BrowserPageActions.panelNode.addEventListener("popupshown", () => {
executeSoon(resolve);
}, { once: true });
});
}
function promisePageActionViewChildrenVisible(panelViewNode) {
info("promisePageActionViewChildrenVisible waiting for a child node to be visible");
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return BrowserTestUtils.waitForCondition(() => {
let bodyNode = panelViewNode.firstChild;
for (let childNode of bodyNode.childNodes) {
let bounds = dwu.getBoundsWithoutFlushing(childNode);
if (bounds.width > 0 && bounds.height > 0) {
return true;
}
}
return false;
});
}
function pinToURLBar() {

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

@ -33,6 +33,10 @@ cmd_addFoundEngine=Add “%S”
# grouped in a submenu using cmd_addFoundEngineMenu as a label.
cmd_addFoundEngineMenu=Add search engine
searchAddFoundEngine=Add “%S” to One-Click Search
searchAddFoundEngineMenu=Add One-Click Search Engine
searchAddedFoundEngine=Added to Search Dropdown
# LOCALIZATION NOTE (searchForSomethingWith2):
# This string is used to build the header above the list of one-click
# search providers: "Search for <user-typed string> with:"

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

@ -7,11 +7,10 @@
var EXPORTED_SYMBOLS = [
"PageActions",
// PageActions.Action
// PageActions.Button
// PageActions.Subview
// PageActions.ACTION_ID_BOOKMARK
// PageActions.ACTION_ID_BOOKMARK_SEPARATOR
// PageActions.ACTION_ID_BUILT_IN_SEPARATOR
// PageActions.ACTION_ID_TRANSIENT_SEPARATOR
];
ChromeUtils.import("resource://gre/modules/Services.jsm");
@ -28,6 +27,7 @@ ChromeUtils.defineModuleGetter(this, "BinarySearch",
const ACTION_ID_BOOKMARK = "bookmark";
const ACTION_ID_BOOKMARK_SEPARATOR = "bookmarkSeparator";
const ACTION_ID_BUILT_IN_SEPARATOR = "builtInSeparator";
const ACTION_ID_TRANSIENT_SEPARATOR = "transientSeparator";
const PREF_PERSISTED_ACTIONS = "browser.pageActions.persistedActions";
const PERSISTED_ACTIONS_CURRENT_VERSION = 1;
@ -84,43 +84,57 @@ var PageActions = {
_deferredAddActionCalls: [],
/**
* The list of Action objects, sorted in the order in which they should be
* placed in the page action panel. If there are both built-in and non-built-
* in actions, then the list will include the separator between the two. The
* list is not live. (array of Action objects)
* A list of all Action objects, not in any particular order. Not live.
* (array of Action objects)
*/
get actions() {
let actions = this.builtInActions;
if (this.nonBuiltInActions.length) {
// There are non-built-in actions, so include them too.
let lists = [
this._builtInActions,
this._nonBuiltInActions,
this._transientActions,
];
return lists.reduce((memo, list) => memo.concat(list), []);
},
/**
* The list of Action objects that should appear in the panel for a given
* window, sorted in the order in which they appear. If there are both
* built-in and non-built-in actions, then the list will include the separator
* between the two. The list is not live. (array of Action objects)
*
* @param browserWindow (DOM window, required)
* This window's actions will be returned.
* @return (array of PageAction.Action objects) The actions currently in the
* given window's panel.
*/
actionsInPanel(browserWindow) {
function filter(action) {
return action.shouldShowInPanel(browserWindow);
}
let actions = this._builtInActions.filter(filter);
let nonBuiltInActions = this._nonBuiltInActions.filter(filter);
if (nonBuiltInActions.length) {
if (actions.length) {
// There are both built-in and non-built-in actions. Add a separator
// between the two groups so that the returned array looks like:
// [...built-ins, separator, ...non-built-ins]
actions.push(new Action({
id: ACTION_ID_BUILT_IN_SEPARATOR,
_isSeparator: true,
}));
}
actions.push(...this.nonBuiltInActions);
actions.push(...nonBuiltInActions);
}
let transientActions = this._transientActions.filter(filter);
if (transientActions.length) {
if (actions.length) {
actions.push(new Action({
id: ACTION_ID_TRANSIENT_SEPARATOR,
_isSeparator: true,
}));
}
actions.push(...transientActions);
}
return actions;
},
/**
* The list of built-in actions. Not live. (array of Action objects)
*/
get builtInActions() {
return this._builtInActions.slice();
},
/**
* The list of non-built-in actions. Not live. (array of Action objects)
*/
get nonBuiltInActions() {
return this._nonBuiltInActions.slice();
},
/**
* The list of actions currently in the urlbar, sorted in the order in which
* they appear. Not live.
@ -175,25 +189,10 @@ var PageActions = {
this._deferredAddActionCalls.push(() => this.addAction(action));
return action;
}
let hadSep = this.actions.some(a => a.id == ACTION_ID_BUILT_IN_SEPARATOR);
this._registerAction(action);
let sep = null;
if (!hadSep) {
sep = this.actions.find(a => a.id == ACTION_ID_BUILT_IN_SEPARATOR);
}
for (let bpa of allBrowserPageActions()) {
if (sep) {
// There are now both built-in and non-built-in actions, so place the
// separator between the two groups.
bpa.placeAction(sep);
}
bpa.placeAction(action);
}
return action;
},
@ -218,10 +217,13 @@ var PageActions = {
return a.id == action.__insertBeforeActionID;
});
if (index < 0) {
// Append the action.
index = this._builtInActions.length;
// Append the action (excluding transient actions).
index = this._builtInActions.filter(a => !a.__transient).length;
}
this._builtInActions.splice(index, 0, action);
} else if (action.__transient) {
// A transient action.
this._transientActions.push(action);
} else if (gBuiltInActions.find(a => a.id == action.id)) {
// A built-in action. These are always added on init before all other
// actions, one after the other, so just push onto the array.
@ -269,71 +271,9 @@ var PageActions = {
// These keep track of currently registered actions.
_builtInActions: [],
_nonBuiltInActions: [],
_transientActions: [],
_actionsByID: new Map(),
/**
* Returns the ID of the action before which the given action should be
* inserted in the urlbar.
*
* @param action (Action object, required)
* The action you're inserting.
* @return The ID of the reference action, or null if your action should be
* appended.
*/
nextActionIDInUrlbar(browserWindow, action) {
// Actions in the urlbar are always inserted before the bookmark action,
// which always comes last if it's present.
if (action.id == ACTION_ID_BOOKMARK) {
return null;
}
let id = this._nextActionID(action, this.actionsInUrlbar(browserWindow));
return id || ACTION_ID_BOOKMARK;
},
/**
* Returns the ID of the action before which the given action should be
* inserted in the panel.
*
* @param action (Action object, required)
* The action you're inserting.
* @return The ID of the reference action, or null if your action should be
* appended.
*/
nextActionIDInPanel(action) {
return this._nextActionID(action, this.actions);
},
/**
* The DOM nodes of actions should be ordered properly, both in the panel and
* the urlbar. This method returns the ID of the action that comes after the
* given action in the given array. You can use the returned ID to get a DOM
* node ID to pass to node.insertBefore().
*
* Pass PageActions.actions to get the ID of the next action in the panel.
* Pass PageActions.actionsInUrlbar to get the ID of the next action in the
* urlbar.
*
* @param action
* The action whose node you want to insert into your DOM.
* @param actionArray
* The relevant array of actions, either PageActions.actions or
* actionsInUrlbar.
* @return The ID of the action before which the given action should be
* inserted. If the given action should be inserted last, returns
* null.
*/
_nextActionID(action, actionArray) {
let index = actionArray.findIndex(a => a.id == action.id);
if (index < 0) {
return null;
}
let nextAction = actionArray[index + 1];
if (!nextAction) {
return null;
}
return nextAction.id;
},
/**
* Call this when an action is removed.
*
@ -347,7 +287,12 @@ var PageActions = {
}
this._actionsByID.delete(action.id);
for (let list of [this._nonBuiltInActions, this._builtInActions]) {
let lists = [
this._builtInActions,
this._nonBuiltInActions,
this._transientActions,
];
for (let list of lists) {
let index = list.findIndex(a => a.id == action.id);
if (index >= 0) {
list.splice(index, 1);
@ -401,6 +346,7 @@ var PageActions = {
PageActions._purgeUnregisteredPersistedActions();
PageActions._builtInActions = [];
PageActions._nonBuiltInActions = [];
PageActions._transientActions = [];
PageActions._actionsByID = new Map();
},
@ -472,6 +418,7 @@ var PageActions = {
},
};
/**
* A single page action.
*
@ -556,13 +503,18 @@ var PageActions = {
* Called when a browser window's page action panel is showing:
* onShowingInPanel(buttonNode)
* * buttonNode: The action's node in the page action panel.
* @param onSubviewPlaced (function, optional)
* Called when the action's subview is added to its parent panel in a
* browser window:
* onSubviewPlaced(panelViewNode)
* * panelViewNode: The subview's panelview node.
* @param onSubviewShowing (function, optional)
* Called when the action's subview is showing in a browser window:
* onSubviewShowing(panelViewNode)
* * panelViewNode: The subview's panelview node.
* @param pinnedToUrlbar (bool, optional)
* Pass true to pin the action to the urlbar. An action is shown in the
* urlbar if it's pinned and not disabled. False by default.
* @param subview (object, optional)
* An options object suitable for passing to the Subview constructor, if
* you'd like the action to have a subview. See the subview constructor
* for info on this object's properties.
* @param tooltip (string, optional)
* The action's button tooltip text.
* @param urlbarIDOverride (string, optional)
@ -572,6 +524,8 @@ var PageActions = {
* @param wantsIframe (bool, optional)
* Pass true to make an action that shows an iframe in a panel when
* clicked.
* @param wantsSubview (bool, optional)
* Pass true to make an action that shows a panel subview when clicked.
*/
function Action(options) {
setProperties(this, options, {
@ -593,11 +547,13 @@ function Action(options) {
onPlacedInUrlbar: false,
onRemovedFromWindow: false,
onShowingInPanel: false,
onSubviewPlaced: false,
onSubviewShowing: false,
pinnedToUrlbar: false,
subview: false,
tooltip: false,
urlbarIDOverride: false,
wantsIframe: false,
wantsSubview: false,
// private
@ -611,6 +567,12 @@ function Action(options) {
// page action panel.
_isSeparator: false,
// (bool, optional)
// Transient actions have a couple of special properties: (1) They stick to
// the bottom of the panel, and (2) they're hidden in the panel when they're
// disabled. Other than that they behave like other actions.
_transient: false,
// (bool, optional)
// True if the action's urlbar button is defined in markup. In that case, a
// node with the action's urlbar node ID should already exist in the DOM
@ -619,9 +581,6 @@ function Action(options) {
// is removed from the urlbar.
_urlbarNodeInMarkup: false,
});
if (this._subview) {
this._subview = new Subview(options.subview);
}
/**
* A cache of the pre-computed CSS variable values for a given icon
@ -638,6 +597,7 @@ function Action(options) {
iconProps: this._createIconProperties(this._iconURL),
title: this._title,
tooltip: this._tooltip,
wantsSubview: this._wantsSubview,
};
/**
@ -649,14 +609,14 @@ function Action(options) {
Action.prototype = {
/**
* The ID of the action's parent extension (string, nullable)
* The ID of the action's parent extension (string)
*/
get extensionID() {
return this._extensionID;
},
/**
* The action's ID (string, nonnull)
* The action's ID (string)
*/
get id() {
return this._id;
@ -664,7 +624,7 @@ Action.prototype = {
/**
* Attribute name => value mapping to set on nodes created for this action
* (object, nullable)
* (object)
*/
get nodeAttributes() {
return this._nodeAttributes;
@ -672,7 +632,7 @@ Action.prototype = {
/**
* True if the action is pinned to the urlbar. The action is shown in the
* urlbar if it's pinned and not disabled. (bool, nonnull)
* urlbar if it's pinned and not disabled. (bool)
*/
get pinnedToUrlbar() {
return this._pinnedToUrlbar || false;
@ -686,7 +646,7 @@ Action.prototype = {
},
/**
* The action's disabled state (bool, nonnull)
* The action's disabled state (bool)
*/
getDisabled(browserWindow = null) {
return !!this._getProperties(browserWindow).disabled;
@ -697,7 +657,7 @@ Action.prototype = {
/**
* The action's icon URL string, or an object mapping sizes to URL strings
* (string or object, nullable)
* (string or object)
*/
getIconURL(browserWindow = null) {
return this._getProperties(browserWindow).iconURL;
@ -739,7 +699,7 @@ Action.prototype = {
},
/**
* The action's title (string, nonnull)
* The action's title (string)
*/
getTitle(browserWindow = null) {
return this._getProperties(browserWindow).title;
@ -749,7 +709,7 @@ Action.prototype = {
},
/**
* The action's tooltip (string, nullable)
* The action's tooltip (string)
*/
getTooltip(browserWindow = null) {
return this._getProperties(browserWindow).tooltip;
@ -758,6 +718,16 @@ Action.prototype = {
return this._setProperty("tooltip", value, browserWindow);
},
/**
* Whether the action wants a subview (bool)
*/
getWantsSubview(browserWindow = null) {
return !!this._getProperties(browserWindow).wantsSubview;
},
setWantsSubview(value, browserWindow = null) {
return this._setProperty("wantsSubview", !!value, browserWindow);
},
/**
* Sets a property, optionally for a particular browser window.
*
@ -781,7 +751,7 @@ Action.prototype = {
// This may be called before the action has been added.
if (PageActions.actionForID(this.id)) {
for (let bpa of allBrowserPageActions(browserWindow)) {
bpa.updateAction(this, name, value);
bpa.updateAction(this, name, { value });
}
}
},
@ -812,34 +782,26 @@ Action.prototype = {
},
/**
* Override for the ID of the action's activated-action panel anchor (string,
* nullable)
* Override for the ID of the action's activated-action panel anchor (string)
*/
get anchorIDOverride() {
return this._anchorIDOverride;
},
/**
* Override for the ID of the action's urlbar node (string, nullable)
* Override for the ID of the action's urlbar node (string)
*/
get urlbarIDOverride() {
return this._urlbarIDOverride;
},
/**
* True if the action is shown in an iframe (bool, nonnull)
* True if the action is shown in an iframe (bool)
*/
get wantsIframe() {
return this._wantsIframe || false;
},
/**
* A Subview object if the action wants a subview (Subview, nullable)
*/
get subview() {
return this._subview;
},
get labelForHistogram() {
return this._labelForHistogram || this._id;
},
@ -1030,6 +992,31 @@ Action.prototype = {
}
},
/**
* Call this when a panelview node for the action's subview is added to the
* DOM.
*
* @param panelViewNode (DOM node, required)
* The subview's panelview node.
*/
onSubviewPlaced(panelViewNode) {
if (this._onSubviewPlaced) {
this._onSubviewPlaced(panelViewNode);
}
},
/**
* Call this when a panelview node for the action's subview is showing.
*
* @param panelViewNode (DOM node, required)
* The subview's panelview node.
*/
onSubviewShowing(panelViewNode) {
if (this._onSubviewShowing) {
this._onSubviewShowing(panelViewNode);
}
},
/**
* Removes the action's DOM nodes from all browser windows.
*
@ -1042,6 +1029,19 @@ Action.prototype = {
PageActions.onActionRemoved(this);
},
/**
* Returns whether the action should be shown in a given window's panel.
*
* @param browserWindow (DOM window, required)
* The window.
* @return True if the action should be shown and false otherwise. Actions
* are always shown in the panel unless they're both transient and
* disabled.
*/
shouldShowInPanel(browserWindow) {
return !this.__transient || !this.getDisabled(browserWindow);
},
/**
* Returns whether the action should be shown in a given window's urlbar.
*
@ -1066,158 +1066,14 @@ Action.prototype = {
this.PageActions.Action = Action;
/**
* A Subview represents a PanelUI panelview that your actions can show.
* `options` is a required object with the following properties.
*
* @param buttons (array, optional)
* An array of buttons to show in the subview. Each item in the array
* must be an options object suitable for passing to the Button
* constructor. See the Button constructor for information on these
* objects' properties.
* @param onPlaced (function, optional)
* Called when the subview is added to its parent panel in a browser
* window:
* onPlaced(panelViewNode)
* * panelViewNode: The panelview node represented by this Subview.
* @param onShowing (function, optional)
* Called when the subview is showing in a browser window:
* onShowing(panelViewNode)
* * panelViewNode: The panelview node represented by this Subview.
*/
function Subview(options) {
setProperties(this, options, {
buttons: false,
onPlaced: false,
onShowing: false,
});
this._buttons = (this._buttons || []).map(buttonOptions => {
return new Button(buttonOptions);
});
}
Subview.prototype = {
/**
* The subview's buttons (array of Button objects, nonnull)
*/
get buttons() {
return this._buttons;
},
/**
* Call this when a DOM node for the subview is added to the DOM.
*
* @param panelViewNode (DOM node, required)
* The subview's panelview node.
*/
onPlaced(panelViewNode) {
if (this._onPlaced) {
this._onPlaced(panelViewNode);
}
},
/**
* Call this when a DOM node for the subview is showing.
*
* @param panelViewNode (DOM node, required)
* The subview's panelview node.
*/
onShowing(panelViewNode) {
if (this._onShowing) {
this._onShowing(panelViewNode);
}
}
};
this.PageActions.Subview = Subview;
/**
* A button that can be shown in a subview. `options` is a required object with
* the following properties.
*
* @param id (string, required)
* The button's ID. This will not become the ID of a DOM node by itself,
* but it will be used to generate DOM node IDs. But in terms of spaces
* and weird characters and such, do treat this like a DOM node ID.
* @param title (string, required)
* The button's title.
* @param disabled (bool, required)
* Pass true to disable the button.
* @param onCommand (function, optional)
* Called when the button is clicked:
* onCommand(event, buttonNode)
* * event: The triggering event.
* * buttonNode: The node that was clicked.
* @param shortcut (string, optional)
* The button's shortcut text.
*/
function Button(options) {
setProperties(this, options, {
id: true,
title: true,
disabled: false,
onCommand: false,
shortcut: false,
});
}
Button.prototype = {
/**
* True if the button is disabled (bool, nonnull)
*/
get disabled() {
return this._disabled || false;
},
/**
* The button's ID (string, nonnull)
*/
get id() {
return this._id;
},
/**
* The button's shortcut (string, nullable)
*/
get shortcut() {
return this._shortcut;
},
/**
* The button's title (string, nonnull)
*/
get title() {
return this._title;
},
/**
* Call this when the user clicks the button.
*
* @param event (DOM event, required)
* The triggering event.
* @param buttonNode (DOM node, required)
* The button's DOM node that was clicked.
*/
onCommand(event, buttonNode) {
if (this._onCommand) {
this._onCommand(event, buttonNode);
}
}
};
this.PageActions.Button = Button;
this.PageActions.ACTION_ID_BUILT_IN_SEPARATOR = ACTION_ID_BUILT_IN_SEPARATOR;
this.PageActions.ACTION_ID_TRANSIENT_SEPARATOR = ACTION_ID_TRANSIENT_SEPARATOR;
// These are only necessary so that Pocket and the test can use them.
this.PageActions.ACTION_ID_BOOKMARK = ACTION_ID_BOOKMARK;
this.PageActions.ACTION_ID_BOOKMARK_SEPARATOR = ACTION_ID_BOOKMARK_SEPARATOR;
this.PageActions.PREF_PERSISTED_ACTIONS = PREF_PERSISTED_ACTIONS;
// This is only necessary so that the test can access it.
this.PageActions.ACTION_ID_BUILT_IN_SEPARATOR = ACTION_ID_BUILT_IN_SEPARATOR;
// Sorted in the order in which they should appear in the page action panel.
// Does not include the page actions of extensions bundled with the browser.
@ -1275,7 +1131,26 @@ var gBuiltInActions = [
onCommand(event, buttonNode) {
browserPageActions(buttonNode).emailLink.onCommand(event, buttonNode);
},
}
},
// add search engine
{
id: "addSearchEngine",
// The title is set in browser-pageActions.js.
title: "",
_transient: true,
onShowingInPanel(buttonNode) {
browserPageActions(buttonNode).addSearchEngine.onShowingInPanel();
},
onCommand(event, buttonNode) {
browserPageActions(buttonNode).addSearchEngine
.onCommand(event, buttonNode);
},
onSubviewShowing(panelViewNode) {
browserPageActions(panelViewNode).addSearchEngine
.onSubviewShowing(panelViewNode);
},
},
];
if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
@ -1290,22 +1165,14 @@ if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
onLocationChange(browserWindow) {
browserPageActions(browserWindow).sendToDevice.onLocationChange();
},
subview: {
buttons: [
{
id: "notReady",
title: "sendToDevice-notReadyTitle",
disabled: true,
},
],
onPlaced(panelViewNode) {
browserPageActions(panelViewNode).sendToDevice
.onSubviewPlaced(panelViewNode);
},
onShowing(panelViewNode) {
browserPageActions(panelViewNode).sendToDevice
.onShowingSubview(panelViewNode);
},
wantsSubview: true,
onSubviewPlaced(panelViewNode) {
browserPageActions(panelViewNode).sendToDevice
.onSubviewPlaced(panelViewNode);
},
onSubviewShowing(panelViewNode) {
browserPageActions(panelViewNode).sendToDevice
.onShowingSubview(panelViewNode);
},
});
}

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

@ -42,7 +42,15 @@ add_task(async function simple() {
let panelButtonID = BrowserPageActions.panelButtonNodeIDForActionID(id);
let urlbarButtonID = BrowserPageActions.urlbarButtonNodeIDForActionID(id);
// Open the panel so that actions are added to it, and then close it.
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
let initialActions = PageActions.actions;
let initialActionsInPanel = PageActions.actionsInPanel(window);
let initialActionsInUrlbar = PageActions.actionsInUrlbar(window);
let action = PageActions.addAction(new PageActions.Action({
iconURL,
@ -77,37 +85,89 @@ add_task(async function simple() {
Assert.equal(action.id, id, "id");
Assert.deepEqual(action.nodeAttributes, nodeAttributes, "nodeAttributes");
Assert.equal(action.pinnedToUrlbar, false, "pinnedToUrlbar");
Assert.equal(action.subview, null, "subview");
Assert.equal(action.getDisabled(), false, "disabled");
Assert.equal(action.getDisabled(window), false, "disabled in window");
Assert.equal(action.getTitle(), title, "title");
Assert.equal(action.getTitle(window), title, "title in window");
Assert.equal(action.getTooltip(), tooltip, "tooltip");
Assert.equal(action.getTooltip(window), tooltip, "tooltip in window");
Assert.equal(action.getWantsSubview(), false, "subview");
Assert.equal(action.getWantsSubview(window), false, "subview in window");
Assert.equal(action.urlbarIDOverride, null, "urlbarIDOverride");
Assert.equal(action.wantsIframe, false, "wantsIframe");
Assert.ok(!("__insertBeforeActionID" in action), "__insertBeforeActionID");
Assert.ok(!("__isSeparator" in action), "__isSeparator");
Assert.ok(!("__urlbarNodeInMarkup" in action), "__urlbarNodeInMarkup");
Assert.ok(!("__transient" in action), "__transient");
Assert.equal(onPlacedInPanelCallCount, 1,
"onPlacedInPanelCallCount should be inc'ed");
// The action shouldn't be placed in the panel until it opens for the first
// time.
Assert.equal(onPlacedInPanelCallCount, 0,
"onPlacedInPanelCallCount should remain 0");
Assert.equal(onPlacedInUrlbarCallCount, 0,
"onPlacedInUrlbarCallCount should remain 0");
Assert.equal(onShowingInPanelCallCount, 0,
"onShowingInPanelCallCount should remain 0");
// The separator between the built-in and non-built-in actions should have
// been created and included in PageActions.actions, which is why the new
// count should be the initial count + 2, not + 1.
Assert.equal(PageActions.actions.length, initialActions.length + 2,
"PageActions.actions.length should be updated");
Assert.deepEqual(PageActions.actions[PageActions.actions.length - 1], action,
"Last page action should be action");
Assert.equal(PageActions.actions[PageActions.actions.length - 2].id,
PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
"2nd-to-last page action should be separator");
// Open the panel so that actions are added to it, and then close it.
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.equal(onPlacedInPanelCallCount, 1,
"onPlacedInPanelCallCount should be inc'ed");
Assert.equal(onShowingInPanelCallCount, 1,
"onShowingInPanelCallCount should be inc'ed");
// Build an array of the expected actions in the panel and compare it to the
// actual actions. Don't assume that there are or aren't already other non-
// built-in actions.
let sepIndex =
initialActionsInPanel
.findIndex(a => a.id == PageActions.ACTION_ID_BUILT_IN_SEPARATOR);
let initialSepIndex = sepIndex;
let indexInPanel;
if (sepIndex < 0) {
// No prior non-built-in actions.
indexInPanel = initialActionsInPanel.length;
} else {
// Prior non-built-in actions. Find the index where the action goes.
for (indexInPanel = sepIndex + 1;
indexInPanel < initialActionsInPanel.length;
indexInPanel++) {
let a = initialActionsInPanel[indexInPanel];
if (a.getTitle().localeCompare(action.getTitle()) < 1) {
break;
}
}
}
let expectedActionsInPanel = initialActionsInPanel.slice();
expectedActionsInPanel.splice(indexInPanel, 0, action);
// The separator between the built-ins and non-built-ins should be present
// if it's not already.
if (sepIndex < 0) {
expectedActionsInPanel.splice(indexInPanel, 0, new PageActions.Action({
id: PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
_isSeparator: true,
}));
sepIndex = indexInPanel;
indexInPanel++;
}
Assert.deepEqual(PageActions.actionsInPanel(window),
expectedActionsInPanel,
"Actions in panel after adding the action");
// The actions in the urlbar should be the same since the test action isn't
// shown there.
Assert.deepEqual(PageActions.actionsInUrlbar(window),
initialActionsInUrlbar,
"Actions in urlbar after adding the action");
// Check the set of all actions.
Assert.deepEqual(new Set(PageActions.actions),
new Set(initialActions.concat([action])),
"All actions after adding the action");
Assert.deepEqual(PageActions.actionForID(action.id), action,
"actionForID should be action");
@ -116,8 +176,10 @@ add_task(async function simple() {
"PageActions should record action in its list of seen actions");
// The action's panel button should have been created.
let panelButtonNode = document.getElementById(panelButtonID);
let panelButtonNode =
BrowserPageActions.mainViewBodyNode.childNodes[indexInPanel];
Assert.notEqual(panelButtonNode, null, "panelButtonNode");
Assert.equal(panelButtonNode.id, panelButtonID, "panelButtonID");
Assert.equal(panelButtonNode.getAttribute("label"), action.getTitle(),
"label");
for (let name in action.nodeAttributes) {
@ -127,17 +189,16 @@ add_task(async function simple() {
"Equal attribute: " + name);
}
// The panel button should be the last node in the panel, and its previous
// sibling should be the separator between the built-in actions and non-built-
// in actions.
Assert.equal(panelButtonNode.nextSibling, null, "nextSibling");
Assert.notEqual(panelButtonNode.previousSibling, null, "previousSibling");
// The separator between the built-ins and non-built-ins should exist.
let sepNode =
BrowserPageActions.mainViewBodyNode.childNodes[sepIndex];
Assert.notEqual(sepNode, null, "sepNode");
Assert.equal(
panelButtonNode.previousSibling.id,
sepNode.id,
BrowserPageActions.panelButtonNodeIDForActionID(
PageActions.ACTION_ID_BUILT_IN_SEPARATOR
),
"previousSibling.id"
"sepNode.id"
);
// The action's urlbar button should not have been created.
@ -145,8 +206,8 @@ add_task(async function simple() {
Assert.equal(urlbarButtonNode, null, "urlbarButtonNode");
// Open the panel, click the action's button.
await promisePageActionPanelOpen();
Assert.equal(onShowingInPanelCallCount, 1,
await promiseOpenPageActionPanel();
Assert.equal(onShowingInPanelCallCount, 2,
"onShowingInPanelCallCount should be inc'ed");
onCommandExpectedButtonID = panelButtonID;
EventUtils.synthesizeMouseAtCenter(panelButtonNode, {});
@ -233,18 +294,27 @@ add_task(async function simple() {
urlbarButtonNode = document.getElementById(urlbarButtonID);
Assert.equal(urlbarButtonNode, null, "urlbarButtonNode");
// The separator between the built-in actions and non-built-in actions should
// be gone now, too.
let separatorNode = document.getElementById(
BrowserPageActions.panelButtonNodeIDForActionID(
PageActions.ACTION_ID_BUILT_IN_SEPARATOR
)
);
Assert.equal(separatorNode, null, "No separator");
Assert.ok(!BrowserPageActions.mainViewBodyNode
.lastChild.localName.includes("separator"),
"Last child should not be separator");
if (initialSepIndex < 0) {
// The separator between the built-in actions and non-built-in actions
// should be gone now, too.
Assert.equal(separatorNode, null, "No separator");
Assert.ok(!BrowserPageActions.mainViewBodyNode
.lastChild.localName.includes("separator"),
"Last child should not be separator");
} else {
// The separator should still be present.
Assert.notEqual(separatorNode, null, "Separator should still exist");
}
Assert.deepEqual(PageActions.actionsInPanel(window), initialActionsInPanel,
"Actions in panel should go back to initial");
Assert.deepEqual(PageActions.actionsInUrlbar(window), initialActionsInUrlbar,
"Actions in urlbar should go back to initial");
Assert.deepEqual(PageActions.actions, initialActions,
"Actions should go back to initial");
Assert.equal(PageActions.actionForID(action.id), null,
@ -262,12 +332,10 @@ add_task(async function simple() {
add_task(async function withSubview() {
let id = "test-subview";
let onActionCommandCallCount = 0;
let onActionPlacedInPanelCallCount = 0;
let onActionPlacedInUrlbarCallCount = 0;
let onSubviewPlacedCount = 0;
let onSubviewShowingCount = 0;
let onButtonCommandCallCount = 0;
let panelButtonID = BrowserPageActions.panelButtonNodeIDForActionID(id);
let urlbarButtonID = BrowserPageActions.urlbarButtonNodeIDForActionID(id);
@ -279,53 +347,13 @@ add_task(async function withSubview() {
let onSubviewPlacedExpectedPanelViewID = panelViewIDPanel;
let onSubviewShowingExpectedPanelViewID;
let onButtonCommandExpectedButtonID;
let subview = {
buttons: [0, 1, 2].map(index => {
return {
id: "test-subview-button-" + index,
title: "Test subview Button " + index,
};
}),
onPlaced(panelViewNode) {
onSubviewPlacedCount++;
Assert.ok(panelViewNode,
"panelViewNode should be non-null: " + panelViewNode);
Assert.equal(panelViewNode.id, onSubviewPlacedExpectedPanelViewID,
"panelViewNode.id");
},
onShowing(panelViewNode) {
onSubviewShowingCount++;
Assert.ok(panelViewNode,
"panelViewNode should be non-null: " + panelViewNode);
Assert.equal(panelViewNode.id, onSubviewShowingExpectedPanelViewID,
"panelViewNode.id");
},
};
subview.buttons[0].onCommand = (event, buttonNode) => {
onButtonCommandCallCount++;
Assert.ok(event, "event should be non-null: " + event);
Assert.ok(buttonNode, "buttonNode should be non-null: " + buttonNode);
Assert.equal(buttonNode.id, onButtonCommandExpectedButtonID,
"buttonNode.id");
for (let node = buttonNode.parentNode; node; node = node.parentNode) {
if (node.localName == "panel") {
node.hidePopup();
break;
}
}
};
let action = PageActions.addAction(new PageActions.Action({
iconURL: "chrome://browser/skin/mail.svg",
id,
pinnedToUrlbar: true,
subview,
title: "Test subview",
onCommand(event, buttonNode) {
onActionCommandCallCount++;
},
wantsSubview: true,
onPlacedInPanel(buttonNode) {
onActionPlacedInPanelCallCount++;
Assert.ok(buttonNode, "buttonNode should be non-null: " + buttonNode);
@ -336,33 +364,42 @@ add_task(async function withSubview() {
Assert.ok(buttonNode, "buttonNode should be non-null: " + buttonNode);
Assert.equal(buttonNode.id, urlbarButtonID, "buttonNode.id");
},
onSubviewPlaced(panelViewNode) {
onSubviewPlacedCount++;
Assert.ok(panelViewNode,
"panelViewNode should be non-null: " + panelViewNode);
Assert.equal(panelViewNode.id, onSubviewPlacedExpectedPanelViewID,
"panelViewNode.id");
},
onSubviewShowing(panelViewNode) {
onSubviewShowingCount++;
Assert.ok(panelViewNode,
"panelViewNode should be non-null: " + panelViewNode);
Assert.equal(panelViewNode.id, onSubviewShowingExpectedPanelViewID,
"panelViewNode.id");
},
}));
let panelViewButtonIDPanel =
BrowserPageActions._panelViewButtonNodeIDForActionID(
id, subview.buttons[0].id, false
);
let panelViewButtonIDUrlbar =
BrowserPageActions._panelViewButtonNodeIDForActionID(
id, subview.buttons[0].id, true
);
Assert.equal(action.id, id, "id");
Assert.notEqual(action.subview, null, "subview");
Assert.notEqual(action.subview.buttons, null, "subview.buttons");
Assert.equal(action.subview.buttons.length, subview.buttons.length,
"subview.buttons.length");
for (let i = 0; i < subview.buttons.length; i++) {
Assert.equal(action.subview.buttons[i].id, subview.buttons[i].id,
"subview button id for index: " + i);
Assert.equal(action.subview.buttons[i].title, subview.buttons[i].title,
"subview button title for index: " + i);
}
Assert.equal(action.getWantsSubview(), true, "subview");
Assert.equal(action.getWantsSubview(window), true, "subview in window");
// The action shouldn't be placed in the panel until it opens for the first
// time.
Assert.equal(onActionPlacedInPanelCallCount, 0,
"onActionPlacedInPanelCallCount should be 0");
Assert.equal(onSubviewPlacedCount, 0,
"onSubviewPlacedCount should be 0");
// But it should be placed in the urlbar.
Assert.equal(onActionPlacedInUrlbarCallCount, 1,
"onActionPlacedInUrlbarCallCount should be 0");
// Open the panel, which should place the action in it.
await promiseOpenPageActionPanel();
Assert.equal(onActionPlacedInPanelCallCount, 1,
"onActionPlacedInPanelCallCount should be inc'ed");
Assert.equal(onActionPlacedInUrlbarCallCount, 1,
"onActionPlacedInUrlbarCallCount should be inc'ed");
Assert.equal(onSubviewPlacedCount, 1,
"onSubviewPlacedCount should be inc'ed");
Assert.equal(onSubviewShowingCount, 0,
@ -372,9 +409,6 @@ add_task(async function withSubview() {
// have been created.
let panelButtonNode = document.getElementById(panelButtonID);
Assert.notEqual(panelButtonNode, null, "panelButtonNode");
let panelViewButtonNodePanel =
document.getElementById(panelViewButtonIDPanel);
Assert.notEqual(panelViewButtonNodePanel, null, "panelViewButtonNodePanel");
// The action's urlbar button should have been created.
let urlbarButtonNode = document.getElementById(urlbarButtonID);
@ -388,36 +422,20 @@ add_task(async function withSubview() {
"Next node should be the bookmark star"
);
// Open the panel, click the action's button, click the subview's first
// button.
await promisePageActionPanelOpen();
// Click the action's button in the panel. The subview should be shown.
Assert.equal(onSubviewShowingCount, 0,
"onSubviewShowingCount should remain 0");
let subviewShownPromise = promisePageActionViewShown();
onSubviewShowingExpectedPanelViewID = panelViewIDPanel;
// synthesizeMouse often cannot seem to click the right node when used on
// buttons that show subviews and buttons inside subviews. That's why we're
// using node.click() twice here: the first time to show the subview, the
// second time to click a button in the subview.
// EventUtils.synthesizeMouseAtCenter(panelButtonNode, {});
panelButtonNode.click();
await subviewShownPromise;
Assert.equal(onActionCommandCallCount, 0,
"onActionCommandCallCount should remain 0");
Assert.equal(onSubviewShowingCount, 1,
"onSubviewShowingCount should be inc'ed");
onButtonCommandExpectedButtonID = panelViewButtonIDPanel;
// EventUtils.synthesizeMouseAtCenter(panelViewButtonNodePanel, {});
panelViewButtonNodePanel.click();
// Click the main button to hide the main panel.
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.equal(onActionCommandCallCount, 0,
"onActionCommandCallCount should remain 0");
Assert.equal(onButtonCommandCallCount, 1,
"onButtonCommandCallCount should be inc'ed");
// Click the action's urlbar button, which should open the activated-action
// panel showing the subview, and click the subview's first button.
// panel showing the subview.
onSubviewPlacedExpectedPanelViewID = panelViewIDUrlbar;
onSubviewShowingExpectedPanelViewID = panelViewIDUrlbar;
EventUtils.synthesizeMouseAtCenter(urlbarButtonNode, {});
@ -426,14 +444,10 @@ add_task(async function withSubview() {
"onSubviewPlacedCount should be inc'ed");
Assert.equal(onSubviewShowingCount, 2,
"onSubviewShowingCount should be inc'ed");
let panelViewButtonNodeUrlbar =
document.getElementById(panelViewButtonIDUrlbar);
Assert.notEqual(panelViewButtonNodeUrlbar, null, "panelViewButtonNodeUrlbar");
onButtonCommandExpectedButtonID = panelViewButtonIDUrlbar;
EventUtils.synthesizeMouseAtCenter(panelViewButtonNodeUrlbar, {});
// Click the urlbar button again. The activated-action panel should close.
EventUtils.synthesizeMouseAtCenter(urlbarButtonNode, {});
assertActivatedPageActionPanelHidden();
Assert.equal(onButtonCommandCallCount, 2,
"onButtonCommandCallCount should be inc'ed");
// Remove the action.
action.remove();
@ -492,6 +506,10 @@ add_task(async function withIframe() {
Assert.equal(action.id, id, "id");
Assert.equal(action.wantsIframe, true, "wantsIframe");
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.equal(onPlacedInPanelCallCount, 1,
"onPlacedInPanelCallCount should be inc'ed");
Assert.equal(onPlacedInUrlbarCallCount, 1,
@ -518,11 +536,11 @@ add_task(async function withIframe() {
);
// Open the panel, click the action's button.
await promisePageActionPanelOpen();
await promiseOpenPageActionPanel();
Assert.equal(onIframeShowingCount, 0, "onIframeShowingCount should remain 0");
EventUtils.synthesizeMouseAtCenter(panelButtonNode, {});
await promisePanelShown(BrowserPageActions._activatedActionPanelID);
Assert.equal(onCommandCallCount, 0, "onCommandCallCount should remain 0");
Assert.equal(onCommandCallCount, 1, "onCommandCallCount should be inc'ed");
Assert.equal(onIframeShowingCount, 1, "onIframeShowingCount should be inc'ed");
// The activated-action panel should have opened, anchored to the action's
@ -537,7 +555,7 @@ add_task(async function withIframe() {
// Click the action's urlbar button.
EventUtils.synthesizeMouseAtCenter(urlbarButtonNode, {});
await promisePanelShown(BrowserPageActions._activatedActionPanelID);
Assert.equal(onCommandCallCount, 0, "onCommandCallCount should remain 0");
Assert.equal(onCommandCallCount, 2, "onCommandCallCount should be inc'ed");
Assert.equal(onIframeShowingCount, 2, "onIframeShowingCount should be inc'ed");
// The activated-action panel should have opened, again anchored to the
@ -554,10 +572,10 @@ add_task(async function withIframe() {
Assert.equal(urlbarButtonNode, null, "urlbarButtonNode");
// Open the panel, click the action's button.
await promisePageActionPanelOpen();
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(panelButtonNode, {});
await promisePanelShown(BrowserPageActions._activatedActionPanelID);
Assert.equal(onCommandCallCount, 0, "onCommandCallCount should remain 0");
Assert.equal(onCommandCallCount, 3, "onCommandCallCount should be inc'ed");
Assert.equal(onIframeShowingCount, 3, "onIframeShowingCount should be inc'ed");
// The activated-action panel should have opened, this time anchored to the
@ -583,10 +601,10 @@ add_task(async function insertBeforeActionID() {
let id = "test-insertBeforeActionID";
let panelButtonID = BrowserPageActions.panelButtonNodeIDForActionID(id);
let initialActions = PageActions.actions;
let initialBuiltInActions = PageActions.builtInActions;
let initialNonBuiltInActions = PageActions.nonBuiltInActions;
let initialBookmarkSeparatorIndex = PageActions.actions.findIndex(a => {
let initialActions = PageActions.actionsInPanel(window);
let initialBuiltInActions = PageActions._builtInActions.slice();
let initialNonBuiltInActions = PageActions._nonBuiltInActions.slice();
let initialBookmarkSeparatorIndex = initialActions.findIndex(a => {
return a.id == PageActions.ACTION_ID_BOOKMARK_SEPARATOR;
});
@ -602,20 +620,25 @@ add_task(async function insertBeforeActionID() {
PageActions.ACTION_ID_BOOKMARK_SEPARATOR,
"action.__insertBeforeActionID");
Assert.equal(PageActions.actions.length,
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
let newActions = PageActions.actionsInPanel(window);
Assert.equal(newActions.length,
initialActions.length + 1,
"PageActions.actions.length should be updated");
Assert.equal(PageActions.builtInActions.length,
Assert.equal(PageActions._builtInActions.length,
initialBuiltInActions.length + 1,
"PageActions.builtInActions.length should be updated");
Assert.equal(PageActions.nonBuiltInActions.length,
"PageActions._builtInActions.length should be updated");
Assert.equal(PageActions._nonBuiltInActions.length,
initialNonBuiltInActions.length,
"PageActions.nonBuiltInActions.length should be updated");
"PageActions._nonBuiltInActions.length should remain the same");
let actionIndex = PageActions.actions.findIndex(a => a.id == id);
let actionIndex = newActions.findIndex(a => a.id == id);
Assert.equal(initialBookmarkSeparatorIndex, actionIndex,
"initialBookmarkSeparatorIndex");
let newBookmarkSeparatorIndex = PageActions.actions.findIndex(a => {
let newBookmarkSeparatorIndex = newActions.findIndex(a => {
return a.id == PageActions.ACTION_ID_BOOKMARK_SEPARATOR;
});
Assert.equal(newBookmarkSeparatorIndex, initialBookmarkSeparatorIndex + 1,
@ -652,14 +675,15 @@ add_task(async function insertBeforeActionID() {
});
// Tests that the ordering of multiple non-built-in actions is alphabetical.
// Tests that the ordering in the panel of multiple non-built-in actions is
// alphabetical.
add_task(async function multipleNonBuiltInOrdering() {
let idPrefix = "test-multipleNonBuiltInOrdering-";
let titlePrefix = "Test multipleNonBuiltInOrdering ";
let initialActions = PageActions.actions;
let initialBuiltInActions = PageActions.builtInActions;
let initialNonBuiltInActions = PageActions.nonBuiltInActions;
let initialActions = PageActions.actionsInPanel(window);
let initialBuiltInActions = PageActions._builtInActions.slice();
let initialNonBuiltInActions = PageActions._nonBuiltInActions.slice();
// Create some actions in an out-of-order order.
let actions = [2, 1, 4, 3].map(index => {
@ -670,26 +694,30 @@ add_task(async function multipleNonBuiltInOrdering() {
});
// + 1 for the separator between built-in and non-built-in actions.
Assert.equal(PageActions.actions.length,
Assert.equal(PageActions.actionsInPanel(window).length,
initialActions.length + actions.length + 1,
"PageActions.actions.length should be updated");
"PageActions.actionsInPanel().length should be updated");
Assert.equal(PageActions.builtInActions.length,
Assert.equal(PageActions._builtInActions.length,
initialBuiltInActions.length,
"PageActions.builtInActions.length should be same");
Assert.equal(PageActions.nonBuiltInActions.length,
"PageActions._builtInActions.length should be same");
Assert.equal(PageActions._nonBuiltInActions.length,
initialNonBuiltInActions.length + actions.length,
"PageActions.nonBuiltInActions.length should be updated");
"PageActions._nonBuiltInActions.length should be updated");
// Look at the final actions.length actions in PageActions.actions, from first
// to last.
for (let i = 0; i < actions.length; i++) {
let expectedIndex = i + 1;
let actualAction = PageActions.nonBuiltInActions[i];
let actualAction = PageActions._nonBuiltInActions[i];
Assert.equal(actualAction.id, idPrefix + expectedIndex,
"actualAction.id for index: " + i);
}
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
// Check the button nodes in the panel.
let expectedIndex = 1;
let buttonNode = document.getElementById(
@ -739,6 +767,7 @@ add_task(async function multipleNonBuiltInOrdering() {
// and added back.
add_task(async function nonBuiltFirst() {
let initialActions = PageActions.actions;
let initialActionsInPanel = PageActions.actionsInPanel(window);
// Remove all actions.
for (let action of initialActions) {
@ -748,10 +777,10 @@ add_task(async function nonBuiltFirst() {
// Check the actions.
Assert.deepEqual(PageActions.actions.map(a => a.id), [],
"PageActions.actions should be empty");
Assert.deepEqual(PageActions.builtInActions.map(a => a.id), [],
"PageActions.builtInActions should be empty");
Assert.deepEqual(PageActions.nonBuiltInActions.map(a => a.id), [],
"PageActions.nonBuiltInActions should be empty");
Assert.deepEqual(PageActions._builtInActions.map(a => a.id), [],
"PageActions._builtInActions should be empty");
Assert.deepEqual(PageActions._nonBuiltInActions.map(a => a.id), [],
"PageActions._nonBuiltInActions should be empty");
// Check the panel.
Assert.equal(BrowserPageActions.mainViewBodyNode.childNodes.length, 0,
@ -766,12 +795,15 @@ add_task(async function nonBuiltFirst() {
// Check the actions.
Assert.deepEqual(PageActions.actions.map(a => a.id), [action.id],
"Action should be in PageActions.actions");
Assert.deepEqual(PageActions.builtInActions.map(a => a.id), [],
"PageActions.builtInActions should be empty");
Assert.deepEqual(PageActions.nonBuiltInActions.map(a => a.id), [action.id],
"Action should be in PageActions.nonBuiltInActions");
Assert.deepEqual(PageActions._builtInActions.map(a => a.id), [],
"PageActions._builtInActions should be empty");
Assert.deepEqual(PageActions._nonBuiltInActions.map(a => a.id), [action.id],
"Action should be in PageActions._nonBuiltInActions");
// Check the panel.
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.deepEqual(
Array.map(BrowserPageActions.mainViewBodyNode.childNodes, n => n.id),
[BrowserPageActions.panelButtonNodeIDForActionID(action.id)],
@ -785,28 +817,38 @@ add_task(async function nonBuiltFirst() {
// Check the actions.
Assert.deepEqual(
PageActions.actions.map(a => a.id),
initialActions.map(a => a.id).concat(
[PageActions.ACTION_ID_BUILT_IN_SEPARATOR],
new Set(PageActions.actions.map(a => a.id)),
new Set(initialActions.map(a => a.id).concat(
[action.id]
),
)),
"All actions should be in PageActions.actions"
);
Assert.deepEqual(
PageActions.builtInActions.map(a => a.id),
initialActions.map(a => a.id),
"PageActions.builtInActions should be initial actions"
PageActions._builtInActions.map(a => a.id),
initialActions.filter(a => !a.__transient).map(a => a.id),
"PageActions._builtInActions should be initial actions"
);
Assert.deepEqual(
PageActions.nonBuiltInActions.map(a => a.id),
PageActions._nonBuiltInActions.map(a => a.id),
[action.id],
"PageActions.nonBuiltInActions should contain action"
"PageActions._nonBuiltInActions should contain action"
);
// Check the panel.
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.deepEqual(
PageActions.actionsInPanel(window).map(a => a.id),
initialActionsInPanel.map(a => a.id).concat(
[PageActions.ACTION_ID_BUILT_IN_SEPARATOR],
[action.id]
),
"All actions should be in PageActions.actionsInPanel()"
);
Assert.deepEqual(
Array.map(BrowserPageActions.mainViewBodyNode.childNodes, n => n.id),
initialActions.map(a => a.id).concat(
initialActionsInPanel.map(a => a.id).concat(
[PageActions.ACTION_ID_BUILT_IN_SEPARATOR],
[action.id]
).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
@ -823,20 +865,28 @@ add_task(async function nonBuiltFirst() {
"Action should no longer be in PageActions.actions"
);
Assert.deepEqual(
PageActions.builtInActions.map(a => a.id),
initialActions.map(a => a.id),
"PageActions.builtInActions should be initial actions"
PageActions._builtInActions.map(a => a.id),
initialActions.filter(a => !a.__transient).map(a => a.id),
"PageActions._builtInActions should be initial actions"
);
Assert.deepEqual(
PageActions.nonBuiltInActions.map(a => a.id),
PageActions._nonBuiltInActions.map(a => a.id),
[],
"Action should no longer be in PageActions.nonBuiltInActions"
"Action should no longer be in PageActions._nonBuiltInActions"
);
// Check the panel.
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.deepEqual(
PageActions.actionsInPanel(window).map(a => a.id),
initialActionsInPanel.map(a => a.id),
"Action should no longer be in PageActions.actionsInPanel()"
);
Assert.deepEqual(
Array.map(BrowserPageActions.mainViewBodyNode.childNodes, n => n.id),
initialActions.map(a => BrowserPageActions.panelButtonNodeIDForActionID(a.id)),
initialActionsInPanel.map(a => BrowserPageActions.panelButtonNodeIDForActionID(a.id)),
"Action should no longer be in panel"
);
});
@ -1040,6 +1090,7 @@ add_task(async function perWindowState() {
let panelButtonID =
BrowserPageActions.panelButtonNodeIDForActionID(action.id);
for (let win of [window, newWindow]) {
win.BrowserPageActions.placeLazyActionsInPanel();
let panelButtonNode = win.document.getElementById(panelButtonID);
Assert.equal(panelButtonNode.getAttribute("label"), newGlobalTitle,
"Panel button label should be global title");
@ -1233,7 +1284,7 @@ add_task(async function contextMenu() {
}));
// Open the panel and then open the context menu on the action's item.
await promisePageActionPanelOpen();
await promiseOpenPageActionPanel();
let panelButton = BrowserPageActions.panelButtonNodeForActionID(action.id);
let contextMenuPromise = promisePanelShown("pageActionContextMenu");
EventUtils.synthesizeMouseAtCenter(panelButton, {
@ -1349,7 +1400,7 @@ add_task(async function contextMenu() {
}, "Waiting for urlbar button to be removed");
// Open the panel and then open the context menu on the action's item.
await promisePageActionPanelOpen();
await promiseOpenPageActionPanel();
contextMenuPromise = promisePanelShown("pageActionContextMenu");
EventUtils.synthesizeMouseAtCenter(panelButton, {
type: "contextmenu",
@ -1416,11 +1467,216 @@ add_task(async function contextMenu() {
});
// Tests transient actions.
add_task(async function transient() {
let initialActionsInPanel = PageActions.actionsInPanel(window);
let onPlacedInPanelCount = 0;
let onBeforePlacedInWindowCount = 0;
let action = PageActions.addAction(new PageActions.Action({
id: "test-transient",
title: "Test transient",
_transient: true,
onPlacedInPanel(buttonNode) {
onPlacedInPanelCount++;
},
onBeforePlacedInWindow(win) {
onBeforePlacedInWindowCount++;
},
}));
Assert.equal(action.__transient, true, "__transient");
Assert.equal(onPlacedInPanelCount, 0,
"onPlacedInPanelCount should remain 0");
Assert.equal(onBeforePlacedInWindowCount, 0,
"onBeforePlacedInWindowCount should remain 0");
Assert.deepEqual(
PageActions.actionsInPanel(window).map(a => a.id),
initialActionsInPanel.map(a => a.id).concat([
PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
action.id,
]),
"PageActions.actionsInPanel() should be updated"
);
// Check the panel.
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.deepEqual(
Array.map(BrowserPageActions.mainViewBodyNode.childNodes, n => n.id),
initialActionsInPanel.map(a => a.id).concat([
PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
action.id,
]).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
"Actions in panel should be correct"
);
Assert.equal(onPlacedInPanelCount, 1,
"onPlacedInPanelCount should be inc'ed");
Assert.equal(onBeforePlacedInWindowCount, 1,
"onBeforePlacedInWindowCount should be inc'ed");
// Disable the action. It should be removed from the panel.
action.setDisabled(true, window);
Assert.deepEqual(
PageActions.actionsInPanel(window).map(a => a.id),
initialActionsInPanel.map(a => a.id),
"PageActions.actionsInPanel() should revert to initial"
);
// Check the panel.
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.deepEqual(
Array.map(BrowserPageActions.mainViewBodyNode.childNodes, n => n.id),
initialActionsInPanel
.map(a => BrowserPageActions.panelButtonNodeIDForActionID(a.id)),
"Actions in panel should be correct"
);
// Enable the action. It should be added back to the panel.
action.setDisabled(false, window);
Assert.deepEqual(
PageActions.actionsInPanel(window).map(a => a.id),
initialActionsInPanel.map(a => a.id).concat([
PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
action.id,
]),
"PageActions.actionsInPanel() should be updated"
);
// Check the panel.
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.deepEqual(
Array.map(BrowserPageActions.mainViewBodyNode.childNodes, n => n.id),
initialActionsInPanel.map(a => a.id).concat([
PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
action.id,
]).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
"Actions in panel should be correct"
);
Assert.equal(onPlacedInPanelCount, 2,
"onPlacedInPanelCount should be inc'ed");
Assert.equal(onBeforePlacedInWindowCount, 2,
"onBeforePlacedInWindowCount should be inc'ed");
// Add another non-built in but non-transient action.
let otherAction = PageActions.addAction(new PageActions.Action({
id: "test-transient2",
title: "Test transient 2",
}));
Assert.deepEqual(
PageActions.actionsInPanel(window).map(a => a.id),
initialActionsInPanel.map(a => a.id).concat([
PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
otherAction.id,
PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
action.id,
]),
"PageActions.actionsInPanel() should be updated"
);
// Check the panel.
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.deepEqual(
Array.map(BrowserPageActions.mainViewBodyNode.childNodes, n => n.id),
initialActionsInPanel.map(a => a.id).concat([
PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
otherAction.id,
PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
action.id,
]).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
"Actions in panel should be correct"
);
Assert.equal(onPlacedInPanelCount, 2,
"onPlacedInPanelCount should remain the same");
Assert.equal(onBeforePlacedInWindowCount, 2,
"onBeforePlacedInWindowCount should remain the same");
// Disable the action again. It should be removed from the panel.
action.setDisabled(true, window);
Assert.deepEqual(
PageActions.actionsInPanel(window).map(a => a.id),
initialActionsInPanel.map(a => a.id).concat([
PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
otherAction.id,
]),
"PageActions.actionsInPanel() should be updated"
);
// Check the panel.
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.deepEqual(
Array.map(BrowserPageActions.mainViewBodyNode.childNodes, n => n.id),
initialActionsInPanel.map(a => a.id).concat([
PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
otherAction.id,
]).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
"Actions in panel should be correct"
);
// Enable the action again. It should be added back to the panel.
action.setDisabled(false, window);
Assert.deepEqual(
PageActions.actionsInPanel(window).map(a => a.id),
initialActionsInPanel.map(a => a.id).concat([
PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
otherAction.id,
PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
action.id,
]),
"PageActions.actionsInPanel() should be updated"
);
// Check the panel.
await promiseOpenPageActionPanel();
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
await promisePageActionPanelHidden();
Assert.deepEqual(
Array.map(BrowserPageActions.mainViewBodyNode.childNodes, n => n.id),
initialActionsInPanel.map(a => a.id).concat([
PageActions.ACTION_ID_BUILT_IN_SEPARATOR,
otherAction.id,
PageActions.ACTION_ID_TRANSIENT_SEPARATOR,
action.id,
]).map(id => BrowserPageActions.panelButtonNodeIDForActionID(id)),
"Actions in panel should be correct"
);
Assert.equal(onPlacedInPanelCount, 3,
"onPlacedInPanelCount should be inc'ed");
Assert.equal(onBeforePlacedInWindowCount, 3,
"onBeforePlacedInWindowCount should be inc'ed");
// Done, clean up.
action.remove();
otherAction.remove();
});
function assertActivatedPageActionPanelHidden() {
Assert.ok(!document.getElementById(BrowserPageActions._activatedActionPanelID));
}
function promisePageActionPanelOpen() {
function promiseOpenPageActionPanel() {
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return BrowserTestUtils.waitForCondition(() => {

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

@ -115,9 +115,9 @@
}
.searchbar-engine-one-off-item[selected] {
background-color: Highlight;
background-color: var(--autocomplete-popup-highlight-background);
background-image: none;
color: HighlightText;
color: var(--autocomplete-popup-highlight-color);
}
.searchbar-engine-one-off-item > .button-box {
@ -152,8 +152,8 @@
}
.addengine-item[selected] {
background-color: Highlight;
color: HighlightText;
background-color: var(--autocomplete-popup-highlight-background);
color: var(--autocomplete-popup-highlight-color);
}
.addengine-item[type=menu][selected] {

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

@ -163,6 +163,23 @@
list-style-image: url("chrome://browser/skin/sync.svg");
}
#pageAction-panel-addSearchEngine > .toolbarbutton-badge-stack > .toolbarbutton-icon {
width: 16px;
height: 16px;
}
#pageAction-panel-addSearchEngine > .toolbarbutton-badge-stack > .toolbarbutton-badge {
display: -moz-box;
background: url(chrome://browser/skin/search-indicator-badge-add.svg) no-repeat center;
box-shadow: none;
/* "!important" is necessary to override the rule in toolbarbutton.css */
margin: -4px 0 0 !important;
margin-inline-end: -4px !important;
width: 11px;
height: 11px;
min-width: 11px;
min-height: 11px;
}
/* URL bar and page action buttons */
#page-action-buttons {

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

@ -55,14 +55,11 @@ void
HTMLBRElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Display))) {
if (!aData->PropertyIsSet(eCSSProperty_clear)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::clear);
if (value && value->Type() == nsAttrValue::eEnum)
aData->SetKeywordValue(eCSSProperty_clear, value->GetEnumValue());
}
if (!aData->PropertyIsSet(eCSSProperty_clear)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::clear);
if (value && value->Type() == nsAttrValue::eEnum)
aData->SetKeywordValue(eCSSProperty_clear, value->GetEnumValue());
}
nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);
}

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

@ -76,153 +76,145 @@ void
HTMLBodyElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Margin)) {
// This is the one place where we try to set the same property
// multiple times in presentation attributes. Servo does not support
// querying if a property is set (because that is O(n) behavior
// in ServoSpecifiedValues). Instead, we use the below values to keep
// track of whether we have already set a property, and if so, what value
// we set it to (which is used when handling margin
// attributes from the containing frame element)
// This is the one place where we try to set the same property
// multiple times in presentation attributes. Servo does not support
// querying if a property is set (because that is O(n) behavior
// in ServoSpecifiedValues). Instead, we use the below values to keep
// track of whether we have already set a property, and if so, what value
// we set it to (which is used when handling margin
// attributes from the containing frame element)
int32_t bodyMarginWidth = -1;
int32_t bodyMarginHeight = -1;
int32_t bodyTopMargin = -1;
int32_t bodyBottomMargin = -1;
int32_t bodyLeftMargin = -1;
int32_t bodyRightMargin = -1;
int32_t bodyMarginWidth = -1;
int32_t bodyMarginHeight = -1;
int32_t bodyTopMargin = -1;
int32_t bodyBottomMargin = -1;
int32_t bodyLeftMargin = -1;
int32_t bodyRightMargin = -1;
const nsAttrValue* value;
// if marginwidth/marginheight are set, reflect them as 'margin'
value = aAttributes->GetAttr(nsGkAtoms::marginwidth);
if (value && value->Type() == nsAttrValue::eInteger) {
bodyMarginWidth = value->GetIntegerValue();
if (bodyMarginWidth < 0) {
bodyMarginWidth = 0;
}
aData->SetPixelValueIfUnset(eCSSProperty_margin_left, (float)bodyMarginWidth);
aData->SetPixelValueIfUnset(eCSSProperty_margin_right, (float)bodyMarginWidth);
}
const nsAttrValue* value;
// if marginwidth/marginheight are set, reflect them as 'margin'
value = aAttributes->GetAttr(nsGkAtoms::marginwidth);
value = aAttributes->GetAttr(nsGkAtoms::marginheight);
if (value && value->Type() == nsAttrValue::eInteger) {
bodyMarginHeight = value->GetIntegerValue();
if (bodyMarginHeight < 0) {
bodyMarginHeight = 0;
}
aData->SetPixelValueIfUnset(eCSSProperty_margin_top, (float)bodyMarginHeight);
aData->SetPixelValueIfUnset(eCSSProperty_margin_bottom, (float)bodyMarginHeight);
}
// topmargin (IE-attribute)
if (bodyMarginHeight == -1) {
value = aAttributes->GetAttr(nsGkAtoms::topmargin);
if (value && value->Type() == nsAttrValue::eInteger) {
bodyMarginWidth = value->GetIntegerValue();
if (bodyMarginWidth < 0) {
bodyMarginWidth = 0;
bodyTopMargin = value->GetIntegerValue();
if (bodyTopMargin < 0) {
bodyTopMargin = 0;
}
aData->SetPixelValueIfUnset(eCSSProperty_margin_left, (float)bodyMarginWidth);
aData->SetPixelValueIfUnset(eCSSProperty_margin_right, (float)bodyMarginWidth);
aData->SetPixelValueIfUnset(eCSSProperty_margin_top, (float)bodyTopMargin);
}
}
// bottommargin (IE-attribute)
value = aAttributes->GetAttr(nsGkAtoms::marginheight);
if (bodyMarginHeight == -1) {
value = aAttributes->GetAttr(nsGkAtoms::bottommargin);
if (value && value->Type() == nsAttrValue::eInteger) {
bodyMarginHeight = value->GetIntegerValue();
if (bodyMarginHeight < 0) {
bodyMarginHeight = 0;
bodyBottomMargin = value->GetIntegerValue();
if (bodyBottomMargin < 0) {
bodyBottomMargin = 0;
}
aData->SetPixelValueIfUnset(eCSSProperty_margin_top, (float)bodyMarginHeight);
aData->SetPixelValueIfUnset(eCSSProperty_margin_bottom, (float)bodyMarginHeight);
aData->SetPixelValueIfUnset(eCSSProperty_margin_bottom, (float)bodyBottomMargin);
}
}
// topmargin (IE-attribute)
if (bodyMarginHeight == -1) {
value = aAttributes->GetAttr(nsGkAtoms::topmargin);
if (value && value->Type() == nsAttrValue::eInteger) {
bodyTopMargin = value->GetIntegerValue();
if (bodyTopMargin < 0) {
bodyTopMargin = 0;
}
aData->SetPixelValueIfUnset(eCSSProperty_margin_top, (float)bodyTopMargin);
// leftmargin (IE-attribute)
if (bodyMarginWidth == -1) {
value = aAttributes->GetAttr(nsGkAtoms::leftmargin);
if (value && value->Type() == nsAttrValue::eInteger) {
bodyLeftMargin = value->GetIntegerValue();
if (bodyLeftMargin < 0) {
bodyLeftMargin = 0;
}
aData->SetPixelValueIfUnset(eCSSProperty_margin_left, (float)bodyLeftMargin);
}
// bottommargin (IE-attribute)
if (bodyMarginHeight == -1) {
value = aAttributes->GetAttr(nsGkAtoms::bottommargin);
if (value && value->Type() == nsAttrValue::eInteger) {
bodyBottomMargin = value->GetIntegerValue();
if (bodyBottomMargin < 0) {
bodyBottomMargin = 0;
}
aData->SetPixelValueIfUnset(eCSSProperty_margin_bottom, (float)bodyBottomMargin);
}
// rightmargin (IE-attribute)
if (bodyMarginWidth == -1) {
value = aAttributes->GetAttr(nsGkAtoms::rightmargin);
if (value && value->Type() == nsAttrValue::eInteger) {
bodyRightMargin = value->GetIntegerValue();
if (bodyRightMargin < 0) {
bodyRightMargin = 0;
}
aData->SetPixelValueIfUnset(eCSSProperty_margin_right, (float)bodyRightMargin);
}
}
// leftmargin (IE-attribute)
if (bodyMarginWidth == -1) {
value = aAttributes->GetAttr(nsGkAtoms::leftmargin);
if (value && value->Type() == nsAttrValue::eInteger) {
bodyLeftMargin = value->GetIntegerValue();
if (bodyLeftMargin < 0) {
bodyLeftMargin = 0;
// if marginwidth or marginheight is set in the <frame> and not set in the <body>
// reflect them as margin in the <body>
if (bodyMarginWidth == -1 || bodyMarginHeight == -1) {
nsCOMPtr<nsIDocShell> docShell(aData->Document()->GetDocShell());
if (docShell) {
nscoord frameMarginWidth=-1; // default value
nscoord frameMarginHeight=-1; // default value
docShell->GetMarginWidth(&frameMarginWidth); // -1 indicates not set
docShell->GetMarginHeight(&frameMarginHeight);
if (bodyMarginWidth == -1 && frameMarginWidth >= 0) {
if (bodyLeftMargin == -1) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_left, (float)frameMarginWidth);
}
if (bodyRightMargin == -1) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_right, (float)frameMarginWidth);
}
aData->SetPixelValueIfUnset(eCSSProperty_margin_left, (float)bodyLeftMargin);
}
}
// rightmargin (IE-attribute)
if (bodyMarginWidth == -1) {
value = aAttributes->GetAttr(nsGkAtoms::rightmargin);
if (value && value->Type() == nsAttrValue::eInteger) {
bodyRightMargin = value->GetIntegerValue();
if (bodyRightMargin < 0) {
bodyRightMargin = 0;
if (bodyMarginHeight == -1 && frameMarginHeight >= 0) {
if (bodyTopMargin == -1) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_top, (float)frameMarginHeight);
}
aData->SetPixelValueIfUnset(eCSSProperty_margin_right, (float)bodyRightMargin);
}
}
// if marginwidth or marginheight is set in the <frame> and not set in the <body>
// reflect them as margin in the <body>
if (bodyMarginWidth == -1 || bodyMarginHeight == -1) {
nsCOMPtr<nsIDocShell> docShell(aData->Document()->GetDocShell());
if (docShell) {
nscoord frameMarginWidth=-1; // default value
nscoord frameMarginHeight=-1; // default value
docShell->GetMarginWidth(&frameMarginWidth); // -1 indicates not set
docShell->GetMarginHeight(&frameMarginHeight);
if (bodyMarginWidth == -1 && frameMarginWidth >= 0) {
if (bodyLeftMargin == -1) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_left, (float)frameMarginWidth);
}
if (bodyRightMargin == -1) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_right, (float)frameMarginWidth);
}
}
if (bodyMarginHeight == -1 && frameMarginHeight >= 0) {
if (bodyTopMargin == -1) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_top, (float)frameMarginHeight);
}
if (bodyBottomMargin == -1) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_bottom, (float)frameMarginHeight);
}
if (bodyBottomMargin == -1) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_bottom, (float)frameMarginHeight);
}
}
}
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Display))) {
// When display if first asked for, go ahead and get our colors set up.
if (nsHTMLStyleSheet* styleSheet = aData->Document()->GetAttributeStyleSheet()) {
const nsAttrValue* value;
nscolor color;
value = aAttributes->GetAttr(nsGkAtoms::link);
if (value && value->GetColorValue(color)) {
styleSheet->SetLinkColor(color);
}
// When display if first asked for, go ahead and get our colors set up.
if (nsHTMLStyleSheet* styleSheet = aData->Document()->GetAttributeStyleSheet()) {
nscolor color;
value = aAttributes->GetAttr(nsGkAtoms::link);
if (value && value->GetColorValue(color)) {
styleSheet->SetLinkColor(color);
}
value = aAttributes->GetAttr(nsGkAtoms::alink);
if (value && value->GetColorValue(color)) {
styleSheet->SetActiveLinkColor(color);
}
value = aAttributes->GetAttr(nsGkAtoms::alink);
if (value && value->GetColorValue(color)) {
styleSheet->SetActiveLinkColor(color);
}
value = aAttributes->GetAttr(nsGkAtoms::vlink);
if (value && value->GetColorValue(color)) {
styleSheet->SetVisitedLinkColor(color);
}
value = aAttributes->GetAttr(nsGkAtoms::vlink);
if (value && value->GetColorValue(color)) {
styleSheet->SetVisitedLinkColor(color);
}
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Color))) {
if (!aData->PropertyIsSet(eCSSProperty_color)) {
// color: color
nscolor color;
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::text);
if (value && value->GetColorValue(color)) {
aData->SetColorValue(eCSSProperty_color, color);
}
if (!aData->PropertyIsSet(eCSSProperty_color)) {
// color: color
nscolor color;
value = aAttributes->GetAttr(nsGkAtoms::text);
if (value && value->GetColorValue(color)) {
aData->SetColorValue(eCSSProperty_color, color);
}
}

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

@ -58,34 +58,29 @@ void
HTMLFontElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Font))) {
// face: string list
if (!aData->PropertyIsSet(eCSSProperty_font_family)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::face);
if (value && value->Type() == nsAttrValue::eString &&
!value->IsEmptyString()) {
aData->SetFontFamily(value->GetStringValue());
}
}
// size: int
if (!aData->PropertyIsSet(eCSSProperty_font_size)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::size);
if (value && value->Type() == nsAttrValue::eInteger)
aData->SetKeywordValue(eCSSProperty_font_size, value->GetIntegerValue());
// face: string list
if (!aData->PropertyIsSet(eCSSProperty_font_family)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::face);
if (value && value->Type() == nsAttrValue::eString &&
!value->IsEmptyString()) {
aData->SetFontFamily(value->GetStringValue());
}
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Color))) {
if (!aData->PropertyIsSet(eCSSProperty_color)) {
// color: color
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::color);
nscolor color;
if (value && value->GetColorValue(color)) {
aData->SetColorValue(eCSSProperty_color, color);
}
// size: int
if (!aData->PropertyIsSet(eCSSProperty_font_size)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::size);
if (value && value->Type() == nsAttrValue::eInteger)
aData->SetKeywordValue(eCSSProperty_font_size, value->GetIntegerValue());
}
if (!aData->PropertyIsSet(eCSSProperty_color)) {
// color: color
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::color);
nscolor color;
if (value && value->GetColorValue(color)) {
aData->SetColorValue(eCSSProperty_color, color);
}
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(TextReset)) &&
aData->Document()->GetCompatibilityMode() == eCompatibility_NavQuirks) {
if (aData->Document()->GetCompatibilityMode() == eCompatibility_NavQuirks) {
// Make <a><font color="red">text</font></a> give the text a red underline
// in quirks mode. The NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL flag only
// affects quirks mode rendering.

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

@ -67,62 +67,55 @@ HTMLHRElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nscolor color;
bool colorIsSet = colorValue && colorValue->GetColorValue(color);
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Position) |
NS_STYLE_INHERIT_BIT(Border))) {
if (colorIsSet) {
noshade = true;
} else {
noshade = !!aAttributes->GetAttr(nsGkAtoms::noshade);
}
if (colorIsSet) {
noshade = true;
} else {
noshade = !!aAttributes->GetAttr(nsGkAtoms::noshade);
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Margin))) {
// align: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum) {
// Map align attribute into auto side margins
switch (value->GetEnumValue()) {
case NS_STYLE_TEXT_ALIGN_LEFT:
aData->SetPixelValueIfUnset(eCSSProperty_margin_left, 0.0f);
aData->SetAutoValueIfUnset(eCSSProperty_margin_right);
break;
case NS_STYLE_TEXT_ALIGN_RIGHT:
aData->SetAutoValueIfUnset(eCSSProperty_margin_left);
aData->SetPixelValueIfUnset(eCSSProperty_margin_right, 0.0f);
break;
case NS_STYLE_TEXT_ALIGN_CENTER:
aData->SetAutoValueIfUnset(eCSSProperty_margin_left);
aData->SetAutoValueIfUnset(eCSSProperty_margin_right);
break;
}
// align: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum) {
// Map align attribute into auto side margins
switch (value->GetEnumValue()) {
case NS_STYLE_TEXT_ALIGN_LEFT:
aData->SetPixelValueIfUnset(eCSSProperty_margin_left, 0.0f);
aData->SetAutoValueIfUnset(eCSSProperty_margin_right);
break;
case NS_STYLE_TEXT_ALIGN_RIGHT:
aData->SetAutoValueIfUnset(eCSSProperty_margin_left);
aData->SetPixelValueIfUnset(eCSSProperty_margin_right, 0.0f);
break;
case NS_STYLE_TEXT_ALIGN_CENTER:
aData->SetAutoValueIfUnset(eCSSProperty_margin_left);
aData->SetAutoValueIfUnset(eCSSProperty_margin_right);
break;
}
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Position))) {
if (!aData->PropertyIsSet(eCSSProperty_height)) {
// size: integer
if (noshade) {
// noshade case: size is set using the border
aData->SetAutoValue(eCSSProperty_height);
} else {
// normal case
// the height includes the top and bottom borders that are initially 1px.
// for size=1, html.css has a special case rule that makes this work by
// removing all but the top border.
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::size);
if (value && value->Type() == nsAttrValue::eInteger) {
aData->SetPixelValue(eCSSProperty_height, (float)value->GetIntegerValue());
} // else use default value from html.css
}
if (!aData->PropertyIsSet(eCSSProperty_height)) {
// size: integer
if (noshade) {
// noshade case: size is set using the border
aData->SetAutoValue(eCSSProperty_height);
} else {
// normal case
// the height includes the top and bottom borders that are initially 1px.
// for size=1, html.css has a special case rule that makes this work by
// removing all but the top border.
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::size);
if (value && value->Type() == nsAttrValue::eInteger) {
aData->SetPixelValue(eCSSProperty_height, (float)value->GetIntegerValue());
} // else use default value from html.css
}
}
// if not noshade, border styles are dealt with by html.css
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Border)) && noshade) {
if (noshade) {
// size: integer
// if a size is set, use half of it per side, otherwise, use 1px per side
float sizePerSide;
bool allSides = true;
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::size);
value = aAttributes->GetAttr(nsGkAtoms::size);
if (value && value->Type() == nsAttrValue::eInteger) {
sizePerSide = (float)value->GetIntegerValue() / 2.0f;
if (sizePerSide < 1.0f) {
@ -164,12 +157,10 @@ HTMLHRElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
}
}
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Color))) {
// color: a color
// (we got the color attribute earlier)
if (colorIsSet) {
aData->SetColorValueIfUnset(eCSSProperty_color, color);
}
// color: a color
// (we got the color attribute earlier)
if (colorIsSet) {
aData->SetColorValueIfUnset(eCSSProperty_color, color);
}
nsGenericHTMLElement::MapWidthAttributeInto(aAttributes, aData);

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

@ -84,21 +84,19 @@ void
HTMLIFrameElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Border))) {
// frameborder: 0 | 1 (| NO | YES in quirks mode)
// If frameborder is 0 or No, set border to 0
// else leave it as the value set in html.css
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::frameborder);
if (value && value->Type() == nsAttrValue::eEnum) {
int32_t frameborder = value->GetEnumValue();
if (NS_STYLE_FRAME_0 == frameborder ||
NS_STYLE_FRAME_NO == frameborder ||
NS_STYLE_FRAME_OFF == frameborder) {
aData->SetPixelValueIfUnset(eCSSProperty_border_top_width, 0.0f);
aData->SetPixelValueIfUnset(eCSSProperty_border_right_width, 0.0f);
aData->SetPixelValueIfUnset(eCSSProperty_border_bottom_width, 0.0f);
aData->SetPixelValueIfUnset(eCSSProperty_border_left_width, 0.0f);
}
// frameborder: 0 | 1 (| NO | YES in quirks mode)
// If frameborder is 0 or No, set border to 0
// else leave it as the value set in html.css
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::frameborder);
if (value && value->Type() == nsAttrValue::eEnum) {
int32_t frameborder = value->GetEnumValue();
if (NS_STYLE_FRAME_0 == frameborder ||
NS_STYLE_FRAME_NO == frameborder ||
NS_STYLE_FRAME_OFF == frameborder) {
aData->SetPixelValueIfUnset(eCSSProperty_border_top_width, 0.0f);
aData->SetPixelValueIfUnset(eCSSProperty_border_right_width, 0.0f);
aData->SetPixelValueIfUnset(eCSSProperty_border_bottom_width, 0.0f);
aData->SetPixelValueIfUnset(eCSSProperty_border_left_width, 0.0f);
}
}

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

@ -69,13 +69,11 @@ void
HTMLLIElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(List))) {
if (!aData->PropertyIsSet(eCSSProperty_list_style_type)) {
// type: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::type);
if (value && value->Type() == nsAttrValue::eEnum)
aData->SetKeywordValue(eCSSProperty_list_style_type, value->GetEnumValue());
}
if (!aData->PropertyIsSet(eCSSProperty_list_style_type)) {
// type: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::type);
if (value && value->Type() == nsAttrValue::eEnum)
aData->SetKeywordValue(eCSSProperty_list_style_type, value->GetEnumValue());
}
nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);

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

@ -45,12 +45,10 @@ void
HTMLPreElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Text))) {
if (!aData->PropertyIsSet(eCSSProperty_white_space)) {
// wrap: empty
if (aAttributes->GetAttr(nsGkAtoms::wrap))
aData->SetKeywordValue(eCSSProperty_white_space, StyleWhiteSpace::PreWrap);
}
if (!aData->PropertyIsSet(eCSSProperty_white_space)) {
// wrap: empty
if (aAttributes->GetAttr(nsGkAtoms::wrap))
aData->SetKeywordValue(eCSSProperty_white_space, StyleWhiteSpace::PreWrap);
}
nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);

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

@ -81,16 +81,14 @@ static void
DirectoryMapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(List))) {
if (!aData->PropertyIsSet(eCSSProperty_list_style_type)) {
// type: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::type);
if (value) {
if (value->Type() == nsAttrValue::eEnum) {
aData->SetKeywordValue(eCSSProperty_list_style_type, value->GetEnumValue());
} else {
aData->SetKeywordValue(eCSSProperty_list_style_type, NS_STYLE_LIST_STYLE_DISC);
}
if (!aData->PropertyIsSet(eCSSProperty_list_style_type)) {
// type: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::type);
if (value) {
if (value->Type() == nsAttrValue::eEnum) {
aData->SetKeywordValue(eCSSProperty_list_style_type, value->GetEnumValue());
} else {
aData->SetKeywordValue(eCSSProperty_list_style_type, NS_STYLE_LIST_STYLE_DISC);
}
}
}

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

@ -83,13 +83,11 @@ void
HTMLSharedListElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(List))) {
if (!aData->PropertyIsSet(eCSSProperty_list_style_type)) {
// type: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::type);
if (value && value->Type() == nsAttrValue::eEnum) {
aData->SetKeywordValue(eCSSProperty_list_style_type, value->GetEnumValue());
}
if (!aData->PropertyIsSet(eCSSProperty_list_style_type)) {
// type: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::type);
if (value && value->Type() == nsAttrValue::eEnum) {
aData->SetKeywordValue(eCSSProperty_list_style_type, value->GetEnumValue());
}
}

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

@ -55,12 +55,10 @@ void
HTMLTableCaptionElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(TableBorder))) {
if (!aData->PropertyIsSet(eCSSProperty_caption_side)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum)
aData->SetKeywordValue(eCSSProperty_caption_side, value->GetEnumValue());
}
if (!aData->PropertyIsSet(eCSSProperty_caption_side)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum)
aData->SetKeywordValue(eCSSProperty_caption_side, value->GetEnumValue());
}
nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);

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

@ -185,48 +185,44 @@ void
HTMLTableCellElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Position))) {
// width: value
if (!aData->PropertyIsSet(eCSSProperty_width)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
if (value && value->Type() == nsAttrValue::eInteger) {
if (value->GetIntegerValue() > 0)
aData->SetPixelValue(eCSSProperty_width, (float)value->GetIntegerValue());
// else 0 implies auto for compatibility.
}
else if (value && value->Type() == nsAttrValue::ePercent) {
if (value->GetPercentValue() > 0.0f)
aData->SetPercentValue(eCSSProperty_width, value->GetPercentValue());
// else 0 implies auto for compatibility
}
// width: value
if (!aData->PropertyIsSet(eCSSProperty_width)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
if (value && value->Type() == nsAttrValue::eInteger) {
if (value->GetIntegerValue() > 0)
aData->SetPixelValue(eCSSProperty_width, (float)value->GetIntegerValue());
// else 0 implies auto for compatibility.
}
// height: value
if (!aData->PropertyIsSet(eCSSProperty_height)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height);
if (value && value->Type() == nsAttrValue::eInteger) {
if (value->GetIntegerValue() > 0)
aData->SetPixelValue(eCSSProperty_height, (float)value->GetIntegerValue());
// else 0 implies auto for compatibility.
}
else if (value && value->Type() == nsAttrValue::ePercent) {
if (value->GetPercentValue() > 0.0f)
aData->SetPercentValue(eCSSProperty_height, value->GetPercentValue());
// else 0 implies auto for compatibility
}
else if (value && value->Type() == nsAttrValue::ePercent) {
if (value->GetPercentValue() > 0.0f)
aData->SetPercentValue(eCSSProperty_width, value->GetPercentValue());
// else 0 implies auto for compatibility
}
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Text))) {
if (!aData->PropertyIsSet(eCSSProperty_white_space)) {
// nowrap: enum
if (aAttributes->GetAttr(nsGkAtoms::nowrap)) {
// See if our width is not a nonzero integer width.
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
nsCompatibility mode = aData->Document()->GetCompatibilityMode();
if (!value || value->Type() != nsAttrValue::eInteger ||
value->GetIntegerValue() == 0 ||
eCompatibility_NavQuirks != mode) {
aData->SetKeywordValue(eCSSProperty_white_space, StyleWhiteSpace::Nowrap);
}
// height: value
if (!aData->PropertyIsSet(eCSSProperty_height)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height);
if (value && value->Type() == nsAttrValue::eInteger) {
if (value->GetIntegerValue() > 0)
aData->SetPixelValue(eCSSProperty_height, (float)value->GetIntegerValue());
// else 0 implies auto for compatibility.
}
else if (value && value->Type() == nsAttrValue::ePercent) {
if (value->GetPercentValue() > 0.0f)
aData->SetPercentValue(eCSSProperty_height, value->GetPercentValue());
// else 0 implies auto for compatibility
}
}
if (!aData->PropertyIsSet(eCSSProperty_white_space)) {
// nowrap: enum
if (aAttributes->GetAttr(nsGkAtoms::nowrap)) {
// See if our width is not a nonzero integer width.
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
nsCompatibility mode = aData->Document()->GetCompatibilityMode();
if (!value || value->Type() != nsAttrValue::eInteger ||
value->GetIntegerValue() == 0 ||
eCompatibility_NavQuirks != mode) {
aData->SetKeywordValue(eCSSProperty_white_space, StyleWhiteSpace::Nowrap);
}
}
}

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

@ -67,18 +67,16 @@ void
HTMLTableColElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Table))) {
if (!aData->PropertyIsSet(eCSSProperty__x_span)) {
// span: int
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::span);
if (value && value->Type() == nsAttrValue::eInteger) {
int32_t val = value->GetIntegerValue();
// Note: Do NOT use this code for table cells! The value "0"
// means something special for colspan and rowspan, but for <col
// span> and <colgroup span> it's just disallowed.
if (val > 0) {
aData->SetIntValue(eCSSProperty__x_span, value->GetIntegerValue());
}
if (!aData->PropertyIsSet(eCSSProperty__x_span)) {
// span: int
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::span);
if (value && value->Type() == nsAttrValue::eInteger) {
int32_t val = value->GetIntegerValue();
// Note: Do NOT use this code for table cells! The value "0"
// means something special for colspan and rowspan, but for <col
// span> and <colgroup span> it's just disallowed.
if (val > 0) {
aData->SetIntValue(eCSSProperty__x_span, value->GetIntegerValue());
}
}
}

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

@ -946,73 +946,67 @@ HTMLTableElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsCompatibility mode = aData->Document()->GetCompatibilityMode();
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(TableBorder))) {
// cellspacing
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::cellspacing);
if (value && value->Type() == nsAttrValue::eInteger &&
!aData->PropertyIsSet(eCSSProperty_border_spacing)) {
aData->SetPixelValue(eCSSProperty_border_spacing, float(value->GetIntegerValue()));
// cellspacing
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::cellspacing);
if (value && value->Type() == nsAttrValue::eInteger &&
!aData->PropertyIsSet(eCSSProperty_border_spacing)) {
aData->SetPixelValue(eCSSProperty_border_spacing, float(value->GetIntegerValue()));
}
// align; Check for enumerated type (it may be another type if
// illegal)
value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum) {
if (value->GetEnumValue() == NS_STYLE_TEXT_ALIGN_CENTER ||
value->GetEnumValue() == NS_STYLE_TEXT_ALIGN_MOZ_CENTER) {
aData->SetAutoValueIfUnset(eCSSProperty_margin_left);
aData->SetAutoValueIfUnset(eCSSProperty_margin_right);
}
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Margin))) {
// align; Check for enumerated type (it may be another type if
// illegal)
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum) {
if (value->GetEnumValue() == NS_STYLE_TEXT_ALIGN_CENTER ||
value->GetEnumValue() == NS_STYLE_TEXT_ALIGN_MOZ_CENTER) {
aData->SetAutoValueIfUnset(eCSSProperty_margin_left);
aData->SetAutoValueIfUnset(eCSSProperty_margin_right);
}
// hspace is mapped into left and right margin,
// vspace is mapped into top and bottom margins
// - *** Quirks Mode only ***
if (eCompatibility_NavQuirks == mode) {
value = aAttributes->GetAttr(nsGkAtoms::hspace);
if (value && value->Type() == nsAttrValue::eInteger) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_left, (float)value->GetIntegerValue());
aData->SetPixelValueIfUnset(eCSSProperty_margin_right, (float)value->GetIntegerValue());
}
// hspace is mapped into left and right margin,
// vspace is mapped into top and bottom margins
// - *** Quirks Mode only ***
if (eCompatibility_NavQuirks == mode) {
value = aAttributes->GetAttr(nsGkAtoms::hspace);
value = aAttributes->GetAttr(nsGkAtoms::vspace);
if (value && value->Type() == nsAttrValue::eInteger) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_left, (float)value->GetIntegerValue());
aData->SetPixelValueIfUnset(eCSSProperty_margin_right, (float)value->GetIntegerValue());
}
value = aAttributes->GetAttr(nsGkAtoms::vspace);
if (value && value->Type() == nsAttrValue::eInteger) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_top, (float)value->GetIntegerValue());
aData->SetPixelValueIfUnset(eCSSProperty_margin_bottom, (float)value->GetIntegerValue());
}
if (value && value->Type() == nsAttrValue::eInteger) {
aData->SetPixelValueIfUnset(eCSSProperty_margin_top, (float)value->GetIntegerValue());
aData->SetPixelValueIfUnset(eCSSProperty_margin_bottom, (float)value->GetIntegerValue());
}
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Border))) {
// bordercolor
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::bordercolor);
nscolor color;
if (value && value->GetColorValue(color)) {
aData->SetColorValueIfUnset(eCSSProperty_border_top_color, color);
aData->SetColorValueIfUnset(eCSSProperty_border_left_color, color);
aData->SetColorValueIfUnset(eCSSProperty_border_bottom_color, color);
aData->SetColorValueIfUnset(eCSSProperty_border_right_color, color);
}
// border
const nsAttrValue* borderValue = aAttributes->GetAttr(nsGkAtoms::border);
if (borderValue) {
// border = 1 pixel default
int32_t borderThickness = 1;
if (borderValue->Type() == nsAttrValue::eInteger)
borderThickness = borderValue->GetIntegerValue();
// by default, set all border sides to the specified width
aData->SetPixelValueIfUnset(eCSSProperty_border_top_width, (float)borderThickness);
aData->SetPixelValueIfUnset(eCSSProperty_border_left_width, (float)borderThickness);
aData->SetPixelValueIfUnset(eCSSProperty_border_bottom_width, (float)borderThickness);
aData->SetPixelValueIfUnset(eCSSProperty_border_right_width, (float)borderThickness);
}
// bordercolor
value = aAttributes->GetAttr(nsGkAtoms::bordercolor);
nscolor color;
if (value && value->GetColorValue(color)) {
aData->SetColorValueIfUnset(eCSSProperty_border_top_color, color);
aData->SetColorValueIfUnset(eCSSProperty_border_left_color, color);
aData->SetColorValueIfUnset(eCSSProperty_border_bottom_color, color);
aData->SetColorValueIfUnset(eCSSProperty_border_right_color, color);
}
// border
const nsAttrValue* borderValue = aAttributes->GetAttr(nsGkAtoms::border);
if (borderValue) {
// border = 1 pixel default
int32_t borderThickness = 1;
if (borderValue->Type() == nsAttrValue::eInteger)
borderThickness = borderValue->GetIntegerValue();
// by default, set all border sides to the specified width
aData->SetPixelValueIfUnset(eCSSProperty_border_top_width, (float)borderThickness);
aData->SetPixelValueIfUnset(eCSSProperty_border_left_width, (float)borderThickness);
aData->SetPixelValueIfUnset(eCSSProperty_border_bottom_width, (float)borderThickness);
aData->SetPixelValueIfUnset(eCSSProperty_border_right_width, (float)borderThickness);
}
nsGenericHTMLElement::MapImageSizeAttributesInto(aAttributes, aData);
nsGenericHTMLElement::MapBackgroundAttributesInto(aAttributes, aData);
nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);
@ -1055,18 +1049,16 @@ static void
MapInheritedTableAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Padding))) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::cellpadding);
if (value && value->Type() == nsAttrValue::eInteger) {
// We have cellpadding. This will override our padding values if we
// don't have any set.
float pad = float(value->GetIntegerValue());
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::cellpadding);
if (value && value->Type() == nsAttrValue::eInteger) {
// We have cellpadding. This will override our padding values if we
// don't have any set.
float pad = float(value->GetIntegerValue());
aData->SetPixelValueIfUnset(eCSSProperty_padding_top, pad);
aData->SetPixelValueIfUnset(eCSSProperty_padding_right, pad);
aData->SetPixelValueIfUnset(eCSSProperty_padding_bottom, pad);
aData->SetPixelValueIfUnset(eCSSProperty_padding_left, pad);
}
aData->SetPixelValueIfUnset(eCSSProperty_padding_top, pad);
aData->SetPixelValueIfUnset(eCSSProperty_padding_right, pad);
aData->SetPixelValueIfUnset(eCSSProperty_padding_bottom, pad);
aData->SetPixelValueIfUnset(eCSSProperty_padding_left, pad);
}
}

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

@ -165,13 +165,11 @@ void
HTMLTableSectionElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Position))) {
// height: value
if (!aData->PropertyIsSet(eCSSProperty_height)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height);
if (value && value->Type() == nsAttrValue::eInteger)
aData->SetPixelValue(eCSSProperty_height, (float)value->GetIntegerValue());
}
// height: value
if (!aData->PropertyIsSet(eCSSProperty_height)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height);
if (value && value->Type() == nsAttrValue::eInteger)
aData->SetPixelValue(eCSSProperty_height, (float)value->GetIntegerValue());
}
nsGenericHTMLElement::MapDivAlignAttributeInto(aAttributes, aData);
nsGenericHTMLElement::MapVAlignAttributeInto(aAttributes, aData);

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

@ -450,14 +450,12 @@ void
HTMLTextAreaElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Text))) {
// wrap=off
if (!aData->PropertyIsSet(eCSSProperty_white_space)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::wrap);
if (value && value->Type() == nsAttrValue::eString &&
value->Equals(nsGkAtoms::OFF, eIgnoreCase)) {
aData->SetKeywordValue(eCSSProperty_white_space, StyleWhiteSpace::Pre);
}
// wrap=off
if (!aData->PropertyIsSet(eCSSProperty_white_space)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::wrap);
if (value && value->Type() == nsAttrValue::eString &&
value->Equals(nsGkAtoms::OFF, eIgnoreCase)) {
aData->SetKeywordValue(eCSSProperty_white_space, StyleWhiteSpace::Pre);
}
}

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

@ -1161,33 +1161,24 @@ nsGenericHTMLElement::ParseScrollingValue(const nsAString& aString,
static inline void
MapLangAttributeInto(const nsMappedAttributes* aAttributes, GenericSpecifiedValues* aData)
{
if (!aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Font) |
NS_STYLE_INHERIT_BIT(Text))) {
return;
}
const nsAttrValue* langValue = aAttributes->GetAttr(nsGkAtoms::lang);
if (!langValue) {
return;
}
MOZ_ASSERT(langValue->Type() == nsAttrValue::eAtom);
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Font))) {
aData->SetIdentAtomValueIfUnset(eCSSProperty__x_lang,
langValue->GetAtomValue());
}
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Text))) {
if (!aData->PropertyIsSet(eCSSProperty_text_emphasis_position)) {
const nsAtom* lang = langValue->GetAtomValue();
if (nsStyleUtil::MatchesLanguagePrefix(lang, u"zh")) {
aData->SetKeywordValue(eCSSProperty_text_emphasis_position,
NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH);
} else if (nsStyleUtil::MatchesLanguagePrefix(lang, u"ja") ||
nsStyleUtil::MatchesLanguagePrefix(lang, u"mn")) {
// This branch is currently no part of the spec.
// See bug 1040668 comment 69 and comment 75.
aData->SetKeywordValue(eCSSProperty_text_emphasis_position,
NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT);
}
aData->SetIdentAtomValueIfUnset(eCSSProperty__x_lang,
langValue->GetAtomValue());
if (!aData->PropertyIsSet(eCSSProperty_text_emphasis_position)) {
const nsAtom* lang = langValue->GetAtomValue();
if (nsStyleUtil::MatchesLanguagePrefix(lang, u"zh")) {
aData->SetKeywordValue(eCSSProperty_text_emphasis_position,
NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH);
} else if (nsStyleUtil::MatchesLanguagePrefix(lang, u"ja") ||
nsStyleUtil::MatchesLanguagePrefix(lang, u"mn")) {
// This branch is currently no part of the spec.
// See bug 1040668 comment 69 and comment 75.
aData->SetKeywordValue(eCSSProperty_text_emphasis_position,
NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT);
}
}
}
@ -1199,20 +1190,18 @@ void
nsGenericHTMLElement::MapCommonAttributesIntoExceptHidden(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(UserInterface))) {
if (!aData->PropertyIsSet(eCSSProperty__moz_user_modify)) {
const nsAttrValue* value =
aAttributes->GetAttr(nsGkAtoms::contenteditable);
if (value) {
if (value->Equals(nsGkAtoms::_empty, eCaseMatters) ||
value->Equals(nsGkAtoms::_true, eIgnoreCase)) {
if (!aData->PropertyIsSet(eCSSProperty__moz_user_modify)) {
const nsAttrValue* value =
aAttributes->GetAttr(nsGkAtoms::contenteditable);
if (value) {
if (value->Equals(nsGkAtoms::_empty, eCaseMatters) ||
value->Equals(nsGkAtoms::_true, eIgnoreCase)) {
aData->SetKeywordValue(eCSSProperty__moz_user_modify,
StyleUserModify::ReadWrite);
}
else if (value->Equals(nsGkAtoms::_false, eIgnoreCase)) {
aData->SetKeywordValue(eCSSProperty__moz_user_modify,
StyleUserModify::ReadWrite);
}
else if (value->Equals(nsGkAtoms::_false, eIgnoreCase)) {
aData->SetKeywordValue(eCSSProperty__moz_user_modify,
StyleUserModify::ReadOnly);
}
StyleUserModify::ReadOnly);
}
}
}
@ -1226,11 +1215,9 @@ nsGenericHTMLElement::MapCommonAttributesInto(const nsMappedAttributes* aAttribu
{
MapCommonAttributesIntoExceptHidden(aAttributes, aData);
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Display))) {
if (!aData->PropertyIsSet(eCSSProperty_display)) {
if (aAttributes->IndexOfAttr(nsGkAtoms::hidden) >= 0) {
aData->SetKeywordValue(eCSSProperty_display, StyleDisplay::None);
}
if (!aData->PropertyIsSet(eCSSProperty_display)) {
if (aAttributes->IndexOfAttr(nsGkAtoms::hidden) >= 0) {
aData->SetKeywordValue(eCSSProperty_display, StyleDisplay::None);
}
}
}
@ -1287,26 +1274,24 @@ void
nsGenericHTMLElement::MapImageAlignAttributeInto(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Display))) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum) {
int32_t align = value->GetEnumValue();
if (!aData->PropertyIsSet(eCSSProperty_float_)) {
if (align == NS_STYLE_TEXT_ALIGN_LEFT) {
aData->SetKeywordValue(eCSSProperty_float_, StyleFloat::Left);
} else if (align == NS_STYLE_TEXT_ALIGN_RIGHT) {
aData->SetKeywordValue(eCSSProperty_float_, StyleFloat::Right);
}
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum) {
int32_t align = value->GetEnumValue();
if (!aData->PropertyIsSet(eCSSProperty_float_)) {
if (align == NS_STYLE_TEXT_ALIGN_LEFT) {
aData->SetKeywordValue(eCSSProperty_float_, StyleFloat::Left);
} else if (align == NS_STYLE_TEXT_ALIGN_RIGHT) {
aData->SetKeywordValue(eCSSProperty_float_, StyleFloat::Right);
}
if (!aData->PropertyIsSet(eCSSProperty_vertical_align)) {
switch (align) {
case NS_STYLE_TEXT_ALIGN_LEFT:
case NS_STYLE_TEXT_ALIGN_RIGHT:
break;
default:
aData->SetKeywordValue(eCSSProperty_vertical_align, align);
break;
}
}
if (!aData->PropertyIsSet(eCSSProperty_vertical_align)) {
switch (align) {
case NS_STYLE_TEXT_ALIGN_LEFT:
case NS_STYLE_TEXT_ALIGN_RIGHT:
break;
default:
aData->SetKeywordValue(eCSSProperty_vertical_align, align);
break;
}
}
}
@ -1316,13 +1301,11 @@ void
nsGenericHTMLElement::MapDivAlignAttributeInto(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Text))) {
if (!aData->PropertyIsSet(eCSSProperty_text_align)) {
// align: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum)
aData->SetKeywordValue(eCSSProperty_text_align, value->GetEnumValue());
}
if (!aData->PropertyIsSet(eCSSProperty_text_align)) {
// align: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum)
aData->SetKeywordValue(eCSSProperty_text_align, value->GetEnumValue());
}
}
@ -1330,13 +1313,11 @@ void
nsGenericHTMLElement::MapVAlignAttributeInto(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Display))) {
if (!aData->PropertyIsSet(eCSSProperty_vertical_align)) {
// align: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::valign);
if (value && value->Type() == nsAttrValue::eEnum)
aData->SetKeywordValue(eCSSProperty_vertical_align, value->GetEnumValue());
}
if (!aData->PropertyIsSet(eCSSProperty_vertical_align)) {
// align: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::valign);
if (value && value->Type() == nsAttrValue::eEnum)
aData->SetKeywordValue(eCSSProperty_vertical_align, value->GetEnumValue());
}
}
@ -1344,9 +1325,6 @@ void
nsGenericHTMLElement::MapImageMarginAttributeInto(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (!aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Margin)))
return;
const nsAttrValue* value;
// hspace: value
@ -1386,10 +1364,6 @@ void
nsGenericHTMLElement::MapWidthAttributeInto(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (!aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Position))) {
return;
}
// width: value
if (!aData->PropertyIsSet(eCSSProperty_width)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
@ -1407,9 +1381,6 @@ void
nsGenericHTMLElement::MapHeightAttributeInto(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (!aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Position)))
return;
// height: value
if (!aData->PropertyIsSet(eCSSProperty_height)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height);
@ -1435,9 +1406,6 @@ void
nsGenericHTMLElement::MapImageBorderAttributeInto(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (!(aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Border))))
return;
// border: pixels
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::border);
if (!value)
@ -1472,9 +1440,6 @@ nsGenericHTMLElement::MapBackgroundInto(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (!aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Background)))
return;
if (!aData->PropertyIsSet(eCSSProperty_background_image)) {
// background
nsAttrValue* value =
@ -1489,9 +1454,6 @@ void
nsGenericHTMLElement::MapBGColorInto(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (!aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Background)))
return;
if (!aData->PropertyIsSet(eCSSProperty_background_color)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::bgcolor);
nscolor color;

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

@ -502,280 +502,278 @@ void
nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
GenericSpecifiedValues* aData)
{
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) {
// scriptsizemultiplier
//
// "Specifies the multiplier to be used to adjust font size due to changes
// in scriptlevel.
//
// values: number
// default: 0.71
//
const nsAttrValue* value =
aAttributes->GetAttr(nsGkAtoms::scriptsizemultiplier_);
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty__moz_script_size_multiplier)) {
nsAutoString str(value->GetStringValue());
str.CompressWhitespace();
// MathML numbers can't have leading '+'
if (str.Length() > 0 && str.CharAt(0) != '+') {
nsresult errorCode;
float floatValue = str.ToFloat(&errorCode);
// Negative scriptsizemultipliers are not parsed
if (NS_SUCCEEDED(errorCode) && floatValue >= 0.0f) {
aData->SetNumberValue(eCSSProperty__moz_script_size_multiplier, floatValue);
// scriptsizemultiplier
//
// "Specifies the multiplier to be used to adjust font size due to changes
// in scriptlevel.
//
// values: number
// default: 0.71
//
const nsAttrValue* value =
aAttributes->GetAttr(nsGkAtoms::scriptsizemultiplier_);
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty__moz_script_size_multiplier)) {
nsAutoString str(value->GetStringValue());
str.CompressWhitespace();
// MathML numbers can't have leading '+'
if (str.Length() > 0 && str.CharAt(0) != '+') {
nsresult errorCode;
float floatValue = str.ToFloat(&errorCode);
// Negative scriptsizemultipliers are not parsed
if (NS_SUCCEEDED(errorCode) && floatValue >= 0.0f) {
aData->SetNumberValue(eCSSProperty__moz_script_size_multiplier, floatValue);
} else {
ReportParseErrorNoTag(str,
nsGkAtoms::scriptsizemultiplier_,
aData->Document());
}
}
}
// scriptminsize
//
// "Specifies the minimum font size allowed due to changes in scriptlevel.
// Note that this does not limit the font size due to changes to mathsize."
//
// values: length
// default: 8pt
//
// We don't allow negative values.
// Unitless and percent values give a multiple of the default value.
//
value = aAttributes->GetAttr(nsGkAtoms::scriptminsize_);
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty__moz_script_min_size)) {
nsCSSValue scriptMinSize;
ParseNumericValue(value->GetStringValue(), scriptMinSize,
PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT,
aData->Document());
if (scriptMinSize.GetUnit() == eCSSUnit_Percent) {
scriptMinSize.SetFloatValue(8.0 * scriptMinSize.GetPercentValue(),
eCSSUnit_Point);
}
if (scriptMinSize.GetUnit() != eCSSUnit_Null) {
aData->SetLengthValue(eCSSProperty__moz_script_min_size, scriptMinSize);
}
}
// scriptlevel
//
// "Changes the scriptlevel in effect for the children. When the value is
// given without a sign, it sets scriptlevel to the specified value; when a
// sign is given, it increments ("+") or decrements ("-") the current
// value. (Note that large decrements can result in negative values of
// scriptlevel, but these values are considered legal.)"
//
// values: ( "+" | "-" )? unsigned-integer
// default: inherited
//
value = aAttributes->GetAttr(nsGkAtoms::scriptlevel_);
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty__moz_script_level)) {
nsAutoString str(value->GetStringValue());
str.CompressWhitespace();
if (str.Length() > 0) {
nsresult errorCode;
int32_t intValue = str.ToInteger(&errorCode);
if (NS_SUCCEEDED(errorCode)) {
// This is kind of cheesy ... if the scriptlevel has a sign,
// then it's a relative value and we store the nsCSSValue as an
// Integer to indicate that. Otherwise we store it as a Number
// to indicate that the scriptlevel is absolute.
char16_t ch = str.CharAt(0);
if (ch == '+' || ch == '-') {
aData->SetIntValue(eCSSProperty__moz_script_level, intValue);
} else {
ReportParseErrorNoTag(str,
nsGkAtoms::scriptsizemultiplier_,
aData->Document());
aData->SetNumberValue(eCSSProperty__moz_script_level, intValue);
}
} else {
ReportParseErrorNoTag(str,
nsGkAtoms::scriptlevel_,
aData->Document());
}
}
}
// scriptminsize
//
// "Specifies the minimum font size allowed due to changes in scriptlevel.
// Note that this does not limit the font size due to changes to mathsize."
//
// values: length
// default: 8pt
//
// We don't allow negative values.
// Unitless and percent values give a multiple of the default value.
//
value = aAttributes->GetAttr(nsGkAtoms::scriptminsize_);
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty__moz_script_min_size)) {
nsCSSValue scriptMinSize;
ParseNumericValue(value->GetStringValue(), scriptMinSize,
PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT,
aData->Document());
if (scriptMinSize.GetUnit() == eCSSUnit_Percent) {
scriptMinSize.SetFloatValue(8.0 * scriptMinSize.GetPercentValue(),
eCSSUnit_Point);
}
if (scriptMinSize.GetUnit() != eCSSUnit_Null) {
aData->SetLengthValue(eCSSProperty__moz_script_min_size, scriptMinSize);
}
}
// scriptlevel
//
// "Changes the scriptlevel in effect for the children. When the value is
// given without a sign, it sets scriptlevel to the specified value; when a
// sign is given, it increments ("+") or decrements ("-") the current
// value. (Note that large decrements can result in negative values of
// scriptlevel, but these values are considered legal.)"
//
// values: ( "+" | "-" )? unsigned-integer
// default: inherited
//
value = aAttributes->GetAttr(nsGkAtoms::scriptlevel_);
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty__moz_script_level)) {
nsAutoString str(value->GetStringValue());
str.CompressWhitespace();
if (str.Length() > 0) {
nsresult errorCode;
int32_t intValue = str.ToInteger(&errorCode);
if (NS_SUCCEEDED(errorCode)) {
// This is kind of cheesy ... if the scriptlevel has a sign,
// then it's a relative value and we store the nsCSSValue as an
// Integer to indicate that. Otherwise we store it as a Number
// to indicate that the scriptlevel is absolute.
char16_t ch = str.CharAt(0);
if (ch == '+' || ch == '-') {
aData->SetIntValue(eCSSProperty__moz_script_level, intValue);
} else {
aData->SetNumberValue(eCSSProperty__moz_script_level, intValue);
}
} else {
ReportParseErrorNoTag(str,
nsGkAtoms::scriptlevel_,
aData->Document());
}
}
}
// mathsize
//
// "Specifies the size to display the token content. The values 'small' and
// 'big' choose a size smaller or larger than the current font size, but
// leave the exact proportions unspecified; 'normal' is allowed for
// completeness, but since it is equivalent to '100%' or '1em', it has no
// effect."
//
// values: "small" | "normal" | "big" | length
// default: inherited
//
// fontsize
//
// "Specified the size for the token. Deprecated in favor of mathsize."
//
// values: length
// default: inherited
//
// In both cases, we don't allow negative values.
// Unitless values give a multiple of the default value.
//
bool parseSizeKeywords = true;
value = aAttributes->GetAttr(nsGkAtoms::mathsize_);
if (!value) {
parseSizeKeywords = false;
value = aAttributes->GetAttr(nsGkAtoms::fontsize_);
if (value) {
WarnDeprecated(nsGkAtoms::fontsize_->GetUTF16String(),
nsGkAtoms::mathsize_->GetUTF16String(),
aData->Document());
}
}
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty_font_size)) {
nsAutoString str(value->GetStringValue());
nsCSSValue fontSize;
if (!ParseNumericValue(str, fontSize, PARSE_SUPPRESS_WARNINGS |
PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT,
nullptr)
&& parseSizeKeywords) {
static const char sizes[3][7] = { "small", "normal", "big" };
static const int32_t values[MOZ_ARRAY_LENGTH(sizes)] = {
NS_STYLE_FONT_SIZE_SMALL, NS_STYLE_FONT_SIZE_MEDIUM,
NS_STYLE_FONT_SIZE_LARGE
};
str.CompressWhitespace();
for (uint32_t i = 0; i < ArrayLength(sizes); ++i) {
if (str.EqualsASCII(sizes[i])) {
aData->SetKeywordValue(eCSSProperty_font_size, values[i]);
break;
}
}
} else if (fontSize.GetUnit() == eCSSUnit_Percent) {
aData->SetPercentValue(eCSSProperty_font_size,
fontSize.GetPercentValue());
} else if (fontSize.GetUnit() != eCSSUnit_Null) {
aData->SetLengthValue(eCSSProperty_font_size, fontSize);
}
}
// fontfamily
//
// "Should be the name of a font that may be available to a MathML renderer,
// or a CSS font specification; See Section 6.5 Using CSS with MathML and
// CSS for more information. Deprecated in favor of mathvariant."
//
// values: string
//
value = aAttributes->GetAttr(nsGkAtoms::fontfamily_);
// mathsize
//
// "Specifies the size to display the token content. The values 'small' and
// 'big' choose a size smaller or larger than the current font size, but
// leave the exact proportions unspecified; 'normal' is allowed for
// completeness, but since it is equivalent to '100%' or '1em', it has no
// effect."
//
// values: "small" | "normal" | "big" | length
// default: inherited
//
// fontsize
//
// "Specified the size for the token. Deprecated in favor of mathsize."
//
// values: length
// default: inherited
//
// In both cases, we don't allow negative values.
// Unitless values give a multiple of the default value.
//
bool parseSizeKeywords = true;
value = aAttributes->GetAttr(nsGkAtoms::mathsize_);
if (!value) {
parseSizeKeywords = false;
value = aAttributes->GetAttr(nsGkAtoms::fontsize_);
if (value) {
WarnDeprecated(nsGkAtoms::fontfamily_->GetUTF16String(),
nsGkAtoms::mathvariant_->GetUTF16String(),
WarnDeprecated(nsGkAtoms::fontsize_->GetUTF16String(),
nsGkAtoms::mathsize_->GetUTF16String(),
aData->Document());
}
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty_font_family)) {
aData->SetFontFamily(value->GetStringValue());
}
// fontstyle
//
// "Specified the font style to use for the token. Deprecated in favor of
// mathvariant."
//
// values: "normal" | "italic"
// default: normal (except on <mi>)
//
// Note that the font-style property is reset in layout/style/ when
// -moz-math-variant is specified.
value = aAttributes->GetAttr(nsGkAtoms::fontstyle_);
if (value) {
WarnDeprecated(nsGkAtoms::fontstyle_->GetUTF16String(),
nsGkAtoms::mathvariant_->GetUTF16String(),
aData->Document());
if (value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty_font_style)) {
nsAutoString str(value->GetStringValue());
str.CompressWhitespace();
if (str.EqualsASCII("normal")) {
aData->SetKeywordValue(eCSSProperty_font_style,
NS_STYLE_FONT_STYLE_NORMAL);
} else if (str.EqualsASCII("italic")) {
aData->SetKeywordValue(eCSSProperty_font_style,
NS_STYLE_FONT_STYLE_ITALIC);
}
}
}
// fontweight
//
// "Specified the font weight for the token. Deprecated in favor of
// mathvariant."
//
// values: "normal" | "bold"
// default: normal
//
// Note that the font-weight property is reset in layout/style/ when
// -moz-math-variant is specified.
value = aAttributes->GetAttr(nsGkAtoms::fontweight_);
if (value) {
WarnDeprecated(nsGkAtoms::fontweight_->GetUTF16String(),
nsGkAtoms::mathvariant_->GetUTF16String(),
aData->Document());
if (value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty_font_weight)) {
nsAutoString str(value->GetStringValue());
str.CompressWhitespace();
if (str.EqualsASCII("normal")) {
aData->SetKeywordValue(eCSSProperty_font_weight,
NS_STYLE_FONT_WEIGHT_NORMAL);
} else if (str.EqualsASCII("bold")) {
aData->SetKeywordValue(eCSSProperty_font_weight,
NS_STYLE_FONT_WEIGHT_BOLD);
}
}
}
// mathvariant
//
// "Specifies the logical class of the token. Note that this class is more
// than styling, it typically conveys semantic intent;"
//
// values: "normal" | "bold" | "italic" | "bold-italic" | "double-struck" |
// "bold-fraktur" | "script" | "bold-script" | "fraktur" | "sans-serif" |
// "bold-sans-serif" | "sans-serif-italic" | "sans-serif-bold-italic" |
// "monospace" | "initial" | "tailed" | "looped" | "stretched"
// default: normal (except on <mi>)
//
value = aAttributes->GetAttr(nsGkAtoms::mathvariant_);
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty__moz_math_variant)) {
nsAutoString str(value->GetStringValue());
str.CompressWhitespace();
static const char sizes[19][23] = {
"normal", "bold", "italic", "bold-italic", "script", "bold-script",
"fraktur", "double-struck", "bold-fraktur", "sans-serif",
"bold-sans-serif", "sans-serif-italic", "sans-serif-bold-italic",
"monospace", "initial", "tailed", "looped", "stretched"
};
}
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty_font_size)) {
nsAutoString str(value->GetStringValue());
nsCSSValue fontSize;
if (!ParseNumericValue(str, fontSize, PARSE_SUPPRESS_WARNINGS |
PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT,
nullptr)
&& parseSizeKeywords) {
static const char sizes[3][7] = { "small", "normal", "big" };
static const int32_t values[MOZ_ARRAY_LENGTH(sizes)] = {
NS_MATHML_MATHVARIANT_NORMAL, NS_MATHML_MATHVARIANT_BOLD,
NS_MATHML_MATHVARIANT_ITALIC, NS_MATHML_MATHVARIANT_BOLD_ITALIC,
NS_MATHML_MATHVARIANT_SCRIPT, NS_MATHML_MATHVARIANT_BOLD_SCRIPT,
NS_MATHML_MATHVARIANT_FRAKTUR, NS_MATHML_MATHVARIANT_DOUBLE_STRUCK,
NS_MATHML_MATHVARIANT_BOLD_FRAKTUR, NS_MATHML_MATHVARIANT_SANS_SERIF,
NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF,
NS_MATHML_MATHVARIANT_SANS_SERIF_ITALIC,
NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC,
NS_MATHML_MATHVARIANT_MONOSPACE, NS_MATHML_MATHVARIANT_INITIAL,
NS_MATHML_MATHVARIANT_TAILED, NS_MATHML_MATHVARIANT_LOOPED,
NS_MATHML_MATHVARIANT_STRETCHED
NS_STYLE_FONT_SIZE_SMALL, NS_STYLE_FONT_SIZE_MEDIUM,
NS_STYLE_FONT_SIZE_LARGE
};
str.CompressWhitespace();
for (uint32_t i = 0; i < ArrayLength(sizes); ++i) {
if (str.EqualsASCII(sizes[i])) {
aData->SetKeywordValue(eCSSProperty__moz_math_variant, values[i]);
aData->SetKeywordValue(eCSSProperty_font_size, values[i]);
break;
}
}
} else if (fontSize.GetUnit() == eCSSUnit_Percent) {
aData->SetPercentValue(eCSSProperty_font_size,
fontSize.GetPercentValue());
} else if (fontSize.GetUnit() != eCSSUnit_Null) {
aData->SetLengthValue(eCSSProperty_font_size, fontSize);
}
}
// fontfamily
//
// "Should be the name of a font that may be available to a MathML renderer,
// or a CSS font specification; See Section 6.5 Using CSS with MathML and
// CSS for more information. Deprecated in favor of mathvariant."
//
// values: string
//
value = aAttributes->GetAttr(nsGkAtoms::fontfamily_);
if (value) {
WarnDeprecated(nsGkAtoms::fontfamily_->GetUTF16String(),
nsGkAtoms::mathvariant_->GetUTF16String(),
aData->Document());
}
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty_font_family)) {
aData->SetFontFamily(value->GetStringValue());
}
// fontstyle
//
// "Specified the font style to use for the token. Deprecated in favor of
// mathvariant."
//
// values: "normal" | "italic"
// default: normal (except on <mi>)
//
// Note that the font-style property is reset in layout/style/ when
// -moz-math-variant is specified.
value = aAttributes->GetAttr(nsGkAtoms::fontstyle_);
if (value) {
WarnDeprecated(nsGkAtoms::fontstyle_->GetUTF16String(),
nsGkAtoms::mathvariant_->GetUTF16String(),
aData->Document());
if (value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty_font_style)) {
nsAutoString str(value->GetStringValue());
str.CompressWhitespace();
if (str.EqualsASCII("normal")) {
aData->SetKeywordValue(eCSSProperty_font_style,
NS_STYLE_FONT_STYLE_NORMAL);
} else if (str.EqualsASCII("italic")) {
aData->SetKeywordValue(eCSSProperty_font_style,
NS_STYLE_FONT_STYLE_ITALIC);
}
}
}
// fontweight
//
// "Specified the font weight for the token. Deprecated in favor of
// mathvariant."
//
// values: "normal" | "bold"
// default: normal
//
// Note that the font-weight property is reset in layout/style/ when
// -moz-math-variant is specified.
value = aAttributes->GetAttr(nsGkAtoms::fontweight_);
if (value) {
WarnDeprecated(nsGkAtoms::fontweight_->GetUTF16String(),
nsGkAtoms::mathvariant_->GetUTF16String(),
aData->Document());
if (value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty_font_weight)) {
nsAutoString str(value->GetStringValue());
str.CompressWhitespace();
if (str.EqualsASCII("normal")) {
aData->SetKeywordValue(eCSSProperty_font_weight,
NS_STYLE_FONT_WEIGHT_NORMAL);
} else if (str.EqualsASCII("bold")) {
aData->SetKeywordValue(eCSSProperty_font_weight,
NS_STYLE_FONT_WEIGHT_BOLD);
}
}
}
// mathvariant
//
// "Specifies the logical class of the token. Note that this class is more
// than styling, it typically conveys semantic intent;"
//
// values: "normal" | "bold" | "italic" | "bold-italic" | "double-struck" |
// "bold-fraktur" | "script" | "bold-script" | "fraktur" | "sans-serif" |
// "bold-sans-serif" | "sans-serif-italic" | "sans-serif-bold-italic" |
// "monospace" | "initial" | "tailed" | "looped" | "stretched"
// default: normal (except on <mi>)
//
value = aAttributes->GetAttr(nsGkAtoms::mathvariant_);
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty__moz_math_variant)) {
nsAutoString str(value->GetStringValue());
str.CompressWhitespace();
static const char sizes[19][23] = {
"normal", "bold", "italic", "bold-italic", "script", "bold-script",
"fraktur", "double-struck", "bold-fraktur", "sans-serif",
"bold-sans-serif", "sans-serif-italic", "sans-serif-bold-italic",
"monospace", "initial", "tailed", "looped", "stretched"
};
static const int32_t values[MOZ_ARRAY_LENGTH(sizes)] = {
NS_MATHML_MATHVARIANT_NORMAL, NS_MATHML_MATHVARIANT_BOLD,
NS_MATHML_MATHVARIANT_ITALIC, NS_MATHML_MATHVARIANT_BOLD_ITALIC,
NS_MATHML_MATHVARIANT_SCRIPT, NS_MATHML_MATHVARIANT_BOLD_SCRIPT,
NS_MATHML_MATHVARIANT_FRAKTUR, NS_MATHML_MATHVARIANT_DOUBLE_STRUCK,
NS_MATHML_MATHVARIANT_BOLD_FRAKTUR, NS_MATHML_MATHVARIANT_SANS_SERIF,
NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF,
NS_MATHML_MATHVARIANT_SANS_SERIF_ITALIC,
NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC,
NS_MATHML_MATHVARIANT_MONOSPACE, NS_MATHML_MATHVARIANT_INITIAL,
NS_MATHML_MATHVARIANT_TAILED, NS_MATHML_MATHVARIANT_LOOPED,
NS_MATHML_MATHVARIANT_STRETCHED
};
for (uint32_t i = 0; i < ArrayLength(sizes); ++i) {
if (str.EqualsASCII(sizes[i])) {
aData->SetKeywordValue(eCSSProperty__moz_math_variant, values[i]);
break;
}
}
}
@ -797,22 +795,19 @@ nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
// values: color | "transparent"
// default: "transparent"
//
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Background)) {
const nsAttrValue* value =
aAttributes->GetAttr(nsGkAtoms::mathbackground_);
if (!value) {
value = aAttributes->GetAttr(nsGkAtoms::background);
if (value) {
WarnDeprecated(nsGkAtoms::background->GetUTF16String(),
nsGkAtoms::mathbackground_->GetUTF16String(),
aData->Document());
}
}
value = aAttributes->GetAttr(nsGkAtoms::mathbackground_);
if (!value) {
value = aAttributes->GetAttr(nsGkAtoms::background);
if (value) {
nscolor color;
if (value->GetColorValue(color)) {
aData->SetColorValueIfUnset(eCSSProperty_background_color, color);
}
WarnDeprecated(nsGkAtoms::background->GetUTF16String(),
nsGkAtoms::mathbackground_->GetUTF16String(),
aData->Document());
}
}
if (value) {
nscolor color;
if (value->GetColorValue(color)) {
aData->SetColorValueIfUnset(eCSSProperty_background_color, color);
}
}
@ -833,47 +828,43 @@ nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
// values: color
// default: inherited
//
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Color)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::mathcolor_);
if (!value) {
value = aAttributes->GetAttr(nsGkAtoms::color);
if (value) {
WarnDeprecated(nsGkAtoms::color->GetUTF16String(),
nsGkAtoms::mathcolor_->GetUTF16String(),
aData->Document());
}
}
nscolor color;
if (value && value->GetColorValue(color)) {
aData->SetColorValueIfUnset(eCSSProperty_color, color);
value = aAttributes->GetAttr(nsGkAtoms::mathcolor_);
if (!value) {
value = aAttributes->GetAttr(nsGkAtoms::color);
if (value) {
WarnDeprecated(nsGkAtoms::color->GetUTF16String(),
nsGkAtoms::mathcolor_->GetUTF16String(),
aData->Document());
}
}
nscolor color;
if (value && value->GetColorValue(color)) {
aData->SetColorValueIfUnset(eCSSProperty_color, color);
}
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)) {
// width
//
// "Specifies the desired width of the entire table and is intended for
// visual user agents. When the value is a percentage value, the value is
// relative to the horizontal space a MathML renderer has available for the
// math element. When the value is "auto", the MathML renderer should
// calculate the table width from its contents using whatever layout
// algorithm it chooses. "
//
// values: "auto" | length
// default: auto
//
if (!aData->PropertyIsSet(eCSSProperty_width)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
nsCSSValue width;
// This does not handle auto and unitless values
if (value && value->Type() == nsAttrValue::eString) {
ParseNumericValue(value->GetStringValue(), width, 0, aData->Document());
if (width.GetUnit() == eCSSUnit_Percent) {
aData->SetPercentValue(eCSSProperty_width,
width.GetPercentValue());
} else if (width.GetUnit() != eCSSUnit_Null) {
aData->SetLengthValue(eCSSProperty_width, width);
}
// width
//
// "Specifies the desired width of the entire table and is intended for
// visual user agents. When the value is a percentage value, the value is
// relative to the horizontal space a MathML renderer has available for the
// math element. When the value is "auto", the MathML renderer should
// calculate the table width from its contents using whatever layout
// algorithm it chooses. "
//
// values: "auto" | length
// default: auto
//
if (!aData->PropertyIsSet(eCSSProperty_width)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
nsCSSValue width;
// This does not handle auto and unitless values
if (value && value->Type() == nsAttrValue::eString) {
ParseNumericValue(value->GetStringValue(), width, 0, aData->Document());
if (width.GetUnit() == eCSSUnit_Percent) {
aData->SetPercentValue(eCSSProperty_width,
width.GetPercentValue());
} else if (width.GetUnit() != eCSSUnit_Null) {
aData->SetLengthValue(eCSSProperty_width, width);
}
}
}
@ -898,20 +889,18 @@ nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
// values: "ltr" | "rtl"
// default: inherited
//
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Visibility)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::dir);
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty_direction)) {
nsAutoString str(value->GetStringValue());
static const char dirs[][4] = { "ltr", "rtl" };
static const int32_t dirValues[MOZ_ARRAY_LENGTH(dirs)] = {
NS_STYLE_DIRECTION_LTR, NS_STYLE_DIRECTION_RTL
};
for (uint32_t i = 0; i < ArrayLength(dirs); ++i) {
if (str.EqualsASCII(dirs[i])) {
aData->SetKeywordValue(eCSSProperty_direction, dirValues[i]);
break;
}
value = aAttributes->GetAttr(nsGkAtoms::dir);
if (value && value->Type() == nsAttrValue::eString &&
!aData->PropertyIsSet(eCSSProperty_direction)) {
nsAutoString str(value->GetStringValue());
static const char dirs[][4] = { "ltr", "rtl" };
static const int32_t dirValues[MOZ_ARRAY_LENGTH(dirs)] = {
NS_STYLE_DIRECTION_LTR, NS_STYLE_DIRECTION_RTL
};
for (uint32_t i = 0; i < ArrayLength(dirs); ++i) {
if (str.EqualsASCII(dirs[i])) {
aData->SetKeywordValue(eCSSProperty_direction, dirValues[i]);
break;
}
}
}

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

@ -446,6 +446,13 @@ public:
return mLayer->GetFixedPositionScrollContainerId();
}
bool IsBackfaceHidden() const
{
MOZ_ASSERT(IsValid());
return mLayer->IsBackfaceHidden();
}
// Expose an opaque pointer to the layer. Mostly used for printf
// purposes. This is not intended to be a general-purpose accessor
// for the underlying layer.

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

@ -7,10 +7,7 @@
#ifndef mozilla_layers_APZSampler_h
#define mozilla_layers_APZSampler_h
#include "LayersTypes.h"
#include "mozilla/layers/APZTestData.h"
#include "mozilla/layers/AsyncCompositionManager.h" // for AsyncTransform
#include "mozilla/Maybe.h"
#include "nsTArray.h"
#include "Units.h"
@ -26,16 +23,13 @@ struct WrTransformProperty;
namespace layers {
class APZCTreeManager;
class FocusTarget;
class Layer;
class LayerMetricsWrapper;
struct ScrollThumbData;
class WebRenderScrollData;
/**
* This interface is used to interact with the APZ code from the compositor
* thread. It internally redispatches the functions to the sampler thread
* in the case where the two threads are not the same.
* This interface exposes APZ methods related to "sampling" (i.e. reading the
* async transforms produced by APZ). These methods should all be called on
* the sampler thread.
*/
class APZSampler {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZSampler)
@ -43,38 +37,10 @@ class APZSampler {
public:
explicit APZSampler(const RefPtr<APZCTreeManager>& aApz);
void ClearTree();
void UpdateFocusState(LayersId aRootLayerTreeId,
LayersId aOriginatingLayersId,
const FocusTarget& aFocusTarget);
void UpdateHitTestingTree(LayersId aRootLayerTreeId,
Layer* aRoot,
bool aIsFirstPaint,
LayersId aOriginatingLayersId,
uint32_t aPaintSequenceNumber);
void UpdateHitTestingTree(LayersId aRootLayerTreeId,
const WebRenderScrollData& aScrollData,
bool aIsFirstPaint,
LayersId aOriginatingLayersId,
uint32_t aPaintSequenceNumber);
void NotifyLayerTreeAdopted(LayersId aLayersId,
const RefPtr<APZSampler>& aOldSampler);
void NotifyLayerTreeRemoved(LayersId aLayersId);
bool PushStateToWR(wr::TransactionBuilder& aTxn,
const TimeStamp& aSampleTime,
nsTArray<wr::WrTransformProperty>& aTransformArray);
bool GetAPZTestData(LayersId aLayersId, APZTestData* aOutData);
void SetTestAsyncScrollOffset(LayersId aLayersId,
const FrameMetrics::ViewID& aScrollId,
const CSSPoint& aOffset);
void SetTestAsyncZoom(LayersId aLayersId,
const FrameMetrics::ViewID& aScrollId,
const LayerToParentLayerScale& aZoom);
bool SampleAnimations(const LayerMetricsWrapper& aLayer,
const TimeStamp& aSampleTime);
@ -104,6 +70,18 @@ public:
void MarkAsyncTransformAppliedToContent(const LayerMetricsWrapper& aLayer);
bool HasUnusedAsyncTransform(const LayerMetricsWrapper& aLayer);
/**
* This can be used to assert that the current thread is the
* sampler thread (which samples the async transform).
* This does nothing if thread assertions are disabled.
*/
void AssertOnSamplerThread();
/**
* Returns true if currently on the APZSampler's "sampler thread".
*/
bool IsSamplerThread();
protected:
virtual ~APZSampler();

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

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_layers_APZUpdater_h
#define mozilla_layers_APZUpdater_h
#include "LayersTypes.h"
#include "mozilla/layers/APZTestData.h"
#include "nsThreadUtils.h"
#include "Units.h"
namespace mozilla {
namespace layers {
class APZCTreeManager;
class FocusTarget;
class Layer;
class WebRenderScrollData;
/**
* This interface is used to send updates or otherwise mutate APZ internal
* state. These functions is usually called from the compositor thread in
* response to IPC messages. The method implementations internally redispatch
* themselves to the updater thread in the case where the compositor thread
* is not the updater thread.
*/
class APZUpdater {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZUpdater)
public:
explicit APZUpdater(const RefPtr<APZCTreeManager>& aApz);
bool HasTreeManager(const RefPtr<APZCTreeManager>& aApz);
void ClearTree();
void UpdateFocusState(LayersId aRootLayerTreeId,
LayersId aOriginatingLayersId,
const FocusTarget& aFocusTarget);
void UpdateHitTestingTree(LayersId aRootLayerTreeId,
Layer* aRoot,
bool aIsFirstPaint,
LayersId aOriginatingLayersId,
uint32_t aPaintSequenceNumber);
void UpdateHitTestingTree(LayersId aRootLayerTreeId,
const WebRenderScrollData& aScrollData,
bool aIsFirstPaint,
LayersId aOriginatingLayersId,
uint32_t aPaintSequenceNumber);
void NotifyLayerTreeAdopted(LayersId aLayersId,
const RefPtr<APZUpdater>& aOldUpdater);
void NotifyLayerTreeRemoved(LayersId aLayersId);
bool GetAPZTestData(LayersId aLayersId, APZTestData* aOutData);
void SetTestAsyncScrollOffset(LayersId aLayersId,
const FrameMetrics::ViewID& aScrollId,
const CSSPoint& aOffset);
void SetTestAsyncZoom(LayersId aLayersId,
const FrameMetrics::ViewID& aScrollId,
const LayerToParentLayerScale& aZoom);
/**
* This can be used to assert that the current thread is the
* updater thread (which samples the async transform).
* This does nothing if thread assertions are disabled.
*/
void AssertOnUpdaterThread();
/**
* Runs the given task on the APZ "updater thread" for this APZUpdater. If
* this function is called from the updater thread itself then the task is
* run immediately without getting queued.
*/
void RunOnUpdaterThread(already_AddRefed<Runnable> aTask);
/**
* Returns true if currently on the APZUpdater's "updater thread".
*/
bool IsUpdaterThread();
/**
* Dispatches the given task to the APZ "controller thread", but does it *from*
* the updater thread. That is, if the thread on which this function is called
* is not the updater thread, the task is first dispatched to the updater thread.
* When the updater thread runs it (or if this is called directly on the updater
* thread), that is when the task gets dispatched to the controller thread.
* The controller thread then actually runs the task.
*/
void RunOnControllerThread(already_AddRefed<Runnable> aTask);
protected:
virtual ~APZUpdater();
private:
RefPtr<APZCTreeManager> mApz;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_layers_APZUpdater_h

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

@ -21,7 +21,9 @@
#include "mozilla/gfx/GPUParent.h" // for GPUParent
#include "mozilla/gfx/Logging.h" // for gfx::TreeLog
#include "mozilla/gfx/Point.h" // for Point
#include "mozilla/layers/APZSampler.h" // for APZSampler
#include "mozilla/layers/APZThreadUtils.h" // for AssertOnControllerThread, etc
#include "mozilla/layers/APZUpdater.h" // for APZUpdater
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
@ -140,7 +142,7 @@ public:
}
protected:
virtual ~CheckerboardFlushObserver() {}
virtual ~CheckerboardFlushObserver() = default;
private:
RefPtr<APZCTreeManager> mTreeManager;
@ -224,6 +226,8 @@ private:
APZCTreeManager::APZCTreeManager(LayersId aRootLayersId)
: mInputQueue(new InputQueue()),
mRootLayersId(aRootLayersId),
mSampler(nullptr),
mUpdater(nullptr),
mTreeLock("APZCTreeLock"),
mHitResultForInputBlock(CompositorHitTestInfo::eInvisibleToHitTest),
mRetainedTouchIdentifier(-1),
@ -244,15 +248,29 @@ APZCTreeManager::APZCTreeManager(LayersId aRootLayersId)
#endif // (MOZ_WIDGET_ANDROID)
}
APZCTreeManager::~APZCTreeManager()
APZCTreeManager::~APZCTreeManager() = default;
void
APZCTreeManager::SetSampler(APZSampler* aSampler)
{
// We're either setting the sampler or clearing it
MOZ_ASSERT((mSampler == nullptr) != (aSampler == nullptr));
mSampler = aSampler;
}
void
APZCTreeManager::SetUpdater(APZUpdater* aUpdater)
{
// We're either setting the updater or clearing it
MOZ_ASSERT((mUpdater == nullptr) != (aUpdater == nullptr));
mUpdater = aUpdater;
}
void
APZCTreeManager::NotifyLayerTreeAdopted(LayersId aLayersId,
const RefPtr<APZCTreeManager>& aOldApzcTreeManager)
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnUpdaterThread();
if (aOldApzcTreeManager) {
aOldApzcTreeManager->mFocusState.RemoveFocusTarget(aLayersId);
@ -279,7 +297,7 @@ APZCTreeManager::NotifyLayerTreeAdopted(LayersId aLayersId,
void
APZCTreeManager::NotifyLayerTreeRemoved(LayersId aLayersId)
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnUpdaterThread();
mFocusState.RemoveFocusTarget(aLayersId);
@ -474,7 +492,7 @@ APZCTreeManager::UpdateFocusState(LayersId aRootLayerTreeId,
LayersId aOriginatingLayersId,
const FocusTarget& aFocusTarget)
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnUpdaterThread();
if (!gfxPrefs::APZKeyboardEnabled()) {
return;
@ -492,7 +510,7 @@ APZCTreeManager::UpdateHitTestingTree(LayersId aRootLayerTreeId,
LayersId aOriginatingLayersId,
uint32_t aPaintSequenceNumber)
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnUpdaterThread();
LayerMetricsWrapper root(aRoot);
UpdateHitTestingTreeImpl(aRootLayerTreeId, root, aIsFirstPaint,
@ -506,7 +524,7 @@ APZCTreeManager::UpdateHitTestingTree(LayersId aRootLayerTreeId,
LayersId aOriginatingLayersId,
uint32_t aPaintSequenceNumber)
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnUpdaterThread();
WebRenderScrollDataWrapper wrapper(&aScrollData);
UpdateHitTestingTreeImpl(aRootLayerTreeId, wrapper, aIsFirstPaint,
@ -518,7 +536,7 @@ APZCTreeManager::PushStateToWR(wr::TransactionBuilder& aTxn,
const TimeStamp& aSampleTime,
nsTArray<wr::WrTransformProperty>& aTransformArray)
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnSamplerThread();
RecursiveMutexAutoLock lock(mTreeLock);
@ -843,7 +861,8 @@ APZCTreeManager::PrepareNodeForLayer(const ScrollNode& aLayer,
(!parentHasPerspective && aLayer.GetClipRect())
? Some(ParentLayerIntRegion(*aLayer.GetClipRect()))
: Nothing(),
GetEventRegionsOverride(aParent, aLayer));
GetEventRegionsOverride(aParent, aLayer),
aLayer.IsBackfaceHidden());
node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
aLayer.GetScrollbarAnimationId(),
aLayer.GetScrollThumbData(),
@ -958,7 +977,8 @@ APZCTreeManager::PrepareNodeForLayer(const ScrollNode& aLayer,
aLayer.GetVisibleRegion(),
aLayer.GetTransformTyped(),
clipRegion,
GetEventRegionsOverride(aParent, aLayer));
GetEventRegionsOverride(aParent, aLayer),
aLayer.IsBackfaceHidden());
apzc->SetAncestorTransform(aAncestorTransform);
PrintAPZCInfo(aLayer, apzc);
@ -1057,7 +1077,8 @@ APZCTreeManager::PrepareNodeForLayer(const ScrollNode& aLayer,
aLayer.GetVisibleRegion(),
aLayer.GetTransformTyped(),
clipRegion,
GetEventRegionsOverride(aParent, aLayer));
GetEventRegionsOverride(aParent, aLayer),
aLayer.IsBackfaceHidden());
}
// Note: if layer properties must be propagated to nodes, RecvUpdate in
@ -1902,7 +1923,7 @@ APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
const CSSRect& aRect,
const uint32_t aFlags)
{
// We could probably move this to run on the sampler thread if needed, but
// We could probably move this to run on the updater thread if needed, but
// either way we should restrict it to a single thread. For now let's use the
// controller thread.
APZThreadUtils::AssertOnControllerThread();
@ -1951,13 +1972,13 @@ void
APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
const Maybe<ZoomConstraints>& aConstraints)
{
if (!APZThreadUtils::IsSamplerThread()) {
if (!GetUpdater()->IsUpdaterThread()) {
// This can happen if we're in the UI process and got a call directly from
// nsBaseWidget (as opposed to over PAPZCTreeManager). We want this function
// to run on the sampler thread, so bounce it over.
// to run on the updater thread, so bounce it over.
MOZ_ASSERT(XRE_IsParentProcess());
APZThreadUtils::RunOnSamplerThread(
GetUpdater()->RunOnUpdaterThread(
NewRunnableMethod<ScrollableLayerGuid, Maybe<ZoomConstraints>>(
"APZCTreeManager::UpdateZoomConstraints",
this,
@ -1967,7 +1988,7 @@ APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
return;
}
APZThreadUtils::AssertOnSamplerThread();
AssertOnUpdaterThread();
RecursiveMutexAutoLock lock(mTreeLock);
RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
@ -2062,7 +2083,7 @@ APZCTreeManager::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
void
APZCTreeManager::ClearTree()
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnUpdaterThread();
#if defined(MOZ_WIDGET_ANDROID)
mToolbarAnimator->ClearTreeManager();
@ -2525,9 +2546,8 @@ APZCTreeManager::BuildOverscrollHandoffChain(const RefPtr<AsyncPanZoomController
void
APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled)
{
APZThreadUtils::RunOnControllerThread(
NewRunnableFunction("SetLongTapEnabledRunnable",
GestureEventListener::SetLongTapEnabled, aLongTapEnabled));
APZThreadUtils::AssertOnControllerThread();
GestureEventListener::SetLongTapEnabled(aLongTapEnabled);
}
RefPtr<HitTestingTreeNode>
@ -3018,7 +3038,7 @@ bool
APZCTreeManager::GetAPZTestData(LayersId aLayersId,
APZTestData* aOutData)
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnUpdaterThread();
MutexAutoLock lock(mTestDataLock);
auto it = mTestData.find(aLayersId);
if (it == mTestData.end()) {
@ -3168,7 +3188,7 @@ APZCTreeManager::ComputeTransformForScrollThumb(
AsyncTransformComponentMatrix overscroll =
aApzc->GetOverscrollTransform(AsyncPanZoomController::eForCompositing);
Matrix4x4 asyncUntransform = (asyncTransform * overscroll).Inverse().ToUnknownMatrix();
Matrix4x4 contentTransform = aScrollableContentTransform;
const Matrix4x4& contentTransform = aScrollableContentTransform;
Matrix4x4 contentUntransform = contentTransform.Inverse();
AsyncTransformComponentMatrix asyncCompensation =
@ -3190,6 +3210,36 @@ APZCTreeManager::ComputeTransformForScrollThumb(
return transform;
}
APZSampler*
APZCTreeManager::GetSampler() const
{
// We should always have a sampler here, since in practice the sampler
// is destroyed at the same time that this APZCTreeMAnager instance is.
MOZ_ASSERT(mSampler);
return mSampler;
}
void
APZCTreeManager::AssertOnSamplerThread()
{
GetSampler()->AssertOnSamplerThread();
}
APZUpdater*
APZCTreeManager::GetUpdater() const
{
// We should always have an updater here, since in practice the updater
// is destroyed at the same time that this APZCTreeManager instance is.
MOZ_ASSERT(mUpdater);
return mUpdater;
}
void
APZCTreeManager::AssertOnUpdaterThread()
{
GetUpdater()->AssertOnUpdaterThread();
}
void
APZCTreeManager::SetDPI(float aDpiValue)
{

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

@ -45,6 +45,8 @@ namespace layers {
class Layer;
class AsyncPanZoomController;
class APZCTreeManagerParent;
class APZSampler;
class APZUpdater;
class CompositorBridgeParent;
class OverscrollHandoffChain;
struct OverscrollHandoffState;
@ -80,7 +82,7 @@ struct ScrollThumbData;
* This class manages the tree of AsyncPanZoomController instances. There is one
* instance of this class owned by each CompositorBridgeParent, and it contains as
* many AsyncPanZoomController instances as there are scrollable container layers.
* This class generally lives on the sampler thread, although some functions
* This class generally lives on the updater thread, although some functions
* may be called from other threads as noted; thread safety is ensured internally.
*
* The bulk of the work of this class happens as part of the UpdateHitTestingTree
@ -116,13 +118,16 @@ class APZCTreeManager : public IAPZCTreeManager
public:
explicit APZCTreeManager(LayersId aRootLayersId);
void SetSampler(APZSampler* aSampler);
void SetUpdater(APZUpdater* aUpdater);
/**
* Notifies this APZCTreeManager that the associated compositor is now
* responsible for managing another layers id, which got moved over from
* some other compositor. That other compositor's APZCTreeManager is also
* provided. This allows APZCTreeManager to transfer any necessary state
* from the old APZCTreeManager related to that layers id.
* This function must be called on the sampler thread.
* This function must be called on the updater thread.
*/
void NotifyLayerTreeAdopted(LayersId aLayersId,
const RefPtr<APZCTreeManager>& aOldTreeManager);
@ -132,14 +137,14 @@ public:
* associated compositor has been removed/destroyed. Note that this does
* NOT get called during shutdown situations, when the root layer tree is
* also getting destroyed.
* This function must be called on the sampler thread.
* This function must be called on the updater thread.
*/
void NotifyLayerTreeRemoved(LayersId aLayersId);
/**
* Rebuild the focus state based on the focus target from the layer tree update
* that just occurred.
* This must be called on the sampler thread.
* This must be called on the updater thread.
*
* @param aRootLayerTreeId The layer tree ID of the root layer corresponding
* to this APZCTreeManager
@ -155,7 +160,7 @@ public:
* Preserve nodes and APZC instances where possible, but retire those whose
* layers are no longer in the layer tree.
*
* This must be called on the sampler thread as it walks the layer tree.
* This must be called on the updater thread as it walks the layer tree.
*
* @param aRootLayerTreeId The layer tree ID of the root layer corresponding
* to this APZCTreeManager
@ -333,7 +338,7 @@ public:
* lifetime of this APZCTreeManager, when this APZCTreeManager is no longer
* needed. Failing to call this function may prevent objects from being freed
* properly.
* This must be called on the sampler thread.
* This must be called on the updater thread.
*/
void ClearTree();
@ -523,10 +528,18 @@ public:
bool aScrollbarIsDescendant,
AsyncTransformComponentMatrix* aOutClipTransform);
// Assert that the current thread is the sampler thread for this APZCTM.
void AssertOnSamplerThread();
// Assert that the current thread is the updater thread for this APZCTM.
void AssertOnUpdaterThread();
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~APZCTreeManager();
APZSampler* GetSampler() const;
APZUpdater* GetUpdater() const;
// Protected hooks for gtests subclass
virtual AsyncPanZoomController* NewAPZCInstance(LayersId aLayersId,
GeckoContentController* aController);
@ -691,6 +704,19 @@ private:
/* Layers id for the root CompositorBridgeParent that owns this APZCTreeManager. */
LayersId mRootLayersId;
/* Pointer to the APZSampler instance that is bound to this APZCTreeManager.
* The sampler has a RefPtr to this class, and this non-owning raw pointer
* back to the APZSampler is nulled out in the sampler's destructor, so this
* pointer should always be valid.
*/
APZSampler* MOZ_NON_OWNING_REF mSampler;
/* Pointer to the APZUpdater instance that is bound to this APZCTreeManager.
* The updater has a RefPtr to this class, and this non-owning raw pointer
* back to the APZUpdater is nulled out in the updater's destructor, so this
* pointer should always be valid.
*/
APZUpdater* MOZ_NON_OWNING_REF mUpdater;
/* Whenever walking or mutating the tree rooted at mRootNode, mTreeLock must be held.
* This lock does not need to be held while manipulating a single APZC instance in
* isolation (that is, if its tree pointers are not being accessed or mutated). The
@ -701,7 +727,7 @@ private:
RefPtr<HitTestingTreeNode> mRootNode;
/* Holds the zoom constraints for scrollable layers, as determined by the
* the main-thread gecko code. This can only be accessed on the sampler
* the main-thread gecko code. This can only be accessed on the updater
* thread. */
std::unordered_map<ScrollableLayerGuid, ZoomConstraints, ScrollableLayerGuidHash> mZoomConstraints;
/* A list of keyboard shortcuts to use for translating keyboard inputs into

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

@ -8,8 +8,10 @@
#include "APZCTreeManager.h"
#include "AsyncPanZoomController.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/LayerMetricsWrapper.h"
#include "mozilla/layers/SynchronousTask.h"
#include "TreeTraversal.h"
namespace mozilla {
@ -18,65 +20,13 @@ namespace layers {
APZSampler::APZSampler(const RefPtr<APZCTreeManager>& aApz)
: mApz(aApz)
{
MOZ_ASSERT(aApz);
mApz->SetSampler(this);
}
APZSampler::~APZSampler()
{
}
void
APZSampler::ClearTree()
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
mApz->ClearTree();
}
void
APZSampler::UpdateFocusState(LayersId aRootLayerTreeId,
LayersId aOriginatingLayersId,
const FocusTarget& aFocusTarget)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
mApz->UpdateFocusState(aRootLayerTreeId, aOriginatingLayersId, aFocusTarget);
}
void
APZSampler::UpdateHitTestingTree(LayersId aRootLayerTreeId,
Layer* aRoot,
bool aIsFirstPaint,
LayersId aOriginatingLayersId,
uint32_t aPaintSequenceNumber)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
mApz->UpdateHitTestingTree(aRootLayerTreeId, aRoot, aIsFirstPaint,
aOriginatingLayersId, aPaintSequenceNumber);
}
void
APZSampler::UpdateHitTestingTree(LayersId aRootLayerTreeId,
const WebRenderScrollData& aScrollData,
bool aIsFirstPaint,
LayersId aOriginatingLayersId,
uint32_t aPaintSequenceNumber)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
mApz->UpdateHitTestingTree(aRootLayerTreeId, aScrollData, aIsFirstPaint,
aOriginatingLayersId, aPaintSequenceNumber);
}
void
APZSampler::NotifyLayerTreeAdopted(LayersId aLayersId,
const RefPtr<APZSampler>& aOldSampler)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
mApz->NotifyLayerTreeAdopted(aLayersId, aOldSampler ? aOldSampler->mApz : nullptr);
}
void
APZSampler::NotifyLayerTreeRemoved(LayersId aLayersId)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
mApz->NotifyLayerTreeRemoved(aLayersId);
mApz->SetSampler(nullptr);
}
bool
@ -89,47 +39,12 @@ APZSampler::PushStateToWR(wr::TransactionBuilder& aTxn,
return mApz->PushStateToWR(aTxn, aSampleTime, aTransformArray);
}
bool
APZSampler::GetAPZTestData(LayersId aLayersId,
APZTestData* aOutData)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
return mApz->GetAPZTestData(aLayersId, aOutData);
}
void
APZSampler::SetTestAsyncScrollOffset(LayersId aLayersId,
const FrameMetrics::ViewID& aScrollId,
const CSSPoint& aOffset)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RefPtr<AsyncPanZoomController> apzc = mApz->GetTargetAPZC(aLayersId, aScrollId);
if (apzc) {
apzc->SetTestAsyncScrollOffset(aOffset);
} else {
NS_WARNING("Unable to find APZC in SetTestAsyncScrollOffset");
}
}
void
APZSampler::SetTestAsyncZoom(LayersId aLayersId,
const FrameMetrics::ViewID& aScrollId,
const LayerToParentLayerScale& aZoom)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RefPtr<AsyncPanZoomController> apzc = mApz->GetTargetAPZC(aLayersId, aScrollId);
if (apzc) {
apzc->SetTestAsyncZoom(aZoom);
} else {
NS_WARNING("Unable to find APZC in SetTestAsyncZoom");
}
}
bool
APZSampler::SampleAnimations(const LayerMetricsWrapper& aLayer,
const TimeStamp& aSampleTime)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
AssertOnSamplerThread();
// TODO: eventually we can drop the aLayer argument and just walk the APZ
// tree directly in mApz.
@ -157,6 +72,8 @@ APZSampler::ComputeTransformForScrollThumb(const LayerToParentLayerMatrix4x4& aC
AsyncTransformComponentMatrix* aOutClipTransform)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
AssertOnSamplerThread();
return mApz->ComputeTransformForScrollThumb(aCurrentTransform,
aContent.GetTransform(),
aContent.GetApzc(),
@ -169,6 +86,9 @@ APZSampler::ComputeTransformForScrollThumb(const LayerToParentLayerMatrix4x4& aC
ParentLayerPoint
APZSampler::GetCurrentAsyncScrollOffset(const LayerMetricsWrapper& aLayer)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
AssertOnSamplerThread();
MOZ_ASSERT(aLayer.GetApzc());
return aLayer.GetApzc()->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForCompositing);
}
@ -176,6 +96,9 @@ APZSampler::GetCurrentAsyncScrollOffset(const LayerMetricsWrapper& aLayer)
AsyncTransform
APZSampler::GetCurrentAsyncTransform(const LayerMetricsWrapper& aLayer)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
AssertOnSamplerThread();
MOZ_ASSERT(aLayer.GetApzc());
return aLayer.GetApzc()->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing);
}
@ -183,6 +106,9 @@ APZSampler::GetCurrentAsyncTransform(const LayerMetricsWrapper& aLayer)
AsyncTransformComponentMatrix
APZSampler::GetOverscrollTransform(const LayerMetricsWrapper& aLayer)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
AssertOnSamplerThread();
MOZ_ASSERT(aLayer.GetApzc());
return aLayer.GetApzc()->GetOverscrollTransform(AsyncPanZoomController::eForCompositing);
}
@ -190,6 +116,9 @@ APZSampler::GetOverscrollTransform(const LayerMetricsWrapper& aLayer)
AsyncTransformComponentMatrix
APZSampler::GetCurrentAsyncTransformWithOverscroll(const LayerMetricsWrapper& aLayer)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
AssertOnSamplerThread();
MOZ_ASSERT(aLayer.GetApzc());
return aLayer.GetApzc()->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::eForCompositing);
}
@ -197,6 +126,9 @@ APZSampler::GetCurrentAsyncTransformWithOverscroll(const LayerMetricsWrapper& aL
void
APZSampler::MarkAsyncTransformAppliedToContent(const LayerMetricsWrapper& aLayer)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
AssertOnSamplerThread();
MOZ_ASSERT(aLayer.GetApzc());
aLayer.GetApzc()->MarkAsyncTransformAppliedToContent();
}
@ -204,11 +136,28 @@ APZSampler::MarkAsyncTransformAppliedToContent(const LayerMetricsWrapper& aLayer
bool
APZSampler::HasUnusedAsyncTransform(const LayerMetricsWrapper& aLayer)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
AssertOnSamplerThread();
AsyncPanZoomController* apzc = aLayer.GetApzc();
return apzc
&& !apzc->GetAsyncTransformAppliedToContent()
&& !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing)).IsIdentity();
}
void
APZSampler::AssertOnSamplerThread()
{
if (APZThreadUtils::GetThreadAssertionsEnabled()) {
MOZ_ASSERT(IsSamplerThread());
}
}
bool
APZSampler::IsSamplerThread()
{
return CompositorThreadHolder::IsInCompositorThread();
}
} // namespace layers
} // namespace mozilla

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

@ -0,0 +1,238 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/layers/APZUpdater.h"
#include "APZCTreeManager.h"
#include "AsyncPanZoomController.h"
#include "base/task.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/SynchronousTask.h"
#include "mozilla/layers/WebRenderScrollData.h"
namespace mozilla {
namespace layers {
APZUpdater::APZUpdater(const RefPtr<APZCTreeManager>& aApz)
: mApz(aApz)
{
MOZ_ASSERT(aApz);
mApz->SetUpdater(this);
}
APZUpdater::~APZUpdater()
{
mApz->SetUpdater(nullptr);
}
bool
APZUpdater::HasTreeManager(const RefPtr<APZCTreeManager>& aApz)
{
return aApz.get() == mApz.get();
}
void
APZUpdater::ClearTree()
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RunOnUpdaterThread(NewRunnableMethod(
"APZUpdater::ClearTree",
mApz,
&APZCTreeManager::ClearTree));
}
void
APZUpdater::UpdateFocusState(LayersId aRootLayerTreeId,
LayersId aOriginatingLayersId,
const FocusTarget& aFocusTarget)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RunOnUpdaterThread(NewRunnableMethod<LayersId, LayersId, FocusTarget>(
"APZUpdater::UpdateFocusState",
mApz,
&APZCTreeManager::UpdateFocusState,
aRootLayerTreeId,
aOriginatingLayersId,
aFocusTarget));
}
void
APZUpdater::UpdateHitTestingTree(LayersId aRootLayerTreeId,
Layer* aRoot,
bool aIsFirstPaint,
LayersId aOriginatingLayersId,
uint32_t aPaintSequenceNumber)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
AssertOnUpdaterThread();
mApz->UpdateHitTestingTree(aRootLayerTreeId, aRoot, aIsFirstPaint,
aOriginatingLayersId, aPaintSequenceNumber);
}
void
APZUpdater::UpdateHitTestingTree(LayersId aRootLayerTreeId,
const WebRenderScrollData& aScrollData,
bool aIsFirstPaint,
LayersId aOriginatingLayersId,
uint32_t aPaintSequenceNumber)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
// use the local variable to resolve the function overload.
auto func = static_cast<void (APZCTreeManager::*)(LayersId,
const WebRenderScrollData&,
bool,
LayersId,
uint32_t)>
(&APZCTreeManager::UpdateHitTestingTree);
RunOnUpdaterThread(NewRunnableMethod<LayersId,
WebRenderScrollData,
bool,
LayersId,
uint32_t>(
"APZUpdater::UpdateHitTestingTree",
mApz,
func,
aRootLayerTreeId,
aScrollData,
aIsFirstPaint,
aOriginatingLayersId,
aPaintSequenceNumber));
}
void
APZUpdater::NotifyLayerTreeAdopted(LayersId aLayersId,
const RefPtr<APZUpdater>& aOldUpdater)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RunOnUpdaterThread(NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>>(
"APZUpdater::NotifyLayerTreeAdopted",
mApz,
&APZCTreeManager::NotifyLayerTreeAdopted,
aLayersId,
aOldUpdater ? aOldUpdater->mApz : nullptr));
}
void
APZUpdater::NotifyLayerTreeRemoved(LayersId aLayersId)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RunOnUpdaterThread(NewRunnableMethod<LayersId>(
"APZUpdater::NotifyLayerTreeRemoved",
mApz,
&APZCTreeManager::NotifyLayerTreeRemoved,
aLayersId));
}
bool
APZUpdater::GetAPZTestData(LayersId aLayersId,
APZTestData* aOutData)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RefPtr<APZCTreeManager> apz = mApz;
bool ret = false;
SynchronousTask waiter("APZUpdater::GetAPZTestData");
RunOnUpdaterThread(NS_NewRunnableFunction(
"APZUpdater::GetAPZTestData",
[&]() {
AutoCompleteTask notifier(&waiter);
ret = apz->GetAPZTestData(aLayersId, aOutData);
}
));
// Wait until the task posted above has run and populated aOutData and ret
waiter.Wait();
return ret;
}
void
APZUpdater::SetTestAsyncScrollOffset(LayersId aLayersId,
const FrameMetrics::ViewID& aScrollId,
const CSSPoint& aOffset)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RefPtr<APZCTreeManager> apz = mApz;
RunOnUpdaterThread(NS_NewRunnableFunction(
"APZUpdater::SetTestAsyncScrollOffset",
[=]() {
RefPtr<AsyncPanZoomController> apzc = apz->GetTargetAPZC(aLayersId, aScrollId);
if (apzc) {
apzc->SetTestAsyncScrollOffset(aOffset);
} else {
NS_WARNING("Unable to find APZC in SetTestAsyncScrollOffset");
}
}
));
}
void
APZUpdater::SetTestAsyncZoom(LayersId aLayersId,
const FrameMetrics::ViewID& aScrollId,
const LayerToParentLayerScale& aZoom)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RefPtr<APZCTreeManager> apz = mApz;
RunOnUpdaterThread(NS_NewRunnableFunction(
"APZUpdater::SetTestAsyncZoom",
[=]() {
RefPtr<AsyncPanZoomController> apzc = apz->GetTargetAPZC(aLayersId, aScrollId);
if (apzc) {
apzc->SetTestAsyncZoom(aZoom);
} else {
NS_WARNING("Unable to find APZC in SetTestAsyncZoom");
}
}
));
}
void
APZUpdater::AssertOnUpdaterThread()
{
if (APZThreadUtils::GetThreadAssertionsEnabled()) {
MOZ_ASSERT(IsUpdaterThread());
}
}
void
APZUpdater::RunOnUpdaterThread(already_AddRefed<Runnable> aTask)
{
RefPtr<Runnable> task = aTask;
MessageLoop* loop = CompositorThreadHolder::Loop();
if (!loop) {
// Could happen during startup
NS_WARNING("Dropping task posted to updater thread");
return;
}
if (IsUpdaterThread()) {
task->Run();
} else {
loop->PostTask(task.forget());
}
}
bool
APZUpdater::IsUpdaterThread()
{
return CompositorThreadHolder::IsInCompositorThread();
}
void
APZUpdater::RunOnControllerThread(already_AddRefed<Runnable> aTask)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RunOnUpdaterThread(NewRunnableFunction(
"APZUpdater::RunOnControllerThread",
&APZThreadUtils::RunOnControllerThread,
Move(aTask)));
}
} // namespace layers
} // namespace mozilla

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

@ -26,8 +26,7 @@ class AsyncPanZoomAnimation {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomAnimation)
public:
explicit AsyncPanZoomAnimation()
{ }
explicit AsyncPanZoomAnimation() = default;
virtual bool DoSample(FrameMetrics& aFrameMetrics,
const TimeDuration& aDelta) = 0;
@ -70,8 +69,7 @@ public:
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~AsyncPanZoomAnimation()
{ }
virtual ~AsyncPanZoomAnimation() = default;
/**
* Tasks scheduled for execution after the APZC's mMonitor is released.

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

@ -550,14 +550,18 @@ private:
class ZoomAnimation: public AsyncPanZoomAnimation {
public:
ZoomAnimation(CSSPoint aStartOffset, CSSToParentLayerScale2D aStartZoom,
CSSPoint aEndOffset, CSSToParentLayerScale2D aEndZoom)
: mTotalDuration(TimeDuration::FromMilliseconds(gfxPrefs::APZZoomAnimationDuration()))
ZoomAnimation(const CSSPoint& aStartOffset,
const CSSToParentLayerScale2D& aStartZoom,
const CSSPoint& aEndOffset,
const CSSToParentLayerScale2D& aEndZoom)
: mTotalDuration(
TimeDuration::FromMilliseconds(gfxPrefs::APZZoomAnimationDuration()))
, mStartOffset(aStartOffset)
, mStartZoom(aStartZoom)
, mEndOffset(aEndOffset)
, mEndZoom(aEndZoom)
{}
{
}
virtual bool DoSample(FrameMetrics& aFrameMetrics,
const TimeDuration& aDelta) override
@ -844,7 +848,7 @@ AsyncPanZoomController::GetInputQueue() const {
void
AsyncPanZoomController::Destroy()
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnUpdaterThread();
CancelAnimation(CancelAnimationFlags::ScrollSnap);
@ -1118,7 +1122,7 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent,
break;
}
case KEYBOARD_INPUT: {
KeyboardInput keyInput = aEvent.AsKeyboardInput();
const KeyboardInput& keyInput = aEvent.AsKeyboardInput();
rv = OnKeyboard(keyInput);
break;
}
@ -1412,7 +1416,7 @@ nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEve
nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
APZC_LOG("%p got a scale in state %d\n", this, mState);
if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
return nsEventStatus_eIgnore;
}
@ -3477,7 +3481,7 @@ AsyncPanZoomController::RequestContentRepaint(const FrameMetrics& aFrameMetrics,
bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
nsTArray<RefPtr<Runnable>>* aOutDeferredTasks)
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnSamplerThread();
// This function may get called multiple with the same sample time, because
// there may be multiple layers with this APZC, and each layer invokes this
@ -3536,7 +3540,7 @@ AsyncPanZoomController::GetOverscrollTransform(AsyncTransformConsumer aMode) con
bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnSamplerThread();
// Don't send any state-change notifications until the end of the function,
// because we may go through some intermediate states while we finish
@ -3815,7 +3819,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe
bool aIsFirstPaint,
bool aThisLayerTreeUpdated)
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnUpdaterThread();
RecursiveMutexAutoLock lock(mRecursiveMutex);
bool isDefault = mScrollMetadata.IsDefault();
@ -4079,6 +4083,22 @@ const ScrollMetadata& AsyncPanZoomController::GetScrollMetadata() const {
return mScrollMetadata;
}
void
AsyncPanZoomController::AssertOnSamplerThread() const
{
if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
treeManagerLocal->AssertOnSamplerThread();
}
}
void
AsyncPanZoomController::AssertOnUpdaterThread() const
{
if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
treeManagerLocal->AssertOnUpdaterThread();
}
}
APZCTreeManager* AsyncPanZoomController::GetApzcTreeManager() const {
mRecursiveMutex.AssertNotCurrentThreadIn();
return mTreeManager;
@ -4433,7 +4453,7 @@ void AsyncPanZoomController::UpdateSharedCompositorFrameMetrics()
void AsyncPanZoomController::ShareCompositorFrameMetrics()
{
APZThreadUtils::AssertOnSamplerThread();
AssertOnUpdaterThread();
// Only create the shared memory buffer if it hasn't already been created,
// we are using progressive tile painting, and we have a

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

@ -67,15 +67,15 @@ struct KeyboardScrollAction;
// Base class for grouping platform-specific APZC state variables.
class PlatformSpecificStateBase {
public:
virtual ~PlatformSpecificStateBase() {}
virtual ~PlatformSpecificStateBase() = default;
virtual AndroidSpecificState* AsAndroidSpecificState() { return nullptr; }
};
/*
* Represents a transform from the ParentLayer coordinate space of an APZC
* to the ParentLayer coordinate space of its parent APZC.
* Each layer along the way contributes to the transform. We track
* contributions that are perspective transforms separately, as sometimes
* Each layer along the way contributes to the transform. We track
* contributions that are perspective transforms separately, as sometimes
* these require special handling.
*/
struct AncestorTransform {
@ -240,6 +240,10 @@ public:
bool UpdateAnimation(const TimeStamp& aSampleTime,
nsTArray<RefPtr<Runnable>>* aOutDeferredTasks);
// --------------------------------------------------------------------------
// These methods must only be called on the updater thread.
//
/**
* A shadow layer update has arrived. |aScrollMetdata| is the new ScrollMetadata
* for the container layer corresponding to this APZC.
@ -753,6 +757,9 @@ protected:
*/
APZCTreeManager* GetApzcTreeManager() const;
void AssertOnSamplerThread() const;
void AssertOnUpdaterThread() const;
/**
* Convert ScreenPoint relative to the screen to LayoutDevicePoint relative
* to the parent document. This excludes the transient compositor transform.
@ -792,7 +799,7 @@ protected:
/* Access to the following two fields is protected by the mRefPtrMonitor,
since they are accessed on the UI thread but can be cleared on the
sampler thread. */
updater thread. */
RefPtr<GeckoContentController> mGeckoContentController;
RefPtr<GestureEventListener> mGestureEventListener;
mutable Monitor mRefPtrMonitor;

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

@ -381,9 +381,9 @@ bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta,
// actually see any changes.
mVelocity = 0.0f;
return false;
} else {
mVelocity *= pow(1.0f - aFriction, float(aDelta.ToMilliseconds()));
}
mVelocity *= pow(1.0f - aFriction, float(aDelta.ToMilliseconds()));
AXIS_LOG("%p|%s reduced velocity to %f due to friction\n",
mAsyncPanZoomController, Name(), mVelocity);
return true;

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

@ -159,8 +159,8 @@ CheckerboardEvent::StartEvent()
}
MonitorAutoLock lock(mRendertraceLock);
std::vector<PropertyValue> history;
for (size_t i = 0; i < sRendertracePropertyCount; i++) {
mBufferedProperties[i].Flush(history, lock);
for (PropertyBuffer& bufferedProperty : mBufferedProperties) {
bufferedProperty.Flush(history, lock);
}
std::sort(history.begin(), history.end());
for (const PropertyValue& p : history) {

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

@ -70,7 +70,9 @@ FocusState::Update(LayersId aRootLayerTreeId,
LayersId aOriginatingLayersId,
const FocusTarget& aState)
{
APZThreadUtils::AssertOnSamplerThread();
// This runs on the updater thread, it's not worth passing around extra raw
// pointers just to assert it.
MutexAutoLock lock(mMutex);
FS_LOG("Update with rlt=%" PRIu64 ", olt=%" PRIu64 ", ft=(%s, %" PRIu64 ")\n",
@ -182,7 +184,8 @@ FocusState::Update(LayersId aRootLayerTreeId,
void
FocusState::RemoveFocusTarget(LayersId aLayersId)
{
APZThreadUtils::AssertOnSamplerThread();
// This runs on the updater thread, it's not worth passing around extra raw
// pointers just to assert it.
MutexAutoLock lock(mMutex);
mFocusTree.erase(aLayersId);

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

@ -138,7 +138,7 @@ private:
private:
// All methods should hold this lock, since this class is accessed via both
// the sampler and controller threads.
// the updater and controller threads.
mutable Mutex mMutex;
// The set of focus targets received indexed by their layer tree ID

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

@ -30,7 +30,9 @@ GenericScrollAnimation::GenericScrollAnimation(AsyncPanZoomController& aApzc,
}
void
GenericScrollAnimation::UpdateDelta(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity)
GenericScrollAnimation::UpdateDelta(TimeStamp aTime,
const nsPoint& aDelta,
const nsSize& aCurrentVelocity)
{
mFinalDestination += aDelta;
@ -38,7 +40,9 @@ GenericScrollAnimation::UpdateDelta(TimeStamp aTime, nsPoint aDelta, const nsSiz
}
void
GenericScrollAnimation::UpdateDestination(TimeStamp aTime, nsPoint aDestination, const nsSize& aCurrentVelocity)
GenericScrollAnimation::UpdateDestination(TimeStamp aTime,
const nsPoint& aDestination,
const nsSize& aCurrentVelocity)
{
mFinalDestination = aDestination;

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

@ -28,8 +28,12 @@ public:
bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override;
void UpdateDelta(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity);
void UpdateDestination(TimeStamp aTime, nsPoint aDestination, const nsSize& aCurrentVelocity);
void UpdateDelta(TimeStamp aTime,
const nsPoint& aDelta,
const nsSize& aCurrentVelocity);
void UpdateDestination(TimeStamp aTime,
const nsPoint& aDestination,
const nsSize& aCurrentVelocity);
CSSPoint GetDestination() const {
return CSSPoint::FromAppUnits(mFinalDestination);

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

@ -81,9 +81,7 @@ GestureEventListener::GestureEventListener(AsyncPanZoomController* aAsyncPanZoom
{
}
GestureEventListener::~GestureEventListener()
{
}
GestureEventListener::~GestureEventListener() = default;
nsEventStatus GestureEventListener::HandleInputEvent(const MultiTouchInput& aEvent)
{

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

@ -10,7 +10,6 @@
#include "gfxPrefs.h"
#include "LayersLogging.h" // for Stringify
#include "mozilla/gfx/Point.h" // for Point4D
#include "mozilla/layers/APZThreadUtils.h" // for AssertOnSamplerThread
#include "mozilla/layers/APZUtils.h" // for CompleteAsyncTransform
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform::operator Matrix4x4()
#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
@ -31,6 +30,7 @@ HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
, mScrollViewId(FrameMetrics::NULL_SCROLL_ID)
, mScrollbarAnimationId(0)
, mFixedPosTarget(FrameMetrics::NULL_SCROLL_ID)
, mIsBackfaceHidden(false)
, mOverride(EventRegionsOverride::NoOverride)
{
if (mIsPrimaryApzcHolder) {
@ -52,14 +52,13 @@ HitTestingTreeNode::RecycleWith(AsyncPanZoomController* aApzc,
// fields.
}
HitTestingTreeNode::~HitTestingTreeNode()
{
}
HitTestingTreeNode::~HitTestingTreeNode() = default;
void
HitTestingTreeNode::Destroy()
{
APZThreadUtils::AssertOnSamplerThread();
// This runs on the updater thread, it's not worth passing around extra raw
// pointers just to assert it.
mPrevSibling = nullptr;
mLastChild = nullptr;
@ -263,13 +262,15 @@ HitTestingTreeNode::SetHitTestData(const EventRegions& aRegions,
const LayerIntRegion& aVisibleRegion,
const CSSTransformMatrix& aTransform,
const Maybe<ParentLayerIntRegion>& aClipRegion,
const EventRegionsOverride& aOverride)
const EventRegionsOverride& aOverride,
bool aIsBackfaceHidden)
{
mEventRegions = aRegions;
mVisibleRegion = aVisibleRegion;
mTransform = aTransform;
mClipRegion = aClipRegion;
mOverride = aOverride;
mIsBackfaceHidden = aIsBackfaceHidden;
}
bool
@ -301,6 +302,13 @@ HitTestingTreeNode::HitTest(const LayerPoint& aPoint) const
auto point = LayerIntPoint::Round(aPoint);
// If the layer's backface is showing and it's hidden, don't hit it.
// This matches the behavior of main-thread hit testing in
// nsDisplayTransform::HitTest().
if (mIsBackfaceHidden) {
return result;
}
// test against event regions in Layer coordinate space
if (!mEventRegions.mHitRegion.Contains(point.x, point.y)) {
return result;

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

@ -89,7 +89,8 @@ public:
const LayerIntRegion& aVisibleRegion,
const CSSTransformMatrix& aTransform,
const Maybe<ParentLayerIntRegion>& aClipRegion,
const EventRegionsOverride& aOverride);
const EventRegionsOverride& aOverride,
bool aIsBackfaceHidden);
bool IsOutsideClip(const ParentLayerPoint& aPoint) const;
/* Scrollbar info */
@ -172,6 +173,14 @@ private:
* transforms. */
CSSTransformMatrix mTransform;
/* Whether layer L is backface-visibility:hidden, and its backface is
* currently visible. It's true that the latter depends on the layer's
* shadow transform, but the sorts of changes APZ makes to the shadow
* transform shouldn't change the backface from hidden to visible or
* vice versa, so it's sufficient to record this at hit test tree
* building time. */
bool mIsBackfaceHidden;
/* This is clip rect for L that we wish to use for hit-testing purposes. Note
* that this may not be exactly the same as the clip rect on layer L because
* of the touch-sensitive region provided by the GeckoContentController, or

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

@ -11,7 +11,7 @@
#include "mozilla/RefCounted.h" // for RefCounted
#include "mozilla/RefPtr.h" // for RefPtr
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
#include "mozilla/layers/APZUtils.h"
#include "mozilla/layers/APZUtils.h"
#include "mozilla/layers/LayersTypes.h" // for TouchBehaviorFlags
#include "mozilla/layers/AsyncDragMetrics.h"
#include "mozilla/TimeStamp.h" // for TimeStamp
@ -52,8 +52,7 @@ public:
explicit InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
TargetConfirmationFlags aFlags);
virtual ~InputBlockState()
{}
virtual ~InputBlockState() = default;
virtual CancelableBlockState* AsCancelableBlock() {
return nullptr;

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

@ -20,9 +20,7 @@
namespace mozilla {
namespace layers {
InputQueue::InputQueue()
{
}
InputQueue::InputQueue() = default;
InputQueue::~InputQueue() {
mQueuedInputs.Clear();

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

@ -11,9 +11,7 @@
namespace mozilla {
namespace layers {
KeyboardShortcut::KeyboardShortcut()
{
}
KeyboardShortcut::KeyboardShortcut() = default;
KeyboardShortcut::KeyboardShortcut(KeyboardInput::KeyboardEventType aEventType,
uint32_t aKeyCode,
@ -130,9 +128,7 @@ KeyboardMap::KeyboardMap(nsTArray<KeyboardShortcut>&& aShortcuts)
{
}
KeyboardMap::KeyboardMap()
{
}
KeyboardMap::KeyboardMap() = default;
Maybe<KeyboardShortcut>
KeyboardMap::FindMatch(const KeyboardInput& aEvent) const

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

@ -69,7 +69,7 @@ private:
// Base class for different overscroll effects;
class OverscrollEffectBase {
public:
virtual ~OverscrollEffectBase() {}
virtual ~OverscrollEffectBase() = default;
virtual void ConsumeOverscroll(ParentLayerPoint& aOverscroll,
bool aShouldOverscrollX,
bool aShouldOverscrollY) = 0;

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

@ -13,7 +13,7 @@
namespace mozilla {
namespace layers {
OverscrollHandoffChain::~OverscrollHandoffChain() {}
OverscrollHandoffChain::~OverscrollHandoffChain() = default;
void
OverscrollHandoffChain::Add(AsyncPanZoomController* aApzc)

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

@ -13,6 +13,8 @@
#include "APZTestCommon.h"
#include "gfxPrefs.h"
#include "mozilla/layers/APZSampler.h"
#include "mozilla/layers/APZUpdater.h"
class APZCBasicTester : public APZCTesterBase {
public:
@ -29,6 +31,8 @@ protected:
APZThreadUtils::SetControllerThread(MessageLoop::current());
tm = new TestAPZCTreeManager(mcc);
updater = new APZUpdater(tm);
sampler = new APZSampler(tm);
apzc = new TestAsyncPanZoomController(LayersId{0}, mcc, tm, mGestureBehavior);
apzc->SetFrameMetrics(TestFrameMetrics());
apzc->GetScrollMetadata().SetIsLayersIdRoot(true);
@ -115,6 +119,8 @@ protected:
AsyncPanZoomController::GestureBehavior mGestureBehavior;
RefPtr<TestAPZCTreeManager> tm;
RefPtr<APZSampler> sampler;
RefPtr<APZUpdater> updater;
RefPtr<TestAsyncPanZoomController> apzc;
};

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

@ -15,6 +15,8 @@
#include "APZTestCommon.h"
#include "gfxPlatform.h"
#include "gfxPrefs.h"
#include "mozilla/layers/APZSampler.h"
#include "mozilla/layers/APZUpdater.h"
class APZCTreeManagerTester : public APZCTesterBase {
protected:
@ -25,6 +27,8 @@ protected:
APZThreadUtils::SetControllerThread(MessageLoop::current());
manager = new TestAPZCTreeManager(mcc);
updater = new APZUpdater(manager);
sampler = new APZSampler(manager);
}
virtual void TearDown() {
@ -54,6 +58,8 @@ protected:
RefPtr<Layer> root;
RefPtr<TestAPZCTreeManager> manager;
RefPtr<APZSampler> sampler;
RefPtr<APZUpdater> updater;
protected:
static ScrollMetadata BuildScrollMetadata(FrameMetrics::ViewID aScrollId,

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

@ -522,6 +522,12 @@ function getHitTestConfig() {
return window.hitTestConfig;
}
// Compute the coordinates of the center of the given element.
function centerOf(element) {
var bounds = element.getBoundingClientRect();
return { x: bounds.x + (bounds.width / 2), y: bounds.y + (bounds.height / 2) };
}
// Peform a compositor hit test at the given point and return the result.
// The returned object has two fields:
// hitInfo: a combination of APZHitResultFlags

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

@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<head>
<title>APZ hit-testing with backface-visibility:hidden</title>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<meta name="viewport" content="width=device-width"/>
<style>
body,html{
height: 100%;
}
body{
margin: 0;
transform-style: preserve-3d;
}
#back, #front{
backface-visibility: hidden;
position: absolute;
width: 100%;
height: 100%
}
#front{
overflow-y:auto;
}
#content{
width: 100%;
height: 200%;
background: linear-gradient(blue, green);
}
#back{
transform: rotateY(180deg);
}
</style>
</head>
<body>
<div id="front">
<div id="content"></div>
</div>
<div id="back"></div></body>
<script type="application/javascript">
function* test(testDriver) {
var config = getHitTestConfig();
if (config.isWebRender) {
ok(true, "This test is only enabled for non-WebRender");
subtestDone();
return;
}
var subframe = document.getElementById('front');
var subframeViewId = config.utils.getViewId(subframe);
var {hitInfo, scrollId} = hitTest(centerOf(subframe));
is(scrollId, subframeViewId,
"hit the scroll frame behind the backface-visibility:hidden element");
subtestDone();
}
waitUntilApzStable().then(runContinuation(test));
</script>
</html>

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

@ -17,11 +17,6 @@
</body>
<script type="application/javascript">
function centerOf(element) {
var bounds = element.getBoundingClientRect();
return { x: bounds.x + (bounds.width / 2), y: bounds.y + (bounds.height / 2) };
}
function* test(testDriver) {
var config = getHitTestConfig();
var utils = config.utils;

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

@ -17,11 +17,6 @@
</body>
<script type="application/javascript">
function centerOf(element) {
var bounds = element.getBoundingClientRect();
return { x: bounds.x + (bounds.width / 2), y: bounds.y + (bounds.height / 2) };
}
function* test(testDriver) {
var config = getHitTestConfig();
var utils = config.utils;

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

@ -1,7 +1,7 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Various tests to exercise the APZ hit-testing codepaths</title>
<title>APZ hit-testing with floated subframe</title>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>

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

@ -21,6 +21,7 @@
helper_iframe_pan.html
helper_iframe1.html
helper_iframe2.html
helper_hittest_backface_hidden.html
helper_hittest_basic.html
helper_hittest_checkerboard.html
helper_hittest_subframe_float.html

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

@ -30,6 +30,7 @@ var subtests = [
{'file': 'helper_hittest_basic.html', 'prefs': prefs},
{'file': 'helper_hittest_subframe_float.html', 'prefs': prefs},
{'file': 'helper_hittest_checkerboard.html', 'prefs': prefs},
{'file': 'helper_hittest_backface_hidden.html', 'prefs': prefs}
];
if (isApzEnabled()) {

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

@ -15,6 +15,7 @@
#include "nsTArray.h"
#include "mozilla/Assertions.h" // for MOZ_ASSERT
#include "mozilla/DebugOnly.h" // for DebugOnly
#include "mozilla/GfxMessageUtils.h" // for ParamTraits specializations
#include "mozilla/ToString.h" // for ToString
#include "mozilla/gfx/CompositorHitTestInfo.h"
#include "ipc/IPCMessageUtils.h"

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

@ -6,8 +6,6 @@
#include "mozilla/layers/APZThreadUtils.h"
#include "mozilla/layers/CompositorThread.h"
namespace mozilla {
namespace layers {
@ -66,39 +64,6 @@ APZThreadUtils::IsControllerThread()
return sControllerThread == MessageLoop::current();
}
/*static*/ void
APZThreadUtils::AssertOnSamplerThread()
{
if (GetThreadAssertionsEnabled()) {
MOZ_ASSERT(IsSamplerThread());
}
}
/*static*/ void
APZThreadUtils::RunOnSamplerThread(already_AddRefed<Runnable> aTask)
{
RefPtr<Runnable> task = aTask;
MessageLoop* loop = CompositorThreadHolder::Loop();
if (!loop) {
// Could happen during startup
NS_WARNING("Dropping task posted to sampler thread");
return;
}
if (IsSamplerThread()) {
task->Run();
} else {
loop->PostTask(task.forget());
}
}
/*static*/ bool
APZThreadUtils::IsSamplerThread()
{
return CompositorThreadHolder::IsInCompositorThread();
}
NS_IMPL_ISUPPORTS(GenericNamedTimerCallbackBase, nsITimerCallback, nsINamed)
} // namespace layers

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

@ -51,25 +51,6 @@ public:
* Returns true if currently on APZ "controller thread".
*/
static bool IsControllerThread();
/**
* This can be used to assert that the current thread is the
* sampler thread (which samples the async transform).
* This does nothing if thread assertions are disabled.
*/
static void AssertOnSamplerThread();
/**
* Runs the given task on the APZ "sampler thread" for this platform. If
* this function is called from the sampler thread itself then the task is
* run immediately without getting queued.
*/
static void RunOnSamplerThread(already_AddRefed<Runnable> aTask);
/**
* Returns true if currently on the APZ "sampler thread".
*/
static bool IsSamplerThread();
};
// A base class for GenericNamedTimerCallback<Function>.

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

@ -8,15 +8,21 @@
#include "apz/src/APZCTreeManager.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "mozilla/layers/APZUpdater.h"
namespace mozilla {
namespace layers {
APZCTreeManagerParent::APZCTreeManagerParent(LayersId aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager)
APZCTreeManagerParent::APZCTreeManagerParent(LayersId aLayersId,
RefPtr<APZCTreeManager> aAPZCTreeManager,
RefPtr<APZUpdater> aAPZUpdater)
: mLayersId(aLayersId)
, mTreeManager(aAPZCTreeManager)
, mTreeManager(Move(aAPZCTreeManager))
, mUpdater(Move(aAPZUpdater))
{
MOZ_ASSERT(aAPZCTreeManager != nullptr);
MOZ_ASSERT(mTreeManager != nullptr);
MOZ_ASSERT(mUpdater != nullptr);
MOZ_ASSERT(mUpdater->HasTreeManager(mTreeManager));
}
APZCTreeManagerParent::~APZCTreeManagerParent()
@ -24,16 +30,20 @@ APZCTreeManagerParent::~APZCTreeManagerParent()
}
void
APZCTreeManagerParent::ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager)
APZCTreeManagerParent::ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager,
RefPtr<APZUpdater> aAPZUpdater)
{
MOZ_ASSERT(aAPZCTreeManager != nullptr);
mTreeManager = aAPZCTreeManager;
MOZ_ASSERT(aAPZUpdater != nullptr);
MOZ_ASSERT(aAPZUpdater->HasTreeManager(aAPZCTreeManager));
mTreeManager = Move(aAPZCTreeManager);
mUpdater = Move(aAPZUpdater);
}
mozilla::ipc::IPCResult
APZCTreeManagerParent::RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap)
{
APZThreadUtils::RunOnControllerThread(NewRunnableMethod<KeyboardMap>(
mUpdater->RunOnControllerThread(NewRunnableMethod<KeyboardMap>(
"layers::IAPZCTreeManager::SetKeyboardMap",
mTreeManager,
&IAPZCTreeManager::SetKeyboardMap,
@ -54,7 +64,7 @@ APZCTreeManagerParent::RecvZoomToRect(
return IPC_FAIL_NO_REASON(this);
}
APZThreadUtils::RunOnControllerThread(
mUpdater->RunOnControllerThread(
NewRunnableMethod<ScrollableLayerGuid, CSSRect, uint32_t>(
"layers::IAPZCTreeManager::ZoomToRect",
mTreeManager,
@ -68,7 +78,7 @@ APZCTreeManagerParent::RecvContentReceivedInputBlock(
const uint64_t& aInputBlockId,
const bool& aPreventDefault)
{
APZThreadUtils::RunOnControllerThread(NewRunnableMethod<uint64_t, bool>(
mUpdater->RunOnControllerThread(NewRunnableMethod<uint64_t, bool>(
"layers::IAPZCTreeManager::ContentReceivedInputBlock",
mTreeManager,
&IAPZCTreeManager::ContentReceivedInputBlock,
@ -90,7 +100,7 @@ APZCTreeManagerParent::RecvSetTargetAPZC(
return IPC_FAIL_NO_REASON(this);
}
}
APZThreadUtils::RunOnControllerThread(
mUpdater->RunOnControllerThread(
NewRunnableMethod<uint64_t,
StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
"layers::IAPZCTreeManager::SetTargetAPZC",
@ -120,7 +130,7 @@ APZCTreeManagerParent::RecvUpdateZoomConstraints(
mozilla::ipc::IPCResult
APZCTreeManagerParent::RecvSetDPI(const float& aDpiValue)
{
APZThreadUtils::RunOnControllerThread(NewRunnableMethod<float>(
mUpdater->RunOnControllerThread(NewRunnableMethod<float>(
"layers::IAPZCTreeManager::SetDPI",
mTreeManager,
&IAPZCTreeManager::SetDPI,
@ -133,7 +143,7 @@ APZCTreeManagerParent::RecvSetAllowedTouchBehavior(
const uint64_t& aInputBlockId,
nsTArray<TouchBehaviorFlags>&& aValues)
{
APZThreadUtils::RunOnControllerThread(
mUpdater->RunOnControllerThread(
NewRunnableMethod<uint64_t,
StoreCopyPassByRRef<nsTArray<TouchBehaviorFlags>>>(
"layers::IAPZCTreeManager::SetAllowedTouchBehavior",
@ -156,7 +166,7 @@ APZCTreeManagerParent::RecvStartScrollbarDrag(
return IPC_FAIL_NO_REASON(this);
}
APZThreadUtils::RunOnControllerThread(
mUpdater->RunOnControllerThread(
NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
"layers::IAPZCTreeManager::StartScrollbarDrag",
mTreeManager,
@ -179,7 +189,7 @@ APZCTreeManagerParent::RecvStartAutoscroll(
// mLayersId stores the parent process's layers id, while nsBaseWidget is
// sending the child process's layers id).
APZThreadUtils::RunOnControllerThread(
mUpdater->RunOnControllerThread(
NewRunnableMethod<ScrollableLayerGuid, ScreenPoint>(
"layers::IAPZCTreeManager::StartAutoscroll",
mTreeManager,
@ -194,7 +204,7 @@ APZCTreeManagerParent::RecvStopAutoscroll(const ScrollableLayerGuid& aGuid)
{
// See RecvStartAutoscroll() for why we don't check the layers id.
APZThreadUtils::RunOnControllerThread(
mUpdater->RunOnControllerThread(
NewRunnableMethod<ScrollableLayerGuid>(
"layers::IAPZCTreeManager::StopAutoscroll",
mTreeManager,
@ -205,9 +215,15 @@ APZCTreeManagerParent::RecvStopAutoscroll(const ScrollableLayerGuid& aGuid)
}
mozilla::ipc::IPCResult
APZCTreeManagerParent::RecvSetLongTapEnabled(const bool& aTapGestureEnabled)
APZCTreeManagerParent::RecvSetLongTapEnabled(const bool& aLongTapEnabled)
{
mTreeManager->SetLongTapEnabled(aTapGestureEnabled);
mUpdater->RunOnControllerThread(
NewRunnableMethod<bool>(
"layers::IAPZCTreeManager::SetLongTapEnabled",
mTreeManager,
&IAPZCTreeManager::SetLongTapEnabled,
aLongTapEnabled));
return IPC_OK();
}

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

@ -13,13 +13,16 @@ namespace mozilla {
namespace layers {
class APZCTreeManager;
class APZUpdater;
class APZCTreeManagerParent
: public PAPZCTreeManagerParent
{
public:
explicit APZCTreeManagerParent(LayersId aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager);
APZCTreeManagerParent(LayersId aLayersId,
RefPtr<APZCTreeManager> aAPZCTreeManager,
RefPtr<APZUpdater> mAPZUpdater);
virtual ~APZCTreeManagerParent();
LayersId GetLayersId() const { return mLayersId; }
@ -28,7 +31,8 @@ public:
* Called when the layer tree that this protocol is connected to
* is adopted by another compositor, and we need to switch APZCTreeManagers.
*/
void ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager);
void ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager,
RefPtr<APZUpdater> aAPZUpdater);
mozilla::ipc::IPCResult
RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap) override;
@ -84,6 +88,7 @@ public:
private:
LayersId mLayersId;
RefPtr<APZCTreeManager> mTreeManager;
RefPtr<APZUpdater> mUpdater;
};
} // namespace layers

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

@ -43,6 +43,7 @@
#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
#include "mozilla/layers/APZSampler.h" // for APZSampler
#include "mozilla/layers/APZThreadUtils.h" // for APZThreadUtils
#include "mozilla/layers/APZUpdater.h" // for APZUpdater
#include "mozilla/layers/AsyncCompositionManager.h"
#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
#include "mozilla/layers/Compositor.h" // for Compositor
@ -382,8 +383,10 @@ CompositorBridgeParent::Initialize()
if (mOptions.UseAPZ()) {
MOZ_ASSERT(!mApzcTreeManager);
MOZ_ASSERT(!mApzSampler);
MOZ_ASSERT(!mApzUpdater);
mApzcTreeManager = new APZCTreeManager(mRootLayerTreeID);
mApzSampler = new APZSampler(mApzcTreeManager);
mApzUpdater = new APZUpdater(mApzcTreeManager);
}
mCompositorBridgeID = 0;
@ -634,9 +637,11 @@ CompositorBridgeParent::ActorDestroy(ActorDestroyReason why)
mCompositionManager = nullptr;
MOZ_ASSERT((mApzSampler != nullptr) == (mApzcTreeManager != nullptr));
if (mApzSampler) {
mApzSampler->ClearTree();
MOZ_ASSERT((mApzUpdater != nullptr) == (mApzcTreeManager != nullptr));
if (mApzUpdater) {
mApzSampler = nullptr;
mApzUpdater->ClearTree();
mApzUpdater = nullptr;
mApzcTreeManager = nullptr;
}
@ -860,10 +865,10 @@ CompositorBridgeParent::NotifyShadowTreeTransaction(LayersId aId, bool aIsFirstP
}
#endif
if (mApzSampler) {
mApzSampler->UpdateFocusState(mRootLayerTreeID, aId, aFocusTarget);
if (mApzUpdater) {
mApzUpdater->UpdateFocusState(mRootLayerTreeID, aId, aFocusTarget);
if (aHitTestUpdate) {
mApzSampler->UpdateHitTestingTree(mRootLayerTreeID,
mApzUpdater->UpdateHitTestingTree(mRootLayerTreeID,
mLayerManager->GetRoot(), aIsFirstPaint, aId, aPaintSequenceNumber);
}
}
@ -1097,8 +1102,9 @@ CompositorBridgeParent::AllocPAPZCTreeManagerParent(const LayersId& aLayersId)
MOZ_ASSERT(XRE_IsGPUProcess());
// We should only ever get this if APZ is enabled in this compositor.
MOZ_ASSERT(mOptions.UseAPZ());
// The mApzcTreeManager should have been created via RecvInitialize()
// The mApzcTreeManager and mApzUpdater should have been created via RecvInitialize()
MOZ_ASSERT(mApzcTreeManager);
MOZ_ASSERT(mApzUpdater);
// The main process should pass in 0 because we assume mRootLayerTreeID
MOZ_ASSERT(!aLayersId.IsValid());
@ -1106,7 +1112,7 @@ CompositorBridgeParent::AllocPAPZCTreeManagerParent(const LayersId& aLayersId)
CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID];
MOZ_ASSERT(state.mParent.get() == this);
MOZ_ASSERT(!state.mApzcTreeManagerParent);
state.mApzcTreeManagerParent = new APZCTreeManagerParent(mRootLayerTreeID, mApzcTreeManager);
state.mApzcTreeManagerParent = new APZCTreeManagerParent(mRootLayerTreeID, mApzcTreeManager, mApzUpdater);
return state.mApzcTreeManagerParent;
}
@ -1125,8 +1131,9 @@ CompositorBridgeParent::AllocateAPZCTreeManagerParent(const MonitorAutoLock& aPr
{
MOZ_ASSERT(aState.mParent == this);
MOZ_ASSERT(mApzcTreeManager);
MOZ_ASSERT(mApzUpdater);
MOZ_ASSERT(!aState.mApzcTreeManagerParent);
aState.mApzcTreeManagerParent = new APZCTreeManagerParent(aLayersId, mApzcTreeManager);
aState.mApzcTreeManagerParent = new APZCTreeManagerParent(aLayersId, mApzcTreeManager, mApzUpdater);
}
PAPZParent*
@ -1171,6 +1178,12 @@ CompositorBridgeParent::GetAPZSampler()
return mApzSampler;
}
RefPtr<APZUpdater>
CompositorBridgeParent::GetAPZUpdater()
{
return mApzUpdater;
}
CompositorBridgeParent*
CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(const LayersId& aLayersId)
{
@ -1229,15 +1242,15 @@ CompositorBridgeParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
Layer* root = aLayerTree->GetRoot();
mLayerManager->SetRoot(root);
if (mApzSampler && !aInfo.isRepeatTransaction()) {
mApzSampler->UpdateFocusState(mRootLayerTreeID,
if (mApzUpdater && !aInfo.isRepeatTransaction()) {
mApzUpdater->UpdateFocusState(mRootLayerTreeID,
mRootLayerTreeID,
aInfo.focusTarget());
if (aHitTestUpdate) {
AutoResolveRefLayers resolve(mCompositionManager);
mApzSampler->UpdateHitTestingTree(
mApzUpdater->UpdateHitTestingTree(
mRootLayerTreeID, root, aInfo.isFirstPaint(),
mRootLayerTreeID, aInfo.paintSequenceNumber());
}
@ -1358,9 +1371,9 @@ CompositorBridgeParent::SetTestAsyncScrollOffset(
const FrameMetrics::ViewID& aScrollId,
const CSSPoint& aPoint)
{
if (mApzSampler) {
if (mApzUpdater) {
MOZ_ASSERT(aLayersId.IsValid());
mApzSampler->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint);
mApzUpdater->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint);
}
}
@ -1370,9 +1383,9 @@ CompositorBridgeParent::SetTestAsyncZoom(
const FrameMetrics::ViewID& aScrollId,
const LayerToParentLayerScale& aZoom)
{
if (mApzSampler) {
if (mApzUpdater) {
MOZ_ASSERT(aLayersId.IsValid());
mApzSampler->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
mApzUpdater->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
}
}
@ -1391,9 +1404,9 @@ void
CompositorBridgeParent::GetAPZTestData(const LayersId& aLayersId,
APZTestData* aOutData)
{
if (mApzSampler) {
if (mApzUpdater) {
MOZ_ASSERT(aLayersId.IsValid());
mApzSampler->GetAPZTestData(aLayersId, aOutData);
mApzUpdater->GetAPZTestData(aLayersId, aOutData);
}
}
@ -1682,7 +1695,7 @@ CompositorBridgeParent::RecvMapAndNotifyChildCreated(const LayersId& aChild,
mozilla::ipc::IPCResult
CompositorBridgeParent::RecvAdoptChild(const LayersId& child)
{
RefPtr<APZSampler> oldApzSampler;
RefPtr<APZUpdater> oldApzUpdater;
APZCTreeManagerParent* parent;
{
MonitorAutoLock lock(*sIndirectLayerTreesLock);
@ -1690,7 +1703,7 @@ CompositorBridgeParent::RecvAdoptChild(const LayersId& child)
// We currently don't support adopting children from one compositor to
// another if the two compositors don't have the same options.
MOZ_ASSERT(sIndirectLayerTrees[child].mParent->mOptions == mOptions);
oldApzSampler = sIndirectLayerTrees[child].mParent->mApzSampler;
oldApzUpdater = sIndirectLayerTrees[child].mParent->mApzUpdater;
}
NotifyChildCreated(child);
if (sIndirectLayerTrees[child].mLayerTree) {
@ -1716,21 +1729,21 @@ CompositorBridgeParent::RecvAdoptChild(const LayersId& child)
parent = sIndirectLayerTrees[child].mApzcTreeManagerParent;
}
if (oldApzSampler) {
if (oldApzUpdater) {
// We don't support moving a child from an APZ-enabled compositor to a
// APZ-disabled compositor. The mOptions assertion above should already
// ensure this, since APZ-ness is one of the things in mOptions. Note
// however it is possible for mApzSampler to be non-null here with
// oldApzSampler null, because the child may not have been previously
// however it is possible for mApzUpdater to be non-null here with
// oldApzUpdater null, because the child may not have been previously
// composited.
MOZ_ASSERT(mApzSampler);
MOZ_ASSERT(mApzUpdater);
}
if (mApzSampler) {
if (mApzUpdater) {
if (parent) {
MOZ_ASSERT(mApzcTreeManager);
parent->ChildAdopted(mApzcTreeManager);
parent->ChildAdopted(mApzcTreeManager, mApzUpdater);
}
mApzSampler->NotifyLayerTreeAdopted(child, oldApzSampler);
mApzUpdater->NotifyLayerTreeAdopted(child, oldApzUpdater);
}
return IPC_OK();
}
@ -1832,7 +1845,7 @@ EraseLayerState(LayersId aId)
if (iter != sIndirectLayerTrees.end()) {
CompositorBridgeParent* parent = iter->second.mParent;
if (parent) {
if (RefPtr<APZSampler> apz = parent->GetAPZSampler()) {
if (RefPtr<APZUpdater> apz = parent->GetAPZUpdater()) {
apz->NotifyLayerTreeRemoved(aId);
}
}

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

@ -64,6 +64,7 @@ namespace layers {
class APZCTreeManager;
class APZCTreeManagerParent;
class APZSampler;
class APZUpdater;
class AsyncCompositionManager;
class AsyncImagePipelineManager;
class Compositor;
@ -461,6 +462,7 @@ public:
AndroidDynamicToolbarAnimator* GetAndroidDynamicToolbarAnimator();
#endif
RefPtr<APZSampler> GetAPZSampler();
RefPtr<APZUpdater> GetAPZUpdater();
CompositorOptions GetOptions() const {
return mOptions;
@ -623,6 +625,7 @@ protected:
RefPtr<APZCTreeManager> mApzcTreeManager;
RefPtr<APZSampler> mApzSampler;
RefPtr<APZUpdater> mApzUpdater;
RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
// This makes sure the compositorParent is not destroyed before receiving

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

@ -19,6 +19,7 @@
#include "mozilla/ipc/Transport.h" // for Transport
#include "mozilla/layers/AnimationHelper.h" // for CompositorAnimationStorage
#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
#include "mozilla/layers/APZUpdater.h" // for APZUpdater
#include "mozilla/layers/AsyncCompositionManager.h"
#include "mozilla/layers/CompositorOptions.h"
#include "mozilla/layers/CompositorThread.h"
@ -133,8 +134,9 @@ CrossProcessCompositorBridgeParent::AllocPAPZCTreeManagerParent(const LayersId&
// Note: we immediately call ClearTree since otherwise the APZCTM will
// retain a reference to itself, through the checkerboard observer.
RefPtr<APZCTreeManager> temp = new APZCTreeManager(LayersId{0});
temp->ClearTree();
return new APZCTreeManagerParent(aLayersId, temp);
RefPtr<APZUpdater> tempUpdater = new APZUpdater(temp);
tempUpdater->ClearTree();
return new APZCTreeManagerParent(aLayersId, temp, tempUpdater);
}
state.mParent->AllocateAPZCTreeManagerParent(lock, aLayersId, state);

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

@ -13,7 +13,6 @@ namespace mozilla {
namespace layers {
// Helper that creates a monitor and a "done" flag, then enters the monitor.
// This can go away when we switch ImageBridge to an XPCOM thread.
class MOZ_STACK_CLASS SynchronousTask
{
friend class AutoCompleteTask;

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

@ -94,6 +94,7 @@ EXPORTS.mozilla.layers += [
'AnimationInfo.h',
'apz/public/APZInputBridge.h',
'apz/public/APZSampler.h',
'apz/public/APZUpdater.h',
'apz/public/CompositorController.h',
'apz/public/GeckoContentController.h',
'apz/public/IAPZCTreeManager.h',
@ -298,6 +299,7 @@ UNIFIED_SOURCES += [
'apz/src/APZCTreeManager.cpp',
'apz/src/APZInputBridge.cpp',
'apz/src/APZSampler.cpp',
'apz/src/APZUpdater.cpp',
'apz/src/APZUtils.cpp',
'apz/src/AsyncPanZoomController.cpp',
'apz/src/AutoscrollAnimation.cpp',

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

@ -16,6 +16,7 @@
#include "mozilla/Range.h"
#include "mozilla/layers/AnimationHelper.h"
#include "mozilla/layers/APZSampler.h"
#include "mozilla/layers/APZUpdater.h"
#include "mozilla/layers/Compositor.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/CompositorThread.h"
@ -516,7 +517,7 @@ WebRenderBridgeParent::UpdateAPZ(bool aUpdateHitTestingTree)
if (!rootWrbp) {
return;
}
if (RefPtr<APZSampler> apz = cbp->GetAPZSampler()) {
if (RefPtr<APZUpdater> apz = cbp->GetAPZUpdater()) {
apz->UpdateFocusState(rootLayersId, GetLayersId(),
mScrollData.GetFocusTarget());
if (aUpdateHitTestingTree) {

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

@ -315,6 +315,13 @@ public:
return mLayer->GetFixedPositionScrollContainerId();
}
bool IsBackfaceHidden() const
{
// This is only used by APZCTM hit testing, and WR does its own
// hit testing, so no need to implement this.
return false;
}
const void* GetLayer() const
{
MOZ_ASSERT(IsValid());

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

@ -75,4 +75,6 @@ skip == treecell-image-svg-1a.xul treecell-image-svg-1-ref.xul # bug 1218954
skip == treecell-image-svg-1b.xul treecell-image-svg-1-ref.xul # bug 1218954
== treechildren-padding-percent-1.xul treechildren-padding-percent-1-ref.xul
test-pref(svg.context-properties.content.enabled,true) == treetwisty-svg-context-paint-1.xul treetwisty-svg-context-paint-1-ref.xul
!= treetwisty-svg-context-paint-1-not-ref.xul treetwisty-svg-context-paint-1-ref.xul
test-pref(svg.context-properties.content.enabled,true) == treetwisty-svg-context-paint-1.xul treetwisty-svg-context-paint-1-ref.xul

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

@ -0,0 +1,37 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<html:style>
treechildren::-moz-tree-twisty {
-moz-appearance: none !important;
-moz-context-properties: none !important;
list-style: none !important;
}
</html:style>
<tree seltype="single" flex="1">
<treecols>
<treecol flex="1" primary="true"/>
</treecols>
<treechildren>
<treeitem>
<treerow>
<treecell label="I am a treecell"></treecell>
</treerow>
</treeitem>
<treeitem container="true" open="true">
<treerow>
<treecell label="Folder"></treecell>
</treerow>
<treechildren>
<treeitem>
<treerow>
<treecell label="I am a treecell"></treecell>
</treerow>
</treeitem>
</treechildren>
</treeitem>
</treechildren>
</tree>
</window>

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

@ -3,7 +3,12 @@
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<image src="treetwisty-no-context-paint.svg" id="preload-image"/>
<html:style>
#preload-image {
visibility: collapse;
}
treechildren::-moz-tree-twisty {
-moz-appearance: none !important;
-moz-context-properties: none !important;

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

@ -3,7 +3,13 @@
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<image src="treetwisty-context-paint.svg" id="preload-image"/>
<html:style>
#preload-image {
visibility: collapse;
}
#preload-image,
treechildren::-moz-tree-twisty {
-moz-appearance: none !important;
-moz-context-properties: fill, fill-opacity, stroke, stroke-opacity !important;

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

@ -18,7 +18,6 @@
#include "nsCSSValue.h"
class nsAttrValue;
struct nsRuleData;
namespace mozilla {
@ -31,9 +30,8 @@ class ServoSpecifiedValues;
class GenericSpecifiedValues
{
protected:
explicit GenericSpecifiedValues(nsIDocument* aDoc, uint32_t aSIDs)
explicit GenericSpecifiedValues(nsIDocument* aDoc)
: mDocument(aDoc)
, mSIDs(aSIDs)
{}
public:
@ -47,14 +45,6 @@ public:
// 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;
}
// Set a property to an identifier (string)
inline void SetIdentStringValue(nsCSSPropertyID aId, const nsString& aValue);
inline void SetIdentStringValueIfUnset(nsCSSPropertyID aId,
@ -118,7 +108,6 @@ public:
inline void SetBackgroundImage(nsAttrValue& value);
nsIDocument* const mDocument;
const uint32_t mSIDs;
};
} // namespace mozilla

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

@ -14,7 +14,6 @@
#include "mozilla/GenericSpecifiedValues.h"
#include "mozilla/ServoBindingTypes.h"
#include "nsStyleStruct.h"
namespace mozilla {
@ -22,7 +21,7 @@ class ServoSpecifiedValues final : public GenericSpecifiedValues
{
public:
ServoSpecifiedValues(nsIDocument* aDocument, RawServoDeclarationBlock* aDecl)
: GenericSpecifiedValues(aDocument, NS_STYLE_INHERIT_MASK)
: GenericSpecifiedValues(aDocument)
, mDecl(aDecl)
{}

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

@ -930,7 +930,16 @@ class Artifacts(object):
with self._pushhead_cache as pushhead_cache:
found_pushids = {}
for tree in CANDIDATE_TREES:
search_trees = list(CANDIDATE_TREES)
# We aren't generally interested in pushes from autoland because
# people aren't generally working off of autoland locally, but we
# sometimes find errant public pushheads on autoland in automation,
# so we check autoland in automation as a workaround.
if os.environ.get('MOZ_AUTOMATION'):
search_trees += ['integration/autoland']
for tree in search_trees:
self.log(logging.INFO, 'artifact',
{'tree': tree,
'rev': rev},

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