зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
eb1ea97541
|
@ -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},
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче