Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE

This commit is contained in:
Noemi Erli 2018-07-14 01:23:32 +03:00
Родитель fe91a8922e 9793ca2da4
Коммит a83eeaf068
261 изменённых файлов: 5860 добавлений и 8153 удалений

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

@ -333,15 +333,6 @@ dependencies = [
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "core-foundation"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "core-foundation"
version = "0.6.0"
@ -351,14 +342,6 @@ dependencies = [
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "core-foundation-sys"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "core-foundation-sys"
version = "0.6.0"
@ -366,22 +349,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "core-graphics"
version = "0.13.0"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "core-text"
version = "9.2.0"
version = "10.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"core-graphics 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-graphics 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2362,9 +2345,9 @@ dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"core-graphics 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-text 9.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-graphics 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-text 10.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
"freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2391,8 +2374,8 @@ dependencies = [
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"core-graphics 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-graphics 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2407,8 +2390,8 @@ version = "0.1.0"
dependencies = [
"app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"core-graphics 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-graphics 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
"foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2575,12 +2558,10 @@ dependencies = [
"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536"
"checksum cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "56d741ea7a69e577f6d06b36b7dff4738f680593dc27a701ffa8506b73ce28bb"
"checksum cookie 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "746858cae4eae40fff37e1998320068df317bc247dc91a67c6cfa053afdc2abb"
"checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980"
"checksum core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7caa6cb9e76ddddbea09a03266d6b3bc98cd41e9fb9b017c473e7cca593ec25"
"checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa"
"checksum core-foundation-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b2a53cce0ddcf7e7e1f998738d757d5a3bf08bf799a180e50ebe50d298f52f5a"
"checksum core-graphics 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb0ed45fdc32f9ab426238fba9407dfead7bacd7900c9b4dd3f396f46eafdae3"
"checksum core-text 9.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bd581c37283d0c23311d179aefbb891f2324ee0405da58a26e8594ab76e5748"
"checksum core-graphics 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e54c4ab33705fa1fc8af375bb7929d68e1c1546c1ecef408966d8c3e49a1d84a"
"checksum core-text 10.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f59bff773954e5cd058a3f5983406b52bec7cc65202bef340ba64a0c40ac91"
"checksum cose 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72fa26cb151d3ae4b70f63d67d0fed57ce04220feafafbae7f503bef7aae590d"
"checksum cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "49726015ab0ca765144fcca61e4a7a543a16b795a777fa53f554da2fffff9a94"
"checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7"

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

@ -8,6 +8,7 @@ var TrackingProtection = {
PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled",
PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
PREF_APP_MENU_TOGGLE: "privacy.trackingprotection.appMenuToggle.enabled",
PREF_ANIMATIONS_ENABLED: "toolkit.cosmeticAnimations.enabled",
enabledGlobally: false,
enabledInPrivateWindows: false,
container: null,
@ -36,6 +37,10 @@ var TrackingProtection = {
this.icon = $("#tracking-protection-icon");
this.appMenuContainer = $("#appMenu-tp-container");
this.appMenuSeparator = $("#appMenu-tp-separator");
this.iconBox = $("#tracking-protection-icon-box");
this.animatedIcon = $("#tracking-protection-icon-animatable-image");
this.animatedIcon.addEventListener("animationend", () => this.iconBox.removeAttribute("animate"));
this.broadcaster = $("#trackingProtectionBroadcaster");
this.enableTooltip =
@ -47,6 +52,15 @@ var TrackingProtection = {
this.disableTooltipPB =
gNavigatorBundle.getString("trackingProtection.toggle.disable.pbmode.tooltip");
this.updateAnimationsEnabled = () => {
this.iconBox.toggleAttribute("animationsenabled",
Services.prefs.getBoolPref(this.PREF_ANIMATIONS_ENABLED, false));
};
this.updateAnimationsEnabled();
Services.prefs.addObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
this.updateEnabled();
this.updateAppMenuToggle = () => {
@ -78,6 +92,7 @@ var TrackingProtection = {
Services.prefs.removeObserver(this.PREF_ENABLED_GLOBALLY, this);
Services.prefs.removeObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this);
Services.prefs.removeObserver(this.PREF_APP_MENU_TOGGLE, this.updateAppMenuToggle);
Services.prefs.removeObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
},
observe() {
@ -156,21 +171,29 @@ var TrackingProtection = {
Services.telemetry.getHistogramById("TRACKING_PROTECTION_SHIELD").add(value);
},
onSecurityChange(state, isSimulated) {
cancelAnimation() {
let iconAnimation = this.animatedIcon.getAnimations()[0];
if (iconAnimation && iconAnimation.currentTime) {
iconAnimation.cancel();
}
this.iconBox.removeAttribute("animate");
},
onSecurityChange(state, webProgress, isSimulated) {
let baseURI = this._baseURIForChannelClassifier;
// Don't deal with about:, file: etc.
if (!baseURI) {
this.icon.removeAttribute("state");
this.cancelAnimation();
this.iconBox.removeAttribute("state");
return;
}
// Only animate the shield if the event was not fired directly from
// the tabbrowser (due to a browser change).
if (isSimulated) {
this.icon.removeAttribute("animate");
} else {
this.icon.setAttribute("animate", "true");
// The user might have navigated before the shield animation
// finished. In this case, reset the animation to be able to
// play it in full again and avoid choppiness.
if (webProgress.isTopLevel) {
this.cancelAnimation();
}
let isBlocking = state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
@ -192,8 +215,14 @@ var TrackingProtection = {
}
if (isBlocking && this.enabled) {
this.icon.setAttribute("tooltiptext", this.activeTooltipText);
this.icon.setAttribute("state", "blocked-tracking-content");
if (isSimulated) {
this.cancelAnimation();
} else if (webProgress.isTopLevel) {
this.iconBox.setAttribute("animate", "true");
}
this.iconBox.setAttribute("tooltiptext", this.activeTooltipText);
this.iconBox.setAttribute("state", "blocked-tracking-content");
this.content.setAttribute("state", "blocked-tracking-content");
// Open the tracking protection introduction panel, if applicable.
@ -208,22 +237,28 @@ var TrackingProtection = {
this.shieldHistogramAdd(2);
} else if (isAllowing) {
if (isSimulated) {
this.cancelAnimation();
} else if (webProgress.isTopLevel) {
this.iconBox.setAttribute("animate", "true");
}
// Only show the shield when TP is enabled for now.
if (this.enabled) {
this.icon.setAttribute("tooltiptext", this.disabledTooltipText);
this.icon.setAttribute("state", "loaded-tracking-content");
this.iconBox.setAttribute("tooltiptext", this.disabledTooltipText);
this.iconBox.setAttribute("state", "loaded-tracking-content");
this.shieldHistogramAdd(1);
} else {
this.icon.removeAttribute("tooltiptext");
this.icon.removeAttribute("state");
this.iconBox.removeAttribute("tooltiptext");
this.iconBox.removeAttribute("state");
this.shieldHistogramAdd(0);
}
// Warn in the control center even with TP disabled.
this.content.setAttribute("state", "loaded-tracking-content");
} else {
this.icon.removeAttribute("tooltiptext");
this.icon.removeAttribute("state");
this.iconBox.removeAttribute("tooltiptext");
this.iconBox.removeAttribute("state");
this.content.removeAttribute("state");
// We didn't show the shield

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

@ -4898,7 +4898,7 @@ var XULBrowserWindow = {
uri = Services.uriFixup.createExposableURI(uri);
} catch (e) {}
gIdentityHandler.updateIdentity(this._state, uri);
TrackingProtection.onSecurityChange(this._state, aIsSimulated);
TrackingProtection.onSecurityChange(this._state, aWebProgress, aIsSimulated);
},
// simulate all change notifications after switching tabs

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

@ -798,7 +798,12 @@
consumeanchor="identity-box"
onclick="PageProxyClickHandler(event);"/>
<image id="sharing-icon" mousethrough="always"/>
<image id="tracking-protection-icon"/>
<box id="tracking-protection-icon-box" animationsenabled="true">
<image id="tracking-protection-icon"/>
<box id="tracking-protection-icon-animatable-box" flex="1">
<image id="tracking-protection-icon-animatable-image" flex="1"/>
</box>
</box>
<box id="blocked-permissions-container" align="center">
<image data-permission-id="geo" class="blocked-permission-icon geo-icon" role="button"
tooltiptext="&urlbar.geolocationBlocked.tooltip;"/>

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

@ -2495,12 +2495,22 @@ window._gBrowser = {
gBrowser._numPinnedTabs;
break;
case this.closingTabsEnum.OTHER:
tabsToClose = this.visibleTabs.length - 1 - gBrowser._numPinnedTabs;
if (!aTab) {
throw new Error("Required argument missing: aTab");
}
if (aTab.multiselected) {
tabsToClose = this.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned).length;
} else {
// If aTab is pinned, it will already be considered
// with gBrowser._numPinnedTabs.
tabsToClose = this.visibleTabs.length - gBrowser._numPinnedTabs -
(aTab.pinned ? 0 : 1);
}
break;
case this.closingTabsEnum.TO_END:
if (!aTab)
if (!aTab) {
throw new Error("Required argument missing: aTab");
}
tabsToClose = this.getTabsToTheEndFrom(aTab).length;
break;
case this.closingTabsEnum.MULTI_SELECTED:
@ -2578,14 +2588,23 @@ window._gBrowser = {
this.removeTabs(tabs);
},
/**
* In a multi-select context, all unpinned and unselected tabs are removed.
* Otherwise all unpinned tabs except aTab are removed.
*/
removeAllTabsBut(aTab) {
if (!this.warnAboutClosingTabs(this.closingTabsEnum.OTHER)) {
if (!this.warnAboutClosingTabs(this.closingTabsEnum.OTHER, aTab)) {
return;
}
let tabs = this.visibleTabs.filter(tab => tab != aTab && !tab.pinned);
this.selectedTab = aTab;
this.removeTabs(tabs);
let tabsToRemove = [];
if (aTab && aTab.multiselected) {
tabsToRemove = this.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned);
} else {
tabsToRemove = this.visibleTabs.filter(tab => tab != aTab && !tab.pinned);
this.selectedTab = aTab;
}
this.removeTabs(tabsToRemove);
},
removeMultiSelectedTabs() {

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

@ -44,38 +44,38 @@ async function testTrackingProtectionAnimation() {
info("Load a test page not containing tracking elements");
let benignTab = await BrowserTestUtils.openNewForegroundTab(tabbrowser, BENIGN_PAGE);
ok(!TrackingProtection.icon.hasAttribute("state"), "icon: no state");
ok(TrackingProtection.icon.hasAttribute("animate"), "icon: animate");
ok(!TrackingProtection.iconBox.hasAttribute("state"), "iconBox: no state");
ok(!TrackingProtection.iconBox.hasAttribute("animate"), "iconBox: no animate");
info("Load a test page containing tracking elements");
let trackingTab = await BrowserTestUtils.openNewForegroundTab(tabbrowser, TRACKING_PAGE);
ok(TrackingProtection.icon.hasAttribute("state"), "icon: state");
ok(TrackingProtection.icon.hasAttribute("animate"), "icon: animate");
ok(TrackingProtection.iconBox.hasAttribute("state"), "iconBox: state");
ok(TrackingProtection.iconBox.hasAttribute("animate"), "iconBox: animate");
info("Switch from tracking -> benign tab");
let securityChanged = waitForSecurityChange();
tabbrowser.selectedTab = benignTab;
await securityChanged;
ok(!TrackingProtection.icon.hasAttribute("state"), "icon: no state");
ok(!TrackingProtection.icon.hasAttribute("animate"), "icon: no animate");
ok(!TrackingProtection.iconBox.hasAttribute("state"), "iconBox: no state");
ok(!TrackingProtection.iconBox.hasAttribute("animate"), "iconBox: no animate");
info("Switch from benign -> tracking tab");
securityChanged = waitForSecurityChange();
tabbrowser.selectedTab = trackingTab;
await securityChanged;
ok(TrackingProtection.icon.hasAttribute("state"), "icon: state");
ok(!TrackingProtection.icon.hasAttribute("animate"), "icon: no animate");
ok(TrackingProtection.iconBox.hasAttribute("state"), "iconBox: state");
ok(!TrackingProtection.iconBox.hasAttribute("animate"), "iconBox: no animate");
info("Reload tracking tab");
securityChanged = waitForSecurityChange(2);
tabbrowser.reload();
await securityChanged;
ok(TrackingProtection.icon.hasAttribute("state"), "icon: state");
ok(TrackingProtection.icon.hasAttribute("animate"), "icon: animate");
ok(TrackingProtection.iconBox.hasAttribute("state"), "iconBox: state");
ok(TrackingProtection.iconBox.hasAttribute("animate"), "iconBox: animate");
}
add_task(async function testNormalBrowsing() {

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

@ -38,10 +38,10 @@ function testTrackingPage(window) {
ok(!TrackingProtection.container.hidden, "The container is visible");
is(TrackingProtection.content.getAttribute("state"), "blocked-tracking-content",
'content: state="blocked-tracking-content"');
is(TrackingProtection.icon.getAttribute("state"), "blocked-tracking-content",
'icon: state="blocked-tracking-content"');
is(TrackingProtection.iconBox.getAttribute("state"), "blocked-tracking-content",
'iconBox: state="blocked-tracking-content"');
ok(!hidden("#tracking-protection-icon"), "icon is visible");
ok(!hidden("#tracking-protection-icon-box"), "icon box is visible");
ok(hidden("#tracking-action-block"), "blockButton is hidden");
ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
@ -59,10 +59,10 @@ function testTrackingPageUnblocked() {
ok(!TrackingProtection.container.hidden, "The container is visible");
is(TrackingProtection.content.getAttribute("state"), "loaded-tracking-content",
'content: state="loaded-tracking-content"');
is(TrackingProtection.icon.getAttribute("state"), "loaded-tracking-content",
'icon: state="loaded-tracking-content"');
is(TrackingProtection.iconBox.getAttribute("state"), "loaded-tracking-content",
'iconBox: state="loaded-tracking-content"');
ok(!hidden("#tracking-protection-icon"), "icon is visible");
ok(!hidden("#tracking-protection-icon-box"), "icon box is visible");
ok(!hidden("#tracking-action-block"), "blockButton is visible");
ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");

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

@ -35,9 +35,9 @@ add_task(async function test_fetch() {
is(TrackingProtection.content.getAttribute("state"), "blocked-tracking-content",
'content: state="blocked-tracking-content"');
is(TrackingProtection.icon.getAttribute("state"), "blocked-tracking-content",
'icon: state="blocked-tracking-content"');
is(TrackingProtection.icon.getAttribute("tooltiptext"),
is(TrackingProtection.iconBox.getAttribute("state"), "blocked-tracking-content",
'iconBox: state="blocked-tracking-content"');
is(TrackingProtection.iconBox.getAttribute("tooltiptext"),
gNavigatorBundle.getString("trackingProtection.icon.activeTooltip"), "correct tooltip");
});
});

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

@ -20,6 +20,7 @@ support-files =
test_bug1358314.html
[browser_isLocalAboutURI.js]
[browser_multiselect_tabs_active_tab_selected_by_default.js]
[browser_multiselect_tabs_close_other_tabs.js]
[browser_multiselect_tabs_close_using_shortcuts.js]
[browser_multiselect_tabs_close.js]
[browser_multiselect_tabs_mute_unmute.js]

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

@ -0,0 +1,108 @@
const PREF_MULTISELECT_TABS = "browser.tabs.multiselect";
const PREF_WARN_ON_CLOSE = "browser.tabs.warnOnCloseOtherTabs";
add_task(async function setPref() {
await SpecialPowers.pushPrefEnv({
set: [
[PREF_MULTISELECT_TABS, true],
[PREF_WARN_ON_CLOSE, false]
]
});
});
add_task(async function withAMultiSelectedTab() {
let initialTab = gBrowser.selectedTab;
let tab1 = await addTab();
let tab2 = await addTab();
let tab3 = await addTab();
let tab4 = await addTab();
is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
await triggerClickOn(tab1, { ctrlKey: true });
let tab4Pinned = BrowserTestUtils.waitForEvent(tab4, "TabPinned");
gBrowser.pinTab(tab4);
await tab4Pinned;
ok(initialTab.multiselected, "InitialTab is multiselected");
ok(tab1.multiselected, "Tab1 is multiselected");
ok(!tab2.multiselected, "Tab2 is not multiselected");
ok(!tab3.multiselected, "Tab3 is not multiselected");
ok(!tab4.multiselected, "Tab4 is not multiselected");
ok(tab4.pinned, "Tab4 is pinned");
is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
is(gBrowser.selectedTab, initialTab, "InitialTab is the active tab");
let closingTabs = [tab2, tab3];
let tabClosingPromises = [];
for (let tab of closingTabs) {
tabClosingPromises.push(BrowserTestUtils.waitForTabClosing(tab));
}
gBrowser.removeAllTabsBut(tab1);
for (let promise of tabClosingPromises) {
await promise;
}
ok(!initialTab.closing, "InitialTab is not closing");
ok(!tab1.closing, "Tab1 is not closing");
ok(tab2.closing, "Tab2 is closing");
ok(tab3.closing, "Tab3 is closing");
ok(!tab4.closing, "Tab4 is not closing");
is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
is(gBrowser.selectedTab, initialTab, "InitialTab is still the active tab");
gBrowser.clearMultiSelectedTabs(false);
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab4);
});
add_task(async function withNotAMultiSelectedTab() {
let initialTab = gBrowser.selectedTab;
let tab1 = await addTab();
let tab2 = await addTab();
let tab3 = await addTab();
let tab4 = await addTab();
is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
await BrowserTestUtils.switchTab(gBrowser, tab1);
await triggerClickOn(tab2, { ctrlKey: true });
let tab4Pinned = BrowserTestUtils.waitForEvent(tab4, "TabPinned");
gBrowser.pinTab(tab4);
await tab4Pinned;
ok(!initialTab.multiselected, "InitialTab is not multiselected");
ok(tab1.multiselected, "Tab1 is multiselected");
ok(tab2.multiselected, "Tab2 is multiselected");
ok(!tab3.multiselected, "Tab3 is not multiselected");
ok(!tab4.multiselected, "Tab4 is not multiselected");
ok(tab4.pinned, "Tab4 is pinned");
is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
is(gBrowser.selectedTab, tab1, "Tab1 is the active tab");
let closingTabs = [tab1, tab2, tab3];
let tabClosingPromises = [];
for (let tab of closingTabs) {
tabClosingPromises.push(BrowserTestUtils.waitForTabClosing(tab));
}
await BrowserTestUtils.switchTab(gBrowser, gBrowser.removeAllTabsBut(initialTab));
for (let promise of tabClosingPromises) {
await promise;
}
ok(!initialTab.closing, "InitialTab is not closing");
ok(tab1.closing, "Tab1 is closing");
ok(tab2.closing, "Tab2 is closing");
ok(tab3.closing, "Tab3 is closing");
ok(!tab4.closing, "Tab4 is not closing");
is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
is(gBrowser.selectedTab, initialTab, "InitialTab is the active tab now");
BrowserTestUtils.removeTab(tab4);
});

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

@ -5,6 +5,7 @@ support-files =
../general/benignPage.html
../general/trackingPage.html
[browser_trackingUI_animation.js]
[browser_trackingUI_appMenu.js]
[browser_trackingUI_appMenu_toggle.js]
[browser_trackingUI_open_preferences.js]

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

@ -0,0 +1,40 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
const TP_PREF = "privacy.trackingprotection.enabled";
const ANIMATIONS_PREF = "toolkit.cosmeticAnimations.enabled";
// Test that the shield icon animation can be controlled by the cosmetic
// animations pref and that one of the icons is visible in each case.
add_task(async function testShieldAnimation() {
await UrlClassifierTestUtils.addTestTrackers();
Services.prefs.setBoolPref(TP_PREF, true);
let tab = gBrowser.selectedTab = gBrowser.addTab();
let animationIcon = document.getElementById("tracking-protection-icon-animatable-image");
let noAnimationIcon = document.getElementById("tracking-protection-icon");
Services.prefs.setBoolPref(ANIMATIONS_PREF, true);
await promiseTabLoadEvent(tab, TRACKING_PAGE);
ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden when animations are enabled");
ok(BrowserTestUtils.is_visible(animationIcon), "the animated icon is shown when animations are enabled");
await promiseTabLoadEvent(tab, BENIGN_PAGE);
ok(BrowserTestUtils.is_hidden(animationIcon), "the animated icon is hidden");
ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden");
Services.prefs.setBoolPref(ANIMATIONS_PREF, false);
await promiseTabLoadEvent(tab, TRACKING_PAGE);
ok(BrowserTestUtils.is_visible(noAnimationIcon), "the default icon is shown when animations are disabled");
ok(BrowserTestUtils.is_hidden(animationIcon), "the animated icon is hidden when animations are disabled");
gBrowser.removeCurrentTab();
Services.prefs.clearUserPref(ANIMATIONS_PREF);
Services.prefs.clearUserPref(TP_PREF);
UrlClassifierTestUtils.cleanupTestTrackers();
});

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

@ -6,8 +6,6 @@
const PREF = "privacy.trackingprotection.enabled";
const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
var {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
async function waitAndAssertPreferencesShown() {
await BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popuphidden");
await TestUtils.waitForCondition(() => gBrowser.currentURI.spec == "about:preferences#privacy",

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

@ -6,8 +6,6 @@
const PREF = "privacy.trackingprotection.enabled";
const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
var {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
// TODO: replace this once bug 1428847 is done.
function hidden(el) {
let win = el.ownerGlobal;

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

@ -21,8 +21,6 @@ const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/
var TrackingProtection = null;
var tabbrowser = null;
var {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
registerCleanupFunction(function() {
TrackingProtection = tabbrowser = null;
UrlClassifierTestUtils.cleanupTestTrackers();
@ -33,6 +31,9 @@ registerCleanupFunction(function() {
}
});
// This is a special version of "hidden" that doesn't check for item
// visibility and just asserts the display and opacity attributes.
// That way we can test elements even when their panel is hidden...
function hidden(sel) {
let win = tabbrowser.ownerGlobal;
let el = win.document.querySelector(sel);
@ -51,10 +52,11 @@ function testBenignPage() {
info("Non-tracking content must not be blocked");
ok(!TrackingProtection.container.hidden, "The container is visible");
ok(!TrackingProtection.content.hasAttribute("state"), "content: no state");
ok(!TrackingProtection.icon.hasAttribute("state"), "icon: no state");
ok(!TrackingProtection.icon.hasAttribute("tooltiptext"), "icon: no tooltip");
ok(!TrackingProtection.iconBox.hasAttribute("state"), "icon box: no state");
ok(!TrackingProtection.iconBox.hasAttribute("tooltiptext"), "icon box: no tooltip");
ok(hidden("#tracking-protection-icon"), "icon is hidden");
let doc = tabbrowser.ownerGlobal.document;
ok(BrowserTestUtils.is_hidden(doc.getElementById("tracking-protection-icon-box")), "icon box is hidden");
ok(hidden("#tracking-action-block"), "blockButton is hidden");
ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
ok(!hidden("#tracking-protection-preferences-button"), "preferences button is visible");
@ -70,10 +72,11 @@ function testBenignPageWithException() {
ok(!TrackingProtection.container.hidden, "The container is visible");
ok(!TrackingProtection.content.hasAttribute("state"), "content: no state");
ok(TrackingProtection.content.hasAttribute("hasException"), "content has exception attribute");
ok(!TrackingProtection.icon.hasAttribute("state"), "icon: no state");
ok(!TrackingProtection.icon.hasAttribute("tooltiptext"), "icon: no tooltip");
ok(!TrackingProtection.iconBox.hasAttribute("state"), "icon box: no state");
ok(!TrackingProtection.iconBox.hasAttribute("tooltiptext"), "icon box: no tooltip");
ok(hidden("#tracking-protection-icon"), "icon is hidden");
let doc = tabbrowser.ownerGlobal.document;
ok(BrowserTestUtils.is_hidden(doc.getElementById("tracking-protection-icon-box")), "icon box is hidden");
is(!hidden("#tracking-action-block"), TrackingProtection.enabled,
"blockButton is visible if TP is on");
ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
@ -92,12 +95,13 @@ function testTrackingPage(window) {
ok(!TrackingProtection.container.hidden, "The container is visible");
is(TrackingProtection.content.getAttribute("state"), "blocked-tracking-content",
'content: state="blocked-tracking-content"');
is(TrackingProtection.icon.getAttribute("state"), "blocked-tracking-content",
'icon: state="blocked-tracking-content"');
is(TrackingProtection.icon.getAttribute("tooltiptext"),
is(TrackingProtection.iconBox.getAttribute("state"), "blocked-tracking-content",
'icon box: state="blocked-tracking-content"');
is(TrackingProtection.iconBox.getAttribute("tooltiptext"),
gNavigatorBundle.getString("trackingProtection.icon.activeTooltip"), "correct tooltip");
ok(!hidden("#tracking-protection-icon"), "icon is visible");
let doc = tabbrowser.ownerGlobal.document;
ok(BrowserTestUtils.is_visible(doc.getElementById("tracking-protection-icon-box")), "icon box is visible");
ok(hidden("#tracking-action-block"), "blockButton is hidden");
ok(!hidden("#tracking-protection-preferences-button"), "preferences button is visible");
@ -123,13 +127,14 @@ function testTrackingPageUnblocked() {
is(TrackingProtection.content.getAttribute("state"), "loaded-tracking-content",
'content: state="loaded-tracking-content"');
if (TrackingProtection.enabled) {
is(TrackingProtection.icon.getAttribute("state"), "loaded-tracking-content",
'icon: state="loaded-tracking-content"');
is(TrackingProtection.icon.getAttribute("tooltiptext"),
is(TrackingProtection.iconBox.getAttribute("state"), "loaded-tracking-content",
'icon box: state="loaded-tracking-content"');
is(TrackingProtection.iconBox.getAttribute("tooltiptext"),
gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip"), "correct tooltip");
}
is(!hidden("#tracking-protection-icon"), TrackingProtection.enabled, "icon is visible if TP is on");
let doc = tabbrowser.ownerGlobal.document;
is(BrowserTestUtils.is_visible(doc.getElementById("tracking-protection-icon-box")), TrackingProtection.enabled, "icon box is visible if TP is on");
is(!hidden("#tracking-action-block"), TrackingProtection.enabled, "blockButton is visible if TP is on");
ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
ok(!hidden("#tracking-protection-preferences-button"), "preferences button is visible");

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

@ -1,3 +1,5 @@
var {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
/**
* Waits for a load (or custom) event to finish in a given tab. If provided
* load an uri into the tab.

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

@ -187,7 +187,12 @@ var UITour = {
},
}],
["trackingProtection", {
query: "#tracking-protection-icon",
query: (aDocument) => {
if (Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled", false)) {
return aDocument.getElementById("tracking-protection-icon-animatable-box");
}
return aDocument.getElementById("tracking-protection-icon");
},
}],
["urlbar", {
query: "#urlbar",

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

@ -5,7 +5,7 @@ const PREF_TP_ENABLED = "privacy.trackingprotection.enabled";
const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/benignPage.html";
const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/trackingPage.html";
const TOOLTIP_PANEL = document.getElementById("UITourTooltip");
const TOOLTIP_ANCHOR = document.getElementById("tracking-protection-icon");
const TOOLTIP_ANCHOR = document.getElementById("tracking-protection-icon-animatable-box");
var {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});

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

@ -23,8 +23,6 @@
--chrome-secondary-background-color: hsl(240, 1%, 20%);
--toolbox-border-bottom-color: hsl(240, 5%, 5%);
--chrome-nav-bar-controls-border-color: hsla(240, 5%, 5%, .3);
--chrome-selection-color: #fff;
--chrome-selection-background-color: #5675B9;
/* Url and search bars */
--lwt-toolbar-field-background-color: rgb(71, 71, 73);
@ -45,8 +43,6 @@
--chrome-secondary-background-color: #f5f6f7;
--toolbox-border-bottom-color: #cccccc;
--chrome-nav-bar-controls-border-color: #ccc;
--chrome-selection-color: #f5f7fa;
--chrome-selection-background-color: #4c9ed9;
}
#tabbrowser-tabs:-moz-lwtheme {
@ -59,13 +55,6 @@ toolbar[brighttext] .toolbarbutton-1 {
fill: rgb(249, 249, 250);
}
#urlbar ::-moz-selection,
#navigator-toolbox .searchbar-textbox ::-moz-selection,
.browserContainer > findbar ::-moz-selection {
background-color: var(--chrome-selection-background-color);
color: var(--chrome-selection-color);
}
/* Change the base colors for the browser chrome */
#TabsToolbar,

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

@ -147,33 +147,122 @@
/* TRACKING PROTECTION ICON */
#tracking-protection-icon {
list-style-image: url(chrome://browser/skin/tracking-protection.svg);
margin-inline-end: 0;
#tracking-protection-icon-box {
visibility: collapse;
overflow: hidden;
width: 20px;
margin-inline-end: -20px;
}
#tracking-protection-icon[state="loaded-tracking-content"] {
list-style-image: url(chrome://browser/skin/tracking-protection-disabled.svg);
#tracking-protection-icon-box[state] {
margin-inline-end: 0px;
visibility: visible;
}
#tracking-protection-icon[animate] {
#tracking-protection-icon-box[animationsenabled][animate] {
transition: margin-left 200ms ease-out, margin-right 200ms ease-out;
}
#tracking-protection-icon:not([state]) {
margin-inline-end: -20px;
pointer-events: none;
opacity: 0;
/* Only animate the shield in, when it disappears hide it immediately. */
transition: none;
list-style-image: none;
#tracking-protection-icon-box[state="blocked-tracking-content"][animationsenabled] > #tracking-protection-icon,
#tracking-protection-icon-box:not([animationsenabled]) > #tracking-protection-icon-animatable-box {
display: none;
}
#tracking-protection-icon-box > #tracking-protection-icon-animatable-box {
position: absolute;
overflow: hidden;
top: calc(50% - 10px); /* half the height of the sprite */
margin-inline-start: 4px;
width: 16px;
height: 20px;
}
#tracking-protection-icon-box[state="blocked-tracking-content"] #tracking-protection-icon-animatable-image {
background-image: url(chrome://browser/skin/tracking-protection-animation.svg);
transform: translateX(-1232px);
width: 1248px;
background-size: auto;
height: 16px;
min-height: 20px;
-moz-context-properties: fill, fill-opacity;
}
#tracking-protection-icon-box[state="blocked-tracking-content"] #tracking-protection-icon-animatable-image:-moz-locale-dir(rtl) {
transform: scaleX(-1) translateX(-1232px);
}
#tracking-protection-icon-box[state="blocked-tracking-content"][animate] #tracking-protection-icon-animatable-image {
animation-name: tp-icon-animation;
animation-timing-function: steps(77);
animation-duration: 3s;
animation-fill-mode: forwards;
}
#tracking-protection-icon-box[state="blocked-tracking-content"][animate] #tracking-protection-icon-animatable-image:-moz-locale-dir(rtl) {
animation-name: tp-icon-animation-rtl;
}
#tracking-protection-icon-box[state="blocked-tracking-content"] > #tracking-protection-icon {
list-style-image: url(chrome://browser/skin/tracking-protection.svg);
}
#tracking-protection-icon-box[state="loaded-tracking-content"] > #tracking-protection-icon {
list-style-image: url(chrome://browser/skin/tracking-protection-disabled.svg);
}
#urlbar[pageproxystate="invalid"] > #identity-box > #extension-icon,
#urlbar[pageproxystate="invalid"] > #identity-box > #tracking-protection-icon {
#urlbar[pageproxystate="invalid"] > #identity-box > #tracking-protection-icon-box {
visibility: collapse;
}
@keyframes tp-icon-animation {
from {
transform: translateX(0);
fill-opacity: 0.3;
}
38% {
fill: inherit;
fill-opacity: 0.3;
}
39% {
fill: #7f00d6;
fill-opacity: 1;
}
75% {
transform: translateX(-1232px);
fill: #7f00d6;
fill-opacity: 1;
}
to {
fill: inherit;
fill-opacity: inherit;
}
}
@keyframes tp-icon-animation-rtl {
from {
transform: scaleX(-1) translateX(0);
fill-opacity: 0.3;
}
38% {
fill: inherit;
fill-opacity: 0.3;
}
39% {
fill: #7f00d6;
fill-opacity: 1;
}
75% {
transform: scaleX(-1) translateX(-1232px);
fill: #7f00d6;
fill-opacity: 1;
}
to {
fill: inherit;
fill-opacity: inherit;
}
}
/* CONNECTION ICON, EXTENSION ICON */
#connection-icon,

Разница между файлами не показана из-за своего большого размера Загрузить разницу

После

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

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

@ -85,6 +85,7 @@
skin/classic/browser/tracking-protection.svg (../shared/identity-block/tracking-protection.svg)
skin/classic/browser/tracking-protection-disabled.svg (../shared/identity-block/tracking-protection-disabled.svg)
skin/classic/browser/tracking-protection-animation.svg (../shared/identity-block/tracking-protection-animation.svg)
skin/classic/browser/panel-icon-arrow-left.svg (../shared/panel-icon-arrow-left.svg)
skin/classic/browser/panel-icon-arrow-right.svg (../shared/panel-icon-arrow-right.svg)
skin/classic/browser/panel-icon-cancel.svg (../shared/panel-icon-cancel.svg)

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

@ -15,7 +15,11 @@ function test() {
DebuggerServer.init();
DebuggerServer.registerAllActors();
DebuggerServer.addActors(ACTORS_URL);
DebuggerServer.registerModule(ACTORS_URL, {
prefix: "testOne",
constructor: "TestActor1",
type: { global: true },
});
let transport = DebuggerServer.connectPipe();
gClient = new DebuggerClient(transport);
@ -24,9 +28,9 @@ function test() {
"Root actor should identify itself as a browser.");
gClient.listTabs().then(aResponse => {
let globalActor = aResponse.testGlobalActor1;
let globalActor = aResponse.testOneActor;
ok(globalActor, "Found the test global actor.");
ok(globalActor.includes("test_one"),
ok(globalActor.includes("testOne"),
"testGlobalActor1's actorPrefix should be used.");
gClient.request({ to: globalActor, type: "ping" }, aResponse => {
@ -40,7 +44,7 @@ function test() {
let count = 0;
for (let connID of Object.getOwnPropertyNames(DebuggerServer._connections)) {
let conn = DebuggerServer._connections[connID];
let actorPrefix = conn._prefix + "test_one";
let actorPrefix = conn._prefix + "testOne";
for (let pool of conn._extraPools) {
count += Object.keys(pool._actors).filter(e => {
return e.startsWith(actorPrefix);
@ -48,8 +52,8 @@ function test() {
}
}
is(count, 2,
"Only two actor exists in all pools. One target-scoped actor and one global.");
is(count, 1,
"Only one actor exists in all pools. One global actor.");
gClient.close().then(finish);
});

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

@ -16,7 +16,11 @@ function test() {
DebuggerServer.init();
DebuggerServer.registerAllActors();
DebuggerServer.addActors(ACTORS_URL);
DebuggerServer.registerModule(ACTORS_URL, {
prefix: "testOne",
constructor: "TestActor1",
type: { target: true },
});
let transport = DebuggerServer.connectPipe();
gClient = new DebuggerClient(transport);
@ -39,12 +43,12 @@ function test() {
function testTargetScopedActor([aGrip, aResponse]) {
let deferred = promise.defer();
ok(aGrip.testTargetScopedActor1,
ok(aGrip.testOneActor,
"Found the test target-scoped actor.");
ok(aGrip.testTargetScopedActor1.includes("test_one"),
"testTargetScopedActor1's actorPrefix should be used.");
ok(aGrip.testOneActor.includes("testOne"),
"testOneActor's actorPrefix should be used.");
gClient.request({ to: aGrip.testTargetScopedActor1, type: "ping" }, aResponse => {
gClient.request({ to: aGrip.testOneActor, type: "ping" }, aResponse => {
is(aResponse.pong, "pong",
"Actor should respond to requests.");

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

@ -16,7 +16,11 @@ function test() {
DebuggerServer.init();
DebuggerServer.registerAllActors();
DebuggerServer.addActors(ACTORS_URL);
DebuggerServer.registerModule(ACTORS_URL, {
prefix: "testOne",
constructor: "TestActor1",
type: { target: true },
});
let transport = DebuggerServer.connectPipe();
gClient = new DebuggerClient(transport);
@ -39,12 +43,12 @@ function test() {
function testTargetScopedActor([aGrip, aResponse]) {
let deferred = promise.defer();
ok(aGrip.testTargetScopedActor1,
ok(aGrip.testOneActor,
"Found the test target-scoped actor.");
ok(aGrip.testTargetScopedActor1.includes("test_one"),
"testTargetScopedActor1's actorPrefix should be used.");
ok(aGrip.testOneActor.includes("testOne"),
"testOneActor's actorPrefix should be used.");
gClient.request({ to: aGrip.testTargetScopedActor1, type: "ping" }, aResponse => {
gClient.request({ to: aGrip.testOneActor, type: "ping" }, aResponse => {
is(aResponse.pong, "pong",
"Actor should respond to requests.");
@ -64,7 +68,7 @@ function closeTab(aTestActor) {
deferred.reject(aResponse);
});
} catch (e) {
is(e.message, "'ping' request packet has no destination.", "testTargetScopedActor1 went away.");
is(e.message, "'ping' request packet has no destination.", "testOnActor went away.");
deferred.resolve();
}

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

@ -9,7 +9,7 @@ function TestActor1(aConnection, aTab) {
}
TestActor1.prototype = {
actorPrefix: "test_one",
actorPrefix: "testOne",
grip: function TA1_grip() {
return { actor: this.actorID,
@ -24,15 +24,4 @@ TestActor1.prototype = {
TestActor1.prototype.requestTypes = {
"ping": TestActor1.prototype.onPing
};
DebuggerServer.removeTargetScopedActor("testTargetScopedActor1");
DebuggerServer.removeGlobalActor("testGlobalActor1");
DebuggerServer.addTargetScopedActor({
constructorName: "TestActor1",
constructorFun: TestActor1,
}, "testTargetScopedActor1");
DebuggerServer.addGlobalActor({
constructorName: "TestActor1",
constructorFun: TestActor1,
}, "testGlobalActor1");
exports.TestActor1 = TestActor1;

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

@ -47,9 +47,8 @@ class FontPropertyValue extends PureComponent {
}
onUnitChange(e) {
// TODO implement conversion.
// Bug 1459898: https://bugzilla.mozilla.org/show_bug.cgi?id=1459898
this.props.onChange(this.props.name, this.props.value, e.target.value);
this.props.onChange(this.props.name, this.props.value, this.props.unit,
e.target.value);
}
onMouseDown(e) {

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

@ -137,6 +137,143 @@ class FontInspector {
});
}
/**
* Convert a value for font-size between two CSS unit types.
* Conversion is done via pixels. If neither of the two given unit types is "px",
* recursively get the value in pixels, then convert that result to the desired unit.
*
* @param {Number} value
* Numeric value to convert.
* @param {String} fromUnit
* CSS unit to convert from.
* @param {String} toUnit
* CSS unit to convert to.
* @return {Number}
* Converted numeric value.
*/
async convertUnits(value, fromUnit, toUnit) {
if (value !== parseFloat(value)) {
throw TypeError(`Invalid value for conversion. Expected Number, got ${value}`);
}
if (fromUnit === toUnit) {
return value;
}
// If neither unit is in pixels, first convert the value to pixels.
// Reassign input value and source CSS unit.
if (toUnit !== "px" && fromUnit !== "px") {
value = await this.convertUnits(value, fromUnit, "px");
fromUnit = "px";
}
// Whether the conversion is done from pixels.
const fromPx = fromUnit === "px";
// Determine the target CSS unit for conversion.
const unit = toUnit === "px" ? fromUnit : toUnit;
// NodeFront instance of selected element.
const node = this.inspector.selection.nodeFront;
// Default output value to input value for a 1-to-1 conversion as a guard against
// unrecognized CSS units. It will not be correct, but it will also not break.
let out = value;
// Computed style for reference node used for conversion of "em", "rem", "%".
let computedStyle;
// Raw DOM node of selected element used for conversion of "vh", "vw", "vmin", "vmax".
let rawNode;
if (unit === "in") {
out = fromPx
? value / 96
: value * 96;
}
if (unit === "cm") {
out = fromPx
? value * 0.02645833333
: value / 0.02645833333;
}
if (unit === "mm") {
out = fromPx
? value * 0.26458333333
: value / 0.26458333333;
}
if (unit === "pt") {
out = fromPx
? value * 0.75
: value / 0.75;
}
if (unit === "pc") {
out = fromPx
? value * 0.0625
: value / 0.0625;
}
if (unit === "%") {
computedStyle = await this.pageStyle.getComputed(node.parentNode());
out = fromPx
? value * 100 / parseFloat(computedStyle["font-size"].value)
: value / 100 * parseFloat(computedStyle["font-size"].value);
}
if (unit === "em") {
computedStyle = await this.pageStyle.getComputed(node.parentNode());
out = fromPx
? value / parseFloat(computedStyle["font-size"].value)
: value * parseFloat(computedStyle["font-size"].value);
}
if (unit === "rem") {
const document = await this.inspector.walker.documentElement();
computedStyle = await this.pageStyle.getComputed(document);
out = fromPx
? value / parseFloat(computedStyle["font-size"].value)
: value * parseFloat(computedStyle["font-size"].value);
}
if (unit === "vh") {
rawNode = await node.rawNode();
out = fromPx
? value * 100 / rawNode.ownerGlobal.innerHeight
: value / 100 * rawNode.ownerGlobal.innerHeight;
}
if (unit === "vw") {
rawNode = await node.rawNode();
out = fromPx
? value * 100 / rawNode.ownerGlobal.innerWidth
: value / 100 * rawNode.ownerGlobal.innerWidth;
}
if (unit === "vmin") {
rawNode = await node.rawNode();
out = fromPx
? value * 100 / Math.min(
rawNode.ownerGlobal.innerWidth, rawNode.ownerGlobal.innerHeight)
: value / 100 * Math.min(
rawNode.ownerGlobal.innerWidth, rawNode.ownerGlobal.innerHeight);
}
if (unit === "vmax") {
rawNode = await node.rawNode();
out = fromPx
? value * 100 / Math.max(
rawNode.ownerGlobal.innerWidth, rawNode.ownerGlobal.innerHeight)
: value / 100 * Math.max(
rawNode.ownerGlobal.innerWidth, rawNode.ownerGlobal.innerHeight);
}
// Return rounded pixel values. Limit other values to 3 decimals.
if (fromPx) {
// Round values like 1.000 to 1
return out === Math.round(out) ? Math.round(out) : out.toFixed(3);
}
return Math.round(out);
}
/**
* Destruction function called when the inspector is destroyed. Removes event listeners
* and cleans up references.
@ -290,14 +427,19 @@ class FontInspector {
/**
* Get a reference to a TextProperty instance from the current selected rule for a
* given property name. If one doesn't exist, create one with the given value.
* If the selected rule no longer exists (ex: during test teardown), return null.
*
* @param {String} name
* CSS property name
* @param {String} value
* CSS property value
* @return {TextProperty}
* @return {TextProperty|null}
*/
getTextProperty(name, value) {
if (!this.selectedRule) {
return null;
}
let textProperty =
this.selectedRule.textProps.find(prop => prop.name === name);
if (!textProperty) {
@ -480,7 +622,14 @@ class FontInspector {
syncChanges(name, value) {
const textProperty = this.getTextProperty(name, value);
if (textProperty) {
textProperty.setValue(value);
// This method may be called after the connection to the page style actor is closed.
// For example, during teardown of automated tests. Here, we catch any failure that
// may occur because of that. We're not interested in handling the error.
try {
textProperty.setValue(value);
} catch (e) {
// Silent error.
}
}
this.ruleView.on("property-value-updated", this.onRulePropertyUpdated);
@ -569,11 +718,24 @@ class FontInspector {
* CSS font property name or axis name
* @param {String} value
* CSS font property numeric value or axis value
* @param {String|null} unit
* CSS unit or null
* @param {String|undefined} fromUnit
* Optional CSS unit to convert from
* @param {String|undefined} toUnit
* Optional CSS unit to convert to
*/
onPropertyChange(property, value, unit) {
async onPropertyChange(property, value, fromUnit, toUnit) {
if (FONT_PROPERTIES.includes(property)) {
let unit = fromUnit;
if (toUnit && fromUnit) {
try {
value = await this.convertUnits(value, fromUnit, toUnit);
unit = toUnit;
} catch (err) {
// Silent error
}
}
this.onFontPropertyUpdate(property, value, unit);
} else {
this.onAxisUpdate(property, value);
@ -670,13 +832,21 @@ class FontInspector {
}
if (!this.store || !this.isSelectedNodeValid()) {
this.store.dispatch(resetFontEditor());
if (this.inspector.selection.isPseudoElementNode()) {
const noPseudoWarning = getStr("fontinspector.noPseduoWarning");
this.store.dispatch(resetFontEditor());
this.store.dispatch(updateWarningMessage(noPseudoWarning));
return;
}
// If the selection is a TextNode, switch selection to be its parent node.
if (this.inspector.selection.isTextNode()) {
const selection = this.inspector.selection;
selection.setNodeFront(selection.nodeFront.parentNode());
return;
}
this.store.dispatch(resetFontEditor());
return;
}
@ -851,7 +1021,13 @@ class FontInspector {
// Prevent reacting to changes we caused.
this.ruleView.off("property-value-updated", this.onRulePropertyUpdated);
// Live preview font property changes on the page.
textProperty.rule.previewPropertyValue(textProperty, value, "");
try {
textProperty.rule.previewPropertyValue(textProperty, value, "");
} catch (e) {
// Silent error
}
// Sync Rule view with changes reflected on the page (debounced).
this.syncChanges(name, value);
}

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

@ -20,6 +20,7 @@ support-files =
skip-if = !e10s # too slow on !e10s, logging fully serialized actors (Bug 1446595)
subsuite = clipboard
[browser_fontinspector_edit-previews.js]
[browser_fontinspector_editor-font-size-conversion.js]
[browser_fontinspector_editor-values.js]
[browser_fontinspector_editor-keywords.js]
[browser_fontinspector_expand-css-code.js]
@ -28,4 +29,5 @@ subsuite = clipboard
[browser_fontinspector_other-fonts.js]
[browser_fontinspector_rendered-fonts.js]
[browser_fontinspector_reveal-in-page.js]
[browser_fontinspector_text-node.js]
[browser_fontinspector_theme-change.js]

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

@ -0,0 +1,71 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* global getPropertyValue waitFor */
"use strict";
// Test that changes to font-size units converts the value to the destination unit.
// Check that converted values are applied to the inline style of the selected element.
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
add_task(async function() {
await pushPref("devtools.inspector.fonteditor.enabled", true);
const { inspector, view, tab, testActor } = await openFontInspectorForURL(TEST_URI);
const viewDoc = view.document;
const property = "font-size";
const selector = "div";
const UNITS = {
"px": "36px",
"%": "100%",
"em": "1em",
};
await selectNode(selector, inspector);
info("Check that font editor shows font-size value in original units");
let fontSize = getPropertyValue(viewDoc, property);
is(fontSize.unit, "em", "Original unit for font size is em");
is(fontSize.value + fontSize.unit, "1em", "Original font size is 1em");
let prevValue = fontSize.value;
let prevUnit = fontSize.unit;
for (const unit in UNITS) {
const value = UNITS[unit];
const onEditorUpdated = inspector.once("fonteditor-updated");
info(`Convert font-size from ${prevValue}${prevUnit} to ${unit}`);
await view.onPropertyChange(property, prevValue, prevUnit, unit);
info("Waiting for font editor to re-render");
await onEditorUpdated;
info(`Waiting for font-size unit dropdown to re-render with selected value: ${unit}`);
await waitFor(() => {
const sel = `#font-editor .font-value-slider[name=${property}] ~ .font-unit-select`;
return viewDoc.querySelector(sel).value === unit;
});
info("Waiting for testactor reflow");
await testActor.reflow();
info(`Check that font editor font-size value is converted to ${unit}`);
fontSize = getPropertyValue(viewDoc, property);
is(fontSize.unit, unit, `Font size unit is converted to ${unit}`);
is(fontSize.value + fontSize.unit, value, `Font size in font editor is ${value}`);
info(`Check that inline style font-size value is converted to ${unit}`);
const inlineStyleValue = await getInlineStyleValue(tab, selector, property);
is(inlineStyleValue, value, `Font size on inline style is ${value}`);
// Store current unit and value to use in conversion on the next iteration.
prevUnit = fontSize.unit;
prevValue = fontSize.value;
}
});
async function getInlineStyleValue(tab, selector, property) {
return ContentTask.spawn(tab.linkedBrowser, { selector, property }, function(args) {
const el = content.document.querySelector(args.selector);
return el && el.style[args.property];
});
}

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

@ -1,6 +1,7 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* global getPropertyValue */
"use strict";
@ -17,16 +18,6 @@ add_task(async function() {
await testKeywordValues(inspector, viewDoc);
});
function getPropertyValue(viewDoc, name) {
const selector = `#font-editor .font-value-slider[name=${name}]`;
return {
value: viewDoc.querySelector(selector).value,
// Ensure unit dropdown exists before querying its value
unit: viewDoc.querySelector(selector + ` ~ .font-unit-select`) &&
viewDoc.querySelector(selector + ` ~ .font-unit-select`).value
};
}
async function testKeywordValues(inspector, viewDoc) {
await selectNode(".bold-text", inspector);

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

@ -0,0 +1,32 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that selecting a text node invokes the font editor on its parent node.
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
add_task(async function() {
await pushPref("devtools.inspector.fonteditor.enabled", true);
const { inspector, view } = await openFontInspectorForURL(TEST_URI);
const viewDoc = view.document;
info("Select the first text node of <body>");
const bodyNode = await getNodeFront("body", inspector);
const { nodes } = await inspector.walker.children(bodyNode);
const onInspectorUpdated = inspector.once("fontinspector-updated");
await selectNode(nodes[0], inspector);
info("Waiting for font editor to render");
await onInspectorUpdated;
const textFonts = getUsedFontsEls(viewDoc);
info("Select the <body> element");
await selectNode("body", inspector);
const parentFonts = getUsedFontsEls(viewDoc);
is(textFonts.length, parentFonts.length, "Font inspector shows same number of fonts");
});

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

@ -37,7 +37,7 @@ selectNode = async function(node, inspector, reason) {
*/
var openFontInspectorForURL = async function(url) {
const tab = await addTab(url);
const {toolbox, inspector} = await openInspector();
const {toolbox, inspector, testActor } = await openInspector();
// Call selectNode again here to force a fontinspector update since we don't
// know if the fontinspector-updated event has been sent while the inspector
@ -46,6 +46,7 @@ var openFontInspectorForURL = async function(url) {
return {
tab,
testActor,
toolbox,
inspector,
view: inspector.fontinspector
@ -204,3 +205,45 @@ function getURL(fontEl) {
function getFamilyName(fontEl) {
return fontEl.querySelector(".font-family-name").textContent;
}
/**
* Get the value and unit of a CSS font property or font axis from the font editor.
*
* @param {document} viewDoc
* Host document of the font inspector panel.
* @param {String} name
* Font property name or axis tag
* @return {Object}
* Object with the value and unit of the given font property or axis tag
* from the corresponding input fron the font editor.
* @Example:
* {
* value: {Number|String|null}
* unit: {String|null}
* }
*/
function getPropertyValue(viewDoc, name) {
const selector = `#font-editor .font-value-slider[name=${name}]`;
return {
// Ensure value input exists before querying its value
value: viewDoc.querySelector(selector) &&
parseFloat(viewDoc.querySelector(selector).value),
// Ensure unit dropdown exists before querying its value
unit: viewDoc.querySelector(selector + ` ~ .font-unit-select`) &&
viewDoc.querySelector(selector + ` ~ .font-unit-select`).value
};
}
/**
* Wait for a predicate to return a result.
*
* @param {Function} condition
* Invoked every 10ms for a maximum of 500 retries until it returns a truthy
* value.
* @return {Promise}
* A promise that is resolved with the result of the condition.
*/
async function waitFor(condition) {
await BrowserTestUtils.waitForCondition(condition, "waitFor", 10, 500);
return condition();
}

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

@ -9,7 +9,6 @@
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
let { Assert } = require("resource://testing-common/Assert.jsm");
const { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
const defer = require("devtools/shared/defer");
const flags = require("devtools/shared/flags");
let { TargetFactory } = require("devtools/client/framework/target");
let { Toolbox } = require("devtools/client/framework/toolbox");
@ -37,15 +36,15 @@ function onNextAnimationFrame(fn) {
}
function setState(component, newState) {
const deferred = defer();
component.setState(newState, onNextAnimationFrame(deferred.resolve));
return deferred.promise;
return new Promise(resolve => {
component.setState(newState, onNextAnimationFrame(resolve));
});
}
function setProps(component, newState) {
const deferred = defer();
component.setProps(newState, onNextAnimationFrame(deferred.resolve));
return deferred.promise;
return new Promise(resolve => {
component.setProps(newState, onNextAnimationFrame(resolve));
});
}
function dumpn(msg) {

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

@ -12,7 +12,6 @@ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget"
const MountainGraphWidget = require("devtools/client/shared/widgets/MountainGraphWidget");
const { CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");
const defer = require("devtools/shared/defer");
const EventEmitter = require("devtools/shared/event-emitter");
const { colorUtils } = require("devtools/shared/css/color");
@ -210,7 +209,7 @@ GraphsController.prototype = {
// until the previous render cycle completes, which can occur
// especially when a recording is finished, and triggers a
// fresh rendering at a higher rate
await (this._rendering && this._rendering.promise);
await this._rendering;
// Check after yielding to ensure we're not tearing down,
// as this can create a race condition in tests
@ -218,12 +217,14 @@ GraphsController.prototype = {
return;
}
this._rendering = defer();
for (const graph of (await this._getEnabled())) {
await graph.setPerformanceData(recordingData, resolution);
this.emit("rendered", graph.graphName);
}
this._rendering.resolve();
this._rendering = new Promise(async (resolve) => {
for (const graph of (await this._getEnabled())) {
await graph.setPerformanceData(recordingData, resolution);
this.emit("rendered", graph.graphName);
}
resolve();
});
await this._rendering;
},
/**
@ -241,7 +242,7 @@ GraphsController.prototype = {
// If there was rendering, wait until the most recent render cycle
// has finished
if (this._rendering) {
await this._rendering.promise;
await this._rendering;
}
for (const graph of this.getWidgets()) {

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

@ -5,8 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const defer = require("devtools/shared/defer");
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
function PerformancePanel(iframeWindow, toolbox) {
@ -30,8 +28,6 @@ PerformancePanel.prototype = {
if (this._opening) {
return this._opening;
}
const deferred = defer();
this._opening = deferred.promise;
this.panelWin.gToolbox = this.toolbox;
this.panelWin.gTarget = this.target;
@ -64,7 +60,9 @@ PerformancePanel.prototype = {
this.isReady = true;
this.emit("ready");
deferred.resolve(this);
this._opening = new Promise(resolve => {
resolve(this);
});
return this._opening;
},

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

@ -43,7 +43,6 @@ var RecordingListItem = React.createFactory(require("devtools/client/performance
var Services = require("Services");
var promise = require("promise");
const defer = require("devtools/shared/defer");
var EventEmitter = require("devtools/shared/event-emitter");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var flags = require("devtools/shared/flags");
@ -542,14 +541,14 @@ var PerformanceController = {
* @return {Promise}
*/
async waitForStateChangeOnRecording(recording, expectedState) {
const deferred = defer();
this.on(EVENTS.RECORDING_STATE_CHANGE, function handler(state, model) {
if (state === expectedState && model === recording) {
this.off(EVENTS.RECORDING_STATE_CHANGE, handler);
deferred.resolve();
}
await new Promise(resolve => {
this.on(EVENTS.RECORDING_STATE_CHANGE, function handler(state, model) {
if (state === expectedState && model === recording) {
this.off(EVENTS.RECORDING_STATE_CHANGE, handler);
resolve();
}
});
});
await deferred.promise;
},
/**

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

@ -38,19 +38,17 @@ function getUnicodeConverter() {
}
function asyncCopy(data, file) {
let deferred = defer();
let string = JSON.stringify(data);
let inputStream = getUnicodeConverter().convertToInputStream(string);
let outputStream = FileUtils.openSafeFileOutputStream(file);
NetUtil.asyncCopy(inputStream, outputStream, status => {
if (!Components.isSuccessCode(status)) {
deferred.reject(new Error("Could not save data to file."));
}
deferred.resolve();
return new Promise((resolve, reject) => {
NetUtil.asyncCopy(inputStream, outputStream, status => {
if (!Components.isSuccessCode(status)) {
reject(new Error("Could not save data to file."));
}
resolve();
});
});
return deferred.promise;
}
/* eslint-enable */

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

@ -160,19 +160,17 @@ function getUnicodeConverter() {
}
function asyncCopy(data, file) {
let deferred = defer();
let string = JSON.stringify(data);
let inputStream = getUnicodeConverter().convertToInputStream(string);
let outputStream = FileUtils.openSafeFileOutputStream(file);
NetUtil.asyncCopy(inputStream, outputStream, status => {
if (!Components.isSuccessCode(status)) {
deferred.reject(new Error("Could not save data to file."));
}
deferred.resolve();
return new Promise((resolve, reject) => {
NetUtil.asyncCopy(inputStream, outputStream, status => {
if (!Components.isSuccessCode(status)) {
reject(new Error("Could not save data to file."));
}
resolve();
});
});
return deferred.promise;
}
/* eslint-enable */

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

@ -124,19 +124,17 @@ function getUnicodeConverter() {
}
function asyncCopy(data, file) {
let deferred = defer();
let string = JSON.stringify(data);
let inputStream = getUnicodeConverter().convertToInputStream(string);
let outputStream = FileUtils.openSafeFileOutputStream(file);
NetUtil.asyncCopy(inputStream, outputStream, status => {
if (!Components.isSuccessCode(status)) {
deferred.reject(new Error("Could not save data to file."));
}
deferred.resolve();
return new Promise((resolve, reject) => {
NetUtil.asyncCopy(inputStream, outputStream, status => {
if (!Components.isSuccessCode(status)) {
reject(new Error("Could not save data to file."));
}
resolve();
});
});
return deferred.promise;
}
/* eslint-enable */

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

@ -73,25 +73,23 @@ function testWorkerMarkerUI(node) {
*/
function evalInDebuggee(script) {
let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
let deferred = defer();
if (!mm) {
throw new Error("`loadFrameScripts()` must be called when using MessageManager.");
}
return new Promise(resolve => {
let id = generateUUID().toString();
mm.sendAsyncMessage("devtools:test:eval", {script: script, id: id});
mm.addMessageListener("devtools:test:eval:response", handler);
let id = generateUUID().toString();
mm.sendAsyncMessage("devtools:test:eval", { script: script, id: id });
mm.addMessageListener("devtools:test:eval:response", handler);
function handler({data}) {
if (id !== data.id) {
return;
}
function handler({ data }) {
if (id !== data.id) {
return;
mm.removeMessageListener("devtools:test:eval:response", handler);
resolve(data.value);
}
mm.removeMessageListener("devtools:test:eval:response", handler);
deferred.resolve(data.value);
}
return deferred.promise;
});
}
/* eslint-enable */

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

@ -17,7 +17,7 @@ const Services = require("Services");
const { ChromeDebuggerActor } = require("devtools/server/actors/thread");
const { WebConsoleActor } = require("devtools/server/actors/webconsole");
const makeDebugger = require("devtools/server/actors/utils/make-debugger");
const { ActorPool } = require("devtools/server/main");
const { ActorPool } = require("devtools/server/actors/common");
const { assert } = require("devtools/shared/DevToolsUtils");
const { TabSources } = require("devtools/server/actors/utils/TabSources");

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

@ -10,7 +10,8 @@
const Services = require("Services");
const { Cc, Ci, Cu } = require("chrome");
const { DebuggerServer, ActorPool } = require("devtools/server/main");
const { DebuggerServer } = require("devtools/server/main");
const { ActorPool } = require("devtools/server/actors/common");
const { ThreadActor } = require("devtools/server/actors/thread");
const { ObjectActor } = require("devtools/server/actors/object");
const { LongStringActor } = require("devtools/server/actors/object/long-string");

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

@ -10,53 +10,28 @@
*/
var { Ci, Cc } = require("chrome");
var Services = require("Services");
var { ActorPool, OriginalLocation, RegisteredActorFactory,
var { ActorPool, RegisteredActorFactory,
ObservedActorFactory } = require("devtools/server/actors/common");
var { LocalDebuggerTransport, ChildDebuggerTransport, WorkerDebuggerTransport } =
require("devtools/shared/transport/transport");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var { dumpn } = DevToolsUtils;
DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => {
// eslint-disable-next-line no-shadow
const { DebuggerSocket } = require("devtools/shared/security/socket");
return DebuggerSocket;
});
DevToolsUtils.defineLazyGetter(this, "Authentication", () => {
return require("devtools/shared/security/auth");
});
DevToolsUtils.defineLazyGetter(this, "generateUUID", () => {
loader.lazyRequireGetter(this, "DebuggerSocket", "devtools/shared/security/socket", true);
loader.lazyRequireGetter(this, "Authentication", "devtools/shared/security/auth");
loader.lazyRequireGetter(this, "LocalDebuggerTransport", "devtools/shared/transport/local-transport", true);
loader.lazyRequireGetter(this, "ChildDebuggerTransport", "devtools/shared/transport/child-transport", true);
loader.lazyRequireGetter(this, "WorkerThreadWorkerDebuggerTransport", "devtools/shared/transport/worker-transport", true);
loader.lazyRequireGetter(this, "MainThreadWorkerDebuggerTransport", "devtools/shared/transport/worker-transport", true);
loader.lazyGetter(this, "generateUUID", () => {
// eslint-disable-next-line no-shadow
const { generateUUID } = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator);
return generateUUID;
});
// Overload `Components` to prevent DevTools loader exception on Components
// object usage
// eslint-disable-next-line no-unused-vars
Object.defineProperty(this, "Components", {
get() {
return require("chrome").components;
}
});
const CONTENT_PROCESS_SERVER_STARTUP_SCRIPT =
"resource://devtools/server/startup/content-process.js";
function loadSubScript(url) {
try {
Services.scriptloader.loadSubScript(url, this);
} catch (e) {
const errorStr = "Error loading: " + url + ":\n" +
(e.fileName ? "at " + e.fileName + " : " + e.lineNumber + "\n" : "") +
e + " - " + e.stack + "\n";
dump(errorStr);
reportError(errorStr);
throw e;
}
}
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
var gRegisteredModules = Object.create(null);
@ -159,7 +134,7 @@ var DebuggerServer = {
}
if (!this.rootlessServer && !this.createRootActor) {
throw new Error("Use DebuggerServer.addActors() to add a root actor " +
throw new Error("Use DebuggerServer.setRootActor() to add a root actor " +
"implementation.");
}
},
@ -200,18 +175,6 @@ var DebuggerServer = {
this.registerActors({ root: true, browser: true, target: true });
},
/**
* Load a subscript into the debugging global.
*
* @param url string A url that will be loaded as a subscript into the
* debugging global. The user must load at least one script
* that implements a createRootActor() function to create the
* server's root actor.
*/
addActors(url) {
loadSubScript.call(this, url);
},
/**
* Register a CommonJS module with the debugger server.
* @param id string
@ -603,7 +566,7 @@ var DebuggerServer = {
this._checkInit();
const transport = isWorker ?
new WorkerDebuggerTransport(scopeOrManager, prefix) :
new WorkerThreadWorkerDebuggerTransport(scopeOrManager, prefix) :
new ChildDebuggerTransport(scopeOrManager, prefix);
return this._onConnection(transport, prefix, true);
@ -772,7 +735,7 @@ var DebuggerServer = {
dbg.removeListener(listener);
// Step 7: Create a transport for the connection to the worker.
const transport = new WorkerDebuggerTransport(dbg, id);
const transport = new MainThreadWorkerDebuggerTransport(dbg, id);
transport.ready();
transport.hooks = {
onClosed: () => {
@ -1336,25 +1299,7 @@ DevToolsUtils.defineLazyGetter(DebuggerServer, "AuthenticationResult", () => {
EventEmitter.decorate(DebuggerServer);
if (this.exports) {
exports.DebuggerServer = DebuggerServer;
exports.ActorPool = ActorPool;
exports.OriginalLocation = OriginalLocation;
}
// Needed on B2G (See header note)
this.DebuggerServer = DebuggerServer;
this.ActorPool = ActorPool;
this.OriginalLocation = OriginalLocation;
// When using DebuggerServer.addActors, some symbols are expected to be in
// the scope of the added actor even before the corresponding modules are
// loaded, so let's explicitly bind the expected symbols here.
var includes = ["Components", "Ci", "Cu", "require", "Services", "DebuggerServer",
"ActorPool", "DevToolsUtils"];
includes.forEach(name => {
DebuggerServer[name] = this[name];
});
exports.DebuggerServer = DebuggerServer;
/**
* Creates a DebuggerServerConnection.

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

@ -78,7 +78,7 @@ function init(msg) {
const { ContentProcessTargetActor } =
loader.require("devtools/server/actors/targets/content-process");
const { ActorPool } = loader.require("devtools/server/main");
const { ActorPool } = loader.require("devtools/server/actors/common");
const actor = new ContentProcessTargetActor(conn);
const actorPool = new ActorPool(conn);
actorPool.addActor(actor);

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

@ -21,7 +21,8 @@ try {
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { dumpn } = DevToolsUtils;
const { DebuggerServer, ActorPool } = require("devtools/server/main");
const { DebuggerServer } = require("devtools/server/main");
const { ActorPool } = require("devtools/server/actors/common");
DebuggerServer.init();
// We want a special server without any root actor and only target-scoped actors.

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

@ -16,7 +16,11 @@ async function test() {
DebuggerServer.init();
DebuggerServer.registerAllActors();
DebuggerServer.addActors(ACTORS_URL);
DebuggerServer.registerModule(ACTORS_URL, {
prefix: "error",
constructor: "ErrorActor",
type: { global: true },
});
const transport = DebuggerServer.connectPipe();
const gClient = new DebuggerClient(transport);

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

@ -27,9 +27,4 @@ ErrorActor.prototype = {
ErrorActor.prototype.requestTypes = {
"error": ErrorActor.prototype.onError
};
DebuggerServer.removeGlobalActor("errorActor");
DebuggerServer.addGlobalActor({
constructorName: "ErrorActor",
constructorFun: ErrorActor,
}, "errorActor");
exports.ErrorActor = ErrorActor;

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

@ -18,8 +18,4 @@ PostInitGlobalActor.prototype = {
PostInitGlobalActor.prototype.requestTypes = {
"ping": PostInitGlobalActor.prototype.onPing,
};
DebuggerServer.addGlobalActor({
constructorName: "PostInitGlobalActor",
constructorFun: PostInitGlobalActor,
}, "postInitGlobalActor");
exports.PostInitGlobalActor = PostInitGlobalActor;

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

@ -9,7 +9,7 @@
function PostInitTargetScopedActor(connection) {}
PostInitTargetScopedActor.prototype = {
actorPostfix: "postInitTab",
actorPostfix: "postInitTargetScoped",
onPing(request) {
return { message: "pong" };
},
@ -18,8 +18,4 @@ PostInitTargetScopedActor.prototype = {
PostInitTargetScopedActor.prototype.requestTypes = {
"ping": PostInitTargetScopedActor.prototype.onPing,
};
DebuggerServer.addTargetScopedActor({
constructorName: "PostInitTargetScopedActor",
constructorFun: PostInitTargetScopedActor,
}, "postInitTargetScopedActor");
exports.PostInitTargetScopedActor = PostInitTargetScopedActor;

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

@ -18,8 +18,4 @@ PreInitGlobalActor.prototype = {
PreInitGlobalActor.prototype.requestTypes = {
"ping": PreInitGlobalActor.prototype.onPing,
};
DebuggerServer.addGlobalActor({
constructorName: "PreInitGlobalActor",
constructorFun: PreInitGlobalActor,
}, "preInitGlobalActor");
exports.PreInitGlobalActor = PreInitGlobalActor;

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

@ -9,7 +9,7 @@
function PreInitTargetScopedActor(connection) {}
PreInitTargetScopedActor.prototype = {
actorPrefix: "preInitTab",
actorPrefix: "preInitTargetScoped",
onPing(request) {
return { message: "pong" };
},
@ -18,8 +18,4 @@ PreInitTargetScopedActor.prototype = {
PreInitTargetScopedActor.prototype.requestTypes = {
"ping": PreInitTargetScopedActor.prototype.onPing,
};
DebuggerServer.addTargetScopedActor({
constructorName: "PreInitTargetScopedActor",
constructorFun: PreInitTargetScopedActor,
}, "preInitTargetScopedActor");
exports.PreInitTargetScopedActor = PreInitTargetScopedActor;

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

@ -16,13 +16,29 @@ function getActorInstance(connID, actorID) {
* regardless of the object's state.
*/
add_task(async function() {
DebuggerServer.addActors("resource://test/pre_init_global_actors.js");
DebuggerServer.addActors("resource://test/pre_init_target_scoped_actors.js");
DebuggerServer.registerModule("resource://test/pre_init_global_actors.js", {
prefix: "preInitGlobal",
constructor: "PreInitGlobalActor",
type: { global: true },
});
DebuggerServer.registerModule("resource://test/pre_init_target_scoped_actors.js", {
prefix: "preInitTargetScoped",
constructor: "PreInitTargetScopedActor",
type: { target: true },
});
const client = await startTestDebuggerServer("example tab");
DebuggerServer.addActors("resource://test/post_init_global_actors.js");
DebuggerServer.addActors("resource://test/post_init_target_scoped_actors.js");
DebuggerServer.registerModule("resource://test/post_init_global_actors.js", {
prefix: "postInitGlobal",
constructor: "PostInitGlobalActor",
type: { global: true },
});
DebuggerServer.registerModule("resource://test/post_init_target_scoped_actors.js", {
prefix: "postInitTargetScoped",
constructor: "PostInitTargetScopedActor",
type: { target: true },
});
let actors = await client.listTabs();
Assert.equal(actors.tabs.length, 1);

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

@ -0,0 +1,128 @@
/* 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/. */
"use strict";
const { Cr } = require("chrome");
const flags = require("devtools/shared/flags");
/**
* A transport for the debugging protocol that uses nsIMessageManagers to
* exchange packets with servers running in child processes.
*
* In the parent process, |mm| should be the nsIMessageSender for the
* child process. In a child process, |mm| should be the child process
* message manager, which sends packets to the parent.
*
* |prefix| is a string included in the message names, to distinguish
* multiple servers running in the same child process.
*
* This transport exchanges messages named 'debug:<prefix>:packet', where
* <prefix> is |prefix|, whose data is the protocol packet.
*/
function ChildDebuggerTransport(mm, prefix) {
this._mm = mm;
this._messageName = "debug:" + prefix + ":packet";
}
/*
* To avoid confusion, we use 'message' to mean something that
* nsIMessageSender conveys, and 'packet' to mean a remote debugging
* protocol packet.
*/
ChildDebuggerTransport.prototype = {
constructor: ChildDebuggerTransport,
hooks: null,
_addListener() {
this._mm.addMessageListener(this._messageName, this);
},
_removeListener() {
try {
this._mm.removeMessageListener(this._messageName, this);
} catch (e) {
if (e.result != Cr.NS_ERROR_NULL_POINTER) {
throw e;
}
// In some cases, especially when using messageManagers in non-e10s mode, we reach
// this point with a dead messageManager which only throws errors but does not
// seem to indicate in any other way that it is dead.
}
},
ready: function() {
this._addListener();
},
close: function() {
this._removeListener();
this.hooks.onClosed();
},
receiveMessage: function({data}) {
this.hooks.onPacket(data);
},
/**
* Helper method to ensure a given `object` can be sent across message manager
* without being serialized to JSON.
* See https://searchfox.org/mozilla-central/rev/6bfadf95b4a6aaa8bb3b2a166d6c3545983e179a/dom/base/nsFrameMessageManager.cpp#458-469
*/
_canBeSerialized: function(object) {
try {
const holder = new StructuredCloneHolder(object);
holder.deserialize(this);
} catch (e) {
return false;
}
return true;
},
pathToUnserializable: function(object) {
for (const key in object) {
const value = object[key];
if (!this._canBeSerialized(value)) {
if (typeof value == "object") {
return [key].concat(this.pathToUnserializable(value));
}
return [key];
}
}
return [];
},
send: function(packet) {
if (flags.testing && !this._canBeSerialized(packet)) {
const attributes = this.pathToUnserializable(packet);
let msg = "Following packet can't be serialized: " + JSON.stringify(packet);
msg += "\nBecause of attributes: " + attributes.join(", ") + "\n";
msg += "Did you pass a function or an XPCOM object in it?";
throw new Error(msg);
}
try {
this._mm.sendAsyncMessage(this._messageName, packet);
} catch (e) {
if (e.result != Cr.NS_ERROR_NULL_POINTER) {
throw e;
}
// In some cases, especially when using messageManagers in non-e10s mode, we reach
// this point with a dead messageManager which only throws errors but does not
// seem to indicate in any other way that it is dead.
}
},
startBulkSend: function() {
throw new Error("Can't send bulk data to child processes.");
},
swapBrowser(mm) {
this._removeListener();
this._mm = mm;
this._addListener();
},
};
exports.ChildDebuggerTransport = ChildDebuggerTransport;

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

@ -0,0 +1,190 @@
/* 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/. */
"use strict";
/* global uneval */
const { CC } = require("chrome");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { dumpn } = DevToolsUtils;
const flags = require("devtools/shared/flags");
const StreamUtils = require("devtools/shared/transport/stream-utils");
const promise = require("promise");
const defer = require("devtools/shared/defer");
loader.lazyGetter(this, "Pipe", () => {
return CC("@mozilla.org/pipe;1", "nsIPipe", "init");
});
/**
* An adapter that handles data transfers between the debugger client and
* server when they both run in the same process. It presents the same API as
* DebuggerTransport, but instead of transmitting serialized messages across a
* connection it merely calls the packet dispatcher of the other side.
*
* @param other LocalDebuggerTransport
* The other endpoint for this debugger connection.
*
* @see DebuggerTransport
*/
function LocalDebuggerTransport(other) {
this.other = other;
this.hooks = null;
// A packet number, shared between this and this.other. This isn't used by the
// protocol at all, but it makes the packet traces a lot easier to follow.
this._serial = this.other ? this.other._serial : { count: 0 };
this.close = this.close.bind(this);
}
LocalDebuggerTransport.prototype = {
/**
* Transmit a message by directly calling the onPacket handler of the other
* endpoint.
*/
send: function(packet) {
const serial = this._serial.count++;
if (flags.wantLogging) {
// Check 'from' first, as 'echo' packets have both.
if (packet.from) {
dumpn("Packet " + serial + " sent from " + uneval(packet.from));
} else if (packet.to) {
dumpn("Packet " + serial + " sent to " + uneval(packet.to));
}
}
this._deepFreeze(packet);
const other = this.other;
if (other) {
DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => {
// Avoid the cost of JSON.stringify() when logging is disabled.
if (flags.wantLogging) {
dumpn("Received packet " + serial + ": " + JSON.stringify(packet, null, 2));
}
if (other.hooks) {
other.hooks.onPacket(packet);
}
}, "LocalDebuggerTransport instance's this.other.hooks.onPacket"));
}
},
/**
* Send a streaming bulk packet directly to the onBulkPacket handler of the
* other endpoint.
*
* This case is much simpler than the full DebuggerTransport, since there is
* no primary stream we have to worry about managing while we hand it off to
* others temporarily. Instead, we can just make a single use pipe and be
* done with it.
*/
startBulkSend: function({actor, type, length}) {
const serial = this._serial.count++;
dumpn("Sent bulk packet " + serial + " for actor " + actor);
if (!this.other) {
const error = new Error("startBulkSend: other side of transport missing");
return promise.reject(error);
}
const pipe = new Pipe(true, true, 0, 0, null);
DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => {
dumpn("Received bulk packet " + serial);
if (!this.other.hooks) {
return;
}
// Receiver
const deferred = defer();
const packet = {
actor: actor,
type: type,
length: length,
copyTo: (output) => {
const copying =
StreamUtils.copyStream(pipe.inputStream, output, length);
deferred.resolve(copying);
return copying;
},
stream: pipe.inputStream,
done: deferred
};
this.other.hooks.onBulkPacket(packet);
// Await the result of reading from the stream
deferred.promise.then(() => pipe.inputStream.close(), this.close);
}, "LocalDebuggerTransport instance's this.other.hooks.onBulkPacket"));
// Sender
const sendDeferred = defer();
// The remote transport is not capable of resolving immediately here, so we
// shouldn't be able to either.
DevToolsUtils.executeSoon(() => {
const copyDeferred = defer();
sendDeferred.resolve({
copyFrom: (input) => {
const copying =
StreamUtils.copyStream(input, pipe.outputStream, length);
copyDeferred.resolve(copying);
return copying;
},
stream: pipe.outputStream,
done: copyDeferred
});
// Await the result of writing to the stream
copyDeferred.promise.then(() => pipe.outputStream.close(), this.close);
});
return sendDeferred.promise;
},
/**
* Close the transport.
*/
close: function() {
if (this.other) {
// Remove the reference to the other endpoint before calling close(), to
// avoid infinite recursion.
const other = this.other;
this.other = null;
other.close();
}
if (this.hooks) {
try {
this.hooks.onClosed();
} catch (ex) {
console.error(ex);
}
this.hooks = null;
}
},
/**
* An empty method for emulating the DebuggerTransport API.
*/
ready: function() {},
/**
* Helper function that makes an object fully immutable.
*/
_deepFreeze: function(object) {
Object.freeze(object);
for (const prop in object) {
// Freeze the properties that are objects, not on the prototype, and not
// already frozen. Note that this might leave an unfrozen reference
// somewhere in the object if there is an already frozen object containing
// an unfrozen object.
if (object.hasOwnProperty(prop) && typeof object === "object" &&
!Object.isFrozen(object)) {
this._deepFreeze(object[prop]);
}
}
}
};
exports.LocalDebuggerTransport = LocalDebuggerTransport;

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

@ -7,8 +7,11 @@
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
DevToolsModules(
'child-transport.js',
'local-transport.js',
'packets.js',
'stream-utils.js',
'transport.js',
'websocket-transport.js',
'worker-transport.js',
)

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,110 @@
/* 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/. */
"use strict";
// Each worker debugger supports only a single connection to the main thread.
// However, its theoretically possible for multiple servers to connect to the
// same worker. Consequently, each transport has a connection id, to allow
// messages from multiple connections to be multiplexed on a single channel.
/**
* A transport that uses a WorkerDebugger to send packets from the main
* thread to a worker thread.
*/
function MainThreadWorkerDebuggerTransport(dbg, id) {
this._dbg = dbg;
this._id = id;
this.onMessage = this._onMessage.bind(this);
}
MainThreadWorkerDebuggerTransport.prototype = {
constructor: MainThreadWorkerDebuggerTransport,
ready: function() {
this._dbg.addListener(this);
},
close: function() {
this._dbg.removeListener(this);
if (this.hooks) {
this.hooks.onClosed();
}
},
send: function(packet) {
this._dbg.postMessage(JSON.stringify({
type: "message",
id: this._id,
message: packet
}));
},
startBulkSend: function() {
throw new Error("Can't send bulk data from worker threads!");
},
_onMessage: function(message) {
const packet = JSON.parse(message);
if (packet.type !== "message" || packet.id !== this._id) {
return;
}
if (this.hooks) {
this.hooks.onPacket(packet.message);
}
}
};
exports.MainThreadWorkerDebuggerTransport = MainThreadWorkerDebuggerTransport;
/**
* A transport that uses a WorkerDebuggerGlobalScope to send packets from a
* worker thread to the main thread.
*/
function WorkerThreadWorkerDebuggerTransport(scope, id) {
this._scope = scope;
this._id = id;
this._onMessage = this._onMessage.bind(this);
}
WorkerThreadWorkerDebuggerTransport.prototype = {
constructor: WorkerThreadWorkerDebuggerTransport,
ready: function() {
this._scope.addEventListener("message", this._onMessage);
},
close: function() {
this._scope.removeEventListener("message", this._onMessage);
if (this.hooks) {
this.hooks.onClosed();
}
},
send: function(packet) {
this._scope.postMessage(JSON.stringify({
type: "message",
id: this._id,
message: packet
}));
},
startBulkSend: function() {
throw new Error("Can't send bulk data from worker threads!");
},
_onMessage: function(event) {
const packet = JSON.parse(event.data);
if (packet.type !== "message" || packet.id !== this._id) {
return;
}
if (this.hooks) {
this.hooks.onPacket(packet.message);
}
}
};
exports.WorkerThreadWorkerDebuggerTransport = WorkerThreadWorkerDebuggerTransport;

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

@ -78,7 +78,7 @@ DocGroup::ReportPerformanceInfo()
#else
uint32_t pid = getpid();
#endif
uint64_t pwid = 0;
uint64_t windowID = 0;
uint16_t count = 0;
uint64_t duration = 0;
bool isTopLevel = false;
@ -88,8 +88,17 @@ DocGroup::ReportPerformanceInfo()
for (const auto& document : *this) {
nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
MOZ_ASSERT(doc);
nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
if (!docURI) {
continue;
}
docURI->GetHost(host);
// If the host is empty, using the url
if (host.IsEmpty()) {
host = docURI->GetSpecOrDefault();
}
// looking for the top level document URI
nsPIDOMWindowInner* win = doc->GetInnerWindow();
nsPIDOMWindowOuter* win = doc->GetWindow();
if (!win) {
continue;
}
@ -101,20 +110,12 @@ DocGroup::ReportPerformanceInfo()
if (!top) {
continue;
}
nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
if (!docURI) {
continue;
}
pwid = top->WindowID();
windowID = top->WindowID();
isTopLevel = outer->IsTopLevelWindow();
docURI->GetHost(host);
// If the host is empty, using the url
if (host.IsEmpty()) {
docURI->GetSpec(host);
}
break;
}
MOZ_ASSERT(!host.IsEmpty());
duration = mPerformanceCounter->GetExecutionDuration();
FallibleTArray<CategoryDispatch> items;
@ -125,11 +126,11 @@ DocGroup::ReportPerformanceInfo()
CategoryDispatch item = CategoryDispatch(index, count);
if (!items.AppendElement(item, fallible)) {
NS_ERROR("Could not complete the operation");
return PerformanceInfo(host, pid, pwid, duration, false, isTopLevel, items);
return PerformanceInfo(host, pid, windowID, duration, false, isTopLevel, items);
}
}
return PerformanceInfo(host, pid, pwid, duration, false, isTopLevel, items);
return PerformanceInfo(host, pid, windowID, duration, false, isTopLevel, items);
}
nsresult

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

@ -295,8 +295,7 @@ nsCommandParams::RemoveValue(const char* aName)
nsCommandParams::HashEntry*
nsCommandParams::GetNamedEntry(const char* aName) const
{
return static_cast<HashEntry*>(
const_cast<PLDHashTable&>(mValuesHash).Search((void*)aName));
return static_cast<HashEntry*>(mValuesHash.Search((void*)aName));
}
nsCommandParams::HashEntry*

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

@ -67,6 +67,7 @@ TextComposition::TextComposition(nsPresContext* aPresContext,
, mIsRequestingCommit(false)
, mIsRequestingCancel(false)
, mRequestedToCommitOrCancel(false)
, mHasDispatchedDOMTextEvent(false)
, mHasReceivedCommitEvent(false)
, mWasNativeCompositionEndEventDiscarded(false)
, mAllowControlCharacters(
@ -108,6 +109,13 @@ TextComposition::MaybeDispatchCompositionUpdate(
return false;
}
// Note that we don't need to dispatch eCompositionUpdate event even if
// mHasDispatchedDOMTextEvent is false and eCompositionCommit event is
// dispatched with empty string immediately after eCompositionStart
// because composition string has never been changed from empty string to
// non-empty string in such composition even if selected string was not
// empty string (mLastData isn't set to selected text when this receives
// eCompositionStart).
if (mLastData == aCompositionEvent->mData) {
return true;
}
@ -356,10 +364,15 @@ TextComposition::DispatchCompositionEvent(
// When mIsComposing is false but the committing string is different from
// the last data (E.g., previous eCompositionChange event made the
// composition string empty or didn't have clause information), we don't
// need to dispatch redundant DOM text event.
// need to dispatch redundant DOM text event. (But note that we need to
// dispatch eCompositionChange event if we have not dispatched
// eCompositionChange event yet and commit string replaces selected string
// with empty string since selected string hasn't been replaced with empty
// string yet.)
if (dispatchDOMTextEvent &&
aCompositionEvent->mMessage != eCompositionChange &&
!mIsComposing && mLastData == aCompositionEvent->mData) {
!mIsComposing && mHasDispatchedDOMTextEvent &&
mLastData == aCompositionEvent->mData) {
dispatchEvent = dispatchDOMTextEvent = false;
}
@ -387,10 +400,14 @@ TextComposition::DispatchCompositionEvent(
// we cannot map multiple event messages to a DOM event type.
if (dispatchDOMTextEvent &&
aCompositionEvent->mMessage != eCompositionChange) {
mHasDispatchedDOMTextEvent = true;
aCompositionEvent->mFlags =
CloneAndDispatchAs(aCompositionEvent, eCompositionChange,
aStatus, aCallBack);
} else {
if (aCompositionEvent->mMessage == eCompositionChange) {
mHasDispatchedDOMTextEvent = true;
}
DispatchEvent(aCompositionEvent, aStatus, aCallBack);
}
} else {

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

@ -369,6 +369,9 @@ private:
// mIsRequestingCancel are set to false.
bool mRequestedToCommitOrCancel;
// Set to true if the instance dispatches an eCompositionChange event.
bool mHasDispatchedDOMTextEvent;
// Before this dispatches commit event into the tree, this is set to true.
// So, this means if native IME already commits the composition.
bool mHasReceivedCommitEvent;

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

@ -153,12 +153,6 @@ public:
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return false;
}
for (uint32_t i = 0; i < aEvent.mCurveLength; ++i) {
if (!IsValid(aEvent.mCurve[i])) {
aRv.Throw(NS_ERROR_TYPE_ERR);
return false;
}
}
}
bool timeAndValueValid = IsValid(aEvent.mValue) &&

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

@ -228,6 +228,13 @@ AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE],
AudioBufferInPlaceScale(aBlock, aScale, WEBAUDIO_BLOCK_SIZE);
}
void
AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE],
float aScale[WEBAUDIO_BLOCK_SIZE])
{
AudioBufferInPlaceScale(aBlock, aScale, WEBAUDIO_BLOCK_SIZE);
}
void
AudioBufferInPlaceScale(float* aBlock,
float aScale,
@ -255,6 +262,30 @@ AudioBufferInPlaceScale(float* aBlock,
}
}
void
AudioBufferInPlaceScale(float* aBlock,
float* aScale,
uint32_t aSize)
{
#ifdef BUILD_ARM_NEON
if (mozilla::supports_neon()) {
AudioBufferInPlaceScale_NEON(aBlock, aScale, aSize);
return;
}
#endif
#ifdef USE_SSE2
if (mozilla::supports_sse2()) {
AudioBufferInPlaceScale_SSE(aBlock, aScale, aSize);
return;
}
#endif
for (uint32_t i = 0; i < aSize; ++i) {
*aBlock++ *= *aScale++;
}
}
void
AudioBlockPanMonoToStereo(const float aInput[WEBAUDIO_BLOCK_SIZE],
float aGainL[WEBAUDIO_BLOCK_SIZE],

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

@ -209,6 +209,18 @@ void AudioBufferInPlaceScale(float* aBlock,
float aScale,
uint32_t aSize);
/**
* a-rate in place gain.
*/
void AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE],
float aScale[WEBAUDIO_BLOCK_SIZE]);
/**
* a-rate in place gain.
*/
void AudioBufferInPlaceScale(float* aBlock,
float* aScale,
uint32_t aSize);
/**
* Upmix a mono input to a stereo output, scaling the two output channels by two
* different gain value.

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

@ -161,6 +161,47 @@ AudioBufferInPlaceScale_NEON(float* aBlock,
}
}
void
AudioBufferInPlaceScale_NEON(float* aBlock,
float* aScale,
uint32_t aSize)
{
ASSERT_ALIGNED(aBlock);
float32x4_t vin0, vin1, vin2, vin3;
float32x4_t vout0, vout1, vout2, vout3;
float32x4_t vscale0, vscale1, vscale2, vscale3;
uint32_t dif = aSize % 16;
uint32_t vectorSize = aSize - dif;
uint32_t i = 0;
for (; i < vectorSize; i+=16) {
vin0 = vld1q_f32(ADDRESS_OF(aBlock, i));
vin1 = vld1q_f32(ADDRESS_OF(aBlock, i+4));
vin2 = vld1q_f32(ADDRESS_OF(aBlock, i+8));
vin3 = vld1q_f32(ADDRESS_OF(aBlock, i+12));
vscale0 = vld1q_f32(ADDRESS_OF(aScale, i));
vscale1 = vld1q_f32(ADDRESS_OF(aScale, i+4));
vscale2 = vld1q_f32(ADDRESS_OF(aScale, i+8));
vscale3 = vld1q_f32(ADDRESS_OF(aScale, i+12));
vout0 = vmulq_f32(vin0, vscale0);
vout1 = vmulq_f32(vin1, vscale1);
vout2 = vmulq_f32(vin2, vscale2);
vout3 = vmulq_f32(vin3, vscale3);
vst1q_f32(ADDRESS_OF(aBlock, i), vout0);
vst1q_f32(ADDRESS_OF(aBlock, i+4), vout1);
vst1q_f32(ADDRESS_OF(aBlock, i+8), vout2);
vst1q_f32(ADDRESS_OF(aBlock, i+12), vout3);
}
for (unsigned j = 0; j < dif; ++i, ++j) {
aBlock[i] *= aScale[i];
}
}
void
AudioBlockPanStereoToStereo_NEON(const float aInputL[WEBAUDIO_BLOCK_SIZE],
const float aInputR[WEBAUDIO_BLOCK_SIZE],

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

@ -28,6 +28,10 @@ void
AudioBufferInPlaceScale_NEON(float* aBlock,
float aScale,
uint32_t aSize);
void
AudioBufferInPlaceScale_NEON(float* aBlock,
float* aScale,
uint32_t aSize);
void
AudioBlockPanStereoToStereo_NEON(const float aInputL[WEBAUDIO_BLOCK_SIZE],

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

@ -148,6 +148,38 @@ AudioBufferInPlaceScale_SSE(float* aBlock,
}
}
void
AudioBufferInPlaceScale_SSE(float* aBlock,
float* aScale,
uint32_t aSize)
{
__m128 vout0, vout1, vout2, vout3,
vgain0, vgain1, vgain2, vgain3,
vin0, vin1, vin2, vin3;
ASSERT_ALIGNED16(aBlock);
ASSERT_MULTIPLE16(aSize);
for (unsigned i = 0; i < aSize; i+=16) {
vin0 = _mm_load_ps(&aBlock[i]);
vin1 = _mm_load_ps(&aBlock[i + 4]);
vin2 = _mm_load_ps(&aBlock[i + 8]);
vin3 = _mm_load_ps(&aBlock[i + 12]);
vgain0 = _mm_load_ps(&aScale[i]);
vgain1 = _mm_load_ps(&aScale[i + 4]);
vgain2 = _mm_load_ps(&aScale[i + 8]);
vgain3 = _mm_load_ps(&aScale[i + 12]);
vout0 = _mm_mul_ps(vin0, vgain0);
vout1 = _mm_mul_ps(vin1, vgain1);
vout2 = _mm_mul_ps(vin2, vgain2);
vout3 = _mm_mul_ps(vin3, vgain3);
_mm_store_ps(&aBlock[i], vout0);
_mm_store_ps(&aBlock[i + 4], vout1);
_mm_store_ps(&aBlock[i + 8], vout2);
_mm_store_ps(&aBlock[i + 12], vout3);
}
}
void
AudioBlockPanStereoToStereo_SSE(const float aInputL[WEBAUDIO_BLOCK_SIZE],
const float aInputR[WEBAUDIO_BLOCK_SIZE],

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

@ -26,6 +26,10 @@ void
AudioBufferInPlaceScale_SSE(float* aBlock,
float aScale,
uint32_t aSize);
void
AudioBufferInPlaceScale_SSE(float* aBlock,
float* aScale,
uint32_t aSize);
void
AudioBlockPanStereoToStereo_SSE(const float aInputL[WEBAUDIO_BLOCK_SIZE],

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

@ -45,7 +45,7 @@ public:
// We override SetValueCurveAtTime to convert the Float32Array to the wrapper
// object.
AudioParam* SetValueCurveAtTime(const Float32Array& aValues,
AudioParam* SetValueCurveAtTime(const nsTArray<float>& aValues,
double aStartTime,
double aDuration,
ErrorResult& aRv)
@ -54,11 +54,9 @@ public:
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return this;
}
aValues.ComputeLengthAndData();
aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
EventInsertionHelper(aRv, AudioTimelineEvent::SetValueCurve,
aStartTime, 0.0f, 0.0f, aDuration, aValues.Data(),
aStartTime, 0.0f, 0.0f, aDuration, aValues.Elements(),
aValues.Length());
return this;
}

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

@ -567,11 +567,16 @@ PannerNodeEngine::EqualPowerPanningFunction(const AudioBlock& aInput,
orientationZ[0] = mOrientationZ.GetValueAtTime(tick);
}
float computedGain[2*WEBAUDIO_BLOCK_SIZE + 4];
float buffer[3*WEBAUDIO_BLOCK_SIZE + 4];
bool onLeft[WEBAUDIO_BLOCK_SIZE];
float* alignedComputedGain = ALIGNED16(computedGain);
ASSERT_ALIGNED16(alignedComputedGain);
float* alignedPanningL = ALIGNED16(buffer);
float* alignedPanningR = alignedPanningL + WEBAUDIO_BLOCK_SIZE;
float* alignedGain = alignedPanningR + WEBAUDIO_BLOCK_SIZE;
ASSERT_ALIGNED16(alignedPanningL);
ASSERT_ALIGNED16(alignedPanningR);
ASSERT_ALIGNED16(alignedGain);
for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
ThreeDPoint position(mPositionX.HasSimpleValue() ? positionX[0] : positionX[counter],
mPositionY.HasSimpleValue() ? positionY[0] : positionY[counter],
@ -611,17 +616,24 @@ PannerNodeEngine::EqualPowerPanningFunction(const AudioBlock& aInput,
distanceGain = ComputeDistanceGain(position);
// Actually compute the left and right gain.
float gainL = cos(0.5 * M_PI * normalizedAzimuth) * aInput.mVolume * distanceGain * coneGain;
float gainR = sin(0.5 * M_PI * normalizedAzimuth) * aInput.mVolume * distanceGain * coneGain;
float gainL = cos(0.5 * M_PI * normalizedAzimuth);
float gainR = sin(0.5 * M_PI * normalizedAzimuth);
alignedComputedGain[counter] = gainL;
alignedComputedGain[WEBAUDIO_BLOCK_SIZE + counter] = gainR;
alignedPanningL[counter] = gainL;
alignedPanningR[counter] = gainR;
alignedGain[counter] = aInput.mVolume * distanceGain * coneGain;
onLeft[counter] = azimuth <= 0;
}
// Apply the gain to the output buffer
ApplyStereoPanning(aInput, aOutput, alignedComputedGain, &alignedComputedGain[WEBAUDIO_BLOCK_SIZE], onLeft);
// Apply the panning to the output buffer
ApplyStereoPanning(aInput, aOutput, alignedPanningL, alignedPanningR, onLeft);
// Apply the input volume, cone and distance gain to the output buffer.
float* outputL = aOutput->ChannelFloatsForWrite(0);
float* outputR = aOutput->ChannelFloatsForWrite(1);
AudioBlockInPlaceScale(outputL, alignedGain);
AudioBlockInPlaceScale(outputR, alignedGain);
}
}

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

@ -162,9 +162,6 @@ TEST(AudioEventTimeline, InvalidEvents)
Timeline timeline(10.0f);
float curve[] = { -1.0f, 0.0f, 1.0f };
float badCurve1[] = { -1.0f, NaN, 1.0f };
float badCurve2[] = { -1.0f, Infinity, 1.0f };
float badCurve3[] = { -1.0f, -Infinity, 1.0f };
ErrorResultMock rv;
@ -202,12 +199,6 @@ TEST(AudioEventTimeline, InvalidEvents)
is(rv, NS_ERROR_DOM_SYNTAX_ERR, "Correct error code returned");
timeline.SetValueCurveAtTime(nullptr, 0, 1.0, 1.0, rv);
is(rv, NS_ERROR_DOM_SYNTAX_ERR, "Correct error code returned");
timeline.SetValueCurveAtTime(badCurve1, ArrayLength(badCurve1), 1.0, 1.0, rv);
is(rv, NS_ERROR_TYPE_ERR, "Correct error code returned");
timeline.SetValueCurveAtTime(badCurve2, ArrayLength(badCurve2), 1.0, 1.0, rv);
is(rv, NS_ERROR_TYPE_ERR, "Correct error code returned");
timeline.SetValueCurveAtTime(badCurve3, ArrayLength(badCurve3), 1.0, 1.0, rv);
is(rv, NS_ERROR_TYPE_ERR, "Correct error code returned");
timeline.SetValueCurveAtTime(curve, ArrayLength(curve), NaN, 1.0, rv);
is(rv, NS_ERROR_DOM_SYNTAX_ERR, "Correct error code returned");
timeline.SetValueCurveAtTime(curve, ArrayLength(curve), Infinity, 1.0, rv);

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

@ -198,6 +198,7 @@ skip-if = toolkit == 'android' # bug 1091965
[test_oscillatorTypeChange.html]
[test_pannerNode.html]
[test_pannerNode_equalPower.html]
[test_pannerNode_audioparam_distance.html]
[test_pannerNodeAbove.html]
[test_pannerNodeAtZeroDistance.html]
[test_pannerNodeChannelCount.html]

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

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Distance effect of a PannerNode with the position set via AudioParams (Bug 1472550)</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="webaudio.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
var o = new OfflineAudioContext(2, 256, 44100);
// We want a stereo constant source.
var b = o.createBuffer(2, 1, 44100);
b.getChannelData(0)[0] = 1;
b.getChannelData(1)[0] = 1;
var c = o.createBufferSource();
c.buffer = b;
c.loop = true;
var p = o.createPanner();
p.positionY.setValueAtTime(1, 0);
p.positionX.setValueAtTime(1, 0);
p.positionZ.setValueAtTime(1, 0);
// Set the listener somewhere far
o.listener.setPosition(20, 2, 20);
c.start();
c.connect(p).connect(o.destination);
o.startRendering().then((ab) => {
// Check that the distance attenuates the sound.
ok(ab.getChannelData(0)[0] < 0.1, "left channel must be very quiet");
ok(ab.getChannelData(1)[0] < 0.1, "right channel must be very quiet");
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

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

@ -54,8 +54,8 @@ function selection_is(s, text)
function deselect()
{
// Click outside text to deselect.
click(1, 1);
// Click outside text (and outside all <rect> elements>) to deselect.
click(15, 15);
selection_is("", "deselecting by clicking outside text");
}

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

@ -11,6 +11,7 @@ support-files =
geo_leak_test.html
dummy.html
ping_worker.html
ping_worker2.html
test_largeAllocation.html
test_largeAllocation.html^headers^
test_largeAllocation2.html

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

@ -7,6 +7,7 @@
const ROOT_URL = "http://example.com/browser/dom/tests/browser";
const DUMMY_URL = ROOT_URL + "/dummy.html";
const WORKER_URL = ROOT_URL + "/ping_worker.html";
const WORKER_URL2 = ROOT_URL + "/ping_worker2.html";
let nextId = 0;
@ -52,8 +53,11 @@ add_task(async function test() {
gBrowser, opening: "about:memory", forceNewProcess: false
});
let page3 = await BrowserTestUtils.openNewForegroundTab({
gBrowser, opening: WORKER_URL
});
// load a 4th tab with a worker
await BrowserTestUtils.withNewTab({ gBrowser, url: WORKER_URL },
await BrowserTestUtils.withNewTab({ gBrowser, url: WORKER_URL2 },
async function(browser) {
// grab events..
let workerDuration = 0;
@ -64,9 +68,22 @@ add_task(async function test() {
let aboutMemoryFound = false;
let parentProcessEvent = false;
let workerEvent = false;
let subFrameIds = [];
let topLevelIds = [];
let sharedWorker = false;
function exploreResults(data) {
for (let entry of data) {
sharedWorker = entry.host.endsWith("shared_worker.js") || sharedWorker;
Assert.ok(entry.host != "" || entry.windowId !=0,
"An entry should have a host or a windowId");
if (entry.windowId != 0 && !entry.isToplevel && !entry.isWorker && !subFrameIds.includes(entry.windowId)) {
subFrameIds.push(entry.windowId);
}
if (entry.isTopLevel && !topLevelIds.includes(entry.windowId)) {
topLevelIds.push(entry.windowId);
}
if (entry.host == "example.com" && entry.isTopLevel) {
isTopLevel = true;
}
@ -84,6 +101,7 @@ add_task(async function test() {
}
// let's look at the data we got back
for (let item of entry.items) {
Assert.ok(item.count > 0, "Categories with an empty count are dropped");
if (entry.isWorker) {
workerTotal += item.count;
} else {
@ -104,6 +122,12 @@ add_task(async function test() {
Assert.ok(parentProcessEvent, "parent process sent back some events");
Assert.ok(isTopLevel, "example.com as a top level window");
Assert.ok(aboutMemoryFound, "about:memory");
Assert.ok(sharedWorker, "We got some info from a shared worker");
// checking that subframes are not orphans
for (let frameId of subFrameIds) {
Assert.ok(topLevelIds.includes(frameId), "subframe is not orphan ");
}
// Doing a second call, we shoud get bigger values
let previousWorkerDuration = workerDuration;
@ -122,5 +146,6 @@ add_task(async function test() {
BrowserTestUtils.removeTab(page1);
BrowserTestUtils.removeTab(page2);
BrowserTestUtils.removeTab(page3);
SpecialPowers.clearUserPref("dom.performance.enable_scheduler_timing");
});

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

@ -5,14 +5,22 @@
<script type="text/javascript">
var myWorker;
var shared;
function init() {
myWorker = new Worker('ping_worker.js');
for (let i = 0; i++; i < 10) myWorker.postMessage("ping");
shared = new SharedWorker('shared_worker.js');
shared.port.start();
shared.port.onmessage = function(e) {
console.log(e);
}
}
</script>
</head>
<body onload="init()">
<h1>A page with a worker</h1>
<h1>A page with a worker and a shared worker</h1>
</body>
</html>

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

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<script type="text/javascript">
var shared;
function init() {
shared = new SharedWorker('shared_worker.js');
shared.port.start();
for (let i = 0; i++; i < 10) shared.port.postMessage(["ok"]);
}
</script>
</head>
<body onload="init()">
<h1>A page with a shared worker</h1>
</body>
</html>

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

@ -0,0 +1,9 @@
onconnect = function(e) {
var port = e.ports[0];
port.onmessage = function(e) {
var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
port.postMessage(e.data[0]);
}
}

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

@ -33,7 +33,7 @@ interface AudioParam {
// Sets an array of arbitrary parameter values starting at time for the given duration.
// The number of values will be scaled to fit into the desired duration.
[Throws]
AudioParam setValueCurveAtTime(Float32Array values, double startTime, double duration);
AudioParam setValueCurveAtTime(sequence<float> values, double startTime, double duration);
// Cancels all scheduled parameter changes with times greater than or equal to startTime.
[Throws]

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

@ -485,19 +485,28 @@ WorkerDebugger::ReportPerformanceInfo()
uint32_t pid = getpid();
#endif
bool isTopLevel= false;
uint64_t pwid = 0;
nsPIDOMWindowInner* win = mWorkerPrivate->GetWindow();
uint64_t windowID = mWorkerPrivate->WindowID();
// Walk up to our containing page and its window
WorkerPrivate* wp = mWorkerPrivate;
while (wp->GetParent()) {
wp = wp->GetParent();
}
nsPIDOMWindowInner* win = wp->GetWindow();
if (win) {
nsPIDOMWindowOuter* outer = win->GetOuterWindow();
if (outer) {
nsCOMPtr<nsPIDOMWindowOuter> top = outer->GetTop();
if (top) {
pwid = top->WindowID();
isTopLevel = pwid == mWorkerPrivate->WindowID();
windowID = top->WindowID();
isTopLevel = outer->IsTopLevelWindow();
}
}
}
// getting the worker URL
RefPtr<nsIURI> scriptURI = mWorkerPrivate->GetResolvedScriptURI();
nsCString url = scriptURI->GetSpecOrDefault();
// Workers only produce metrics for a single category - DispatchCategory::Worker.
// We still return an array of CategoryDispatch so the PerformanceInfo
@ -505,7 +514,6 @@ WorkerDebugger::ReportPerformanceInfo()
FallibleTArray<CategoryDispatch> items;
uint64_t duration = 0;
uint16_t count = 0;
RefPtr<nsIURI> uri = mWorkerPrivate->GetResolvedScriptURI();
RefPtr<PerformanceCounter> perf = mWorkerPrivate->GetPerformanceCounter();
if (perf) {
@ -514,13 +522,11 @@ WorkerDebugger::ReportPerformanceInfo()
CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
if (!items.AppendElement(item, fallible)) {
NS_ERROR("Could not complete the operation");
return PerformanceInfo(uri->GetSpecOrDefault(), pid, pwid, duration,
true, isTopLevel, items);
return PerformanceInfo(url, pid, windowID, duration, true, isTopLevel, items);
}
}
return PerformanceInfo(uri->GetSpecOrDefault(), pid, pwid, duration,
true, isTopLevel, items);
return PerformanceInfo(url, pid, windowID, duration, true, isTopLevel, items);
}
} // dom namespace

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

@ -51,6 +51,15 @@ SimpleTest.waitForFocus(()=>{
clear();
// FYI: Chrome commits composition if blur() and focus() are called during
// composition. But note that if they are called by compositionupdate
// listener, the behavior is unstable. On Windows, composition is
// canceled. On Linux and macOS, the composition is committed
// internally but the string keeps underlined. If they are called
// by input event listener, committed on any platforms though.
// On the other hand, Edge and Safari keeps composition even with
// calling both blur() and focus().
// Committing at compositionstart
aEditor.focus();
aEditor.addEventListener("compositionstart", committer, true);
@ -68,7 +77,7 @@ SimpleTest.waitForFocus(()=>{
caret: { start: 1, length: 0 }, key: { key: "a" }});
aEditor.removeEventListener("compositionupdate", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by compositionupdate event handler");
is(value(), "", "composition in " + aEditor.id + " shouldn't have inserted any text since it's committed at first compositionupdate");
is(value(), "a", "composition in " + aEditor.id + " should have \"a\" since IME committed with it");
clear();
// Committing at first text (eCompositionChange)
@ -93,7 +102,7 @@ SimpleTest.waitForFocus(()=>{
caret: { start: 2, length: 0 }, key: { key: "b" }});
aEditor.removeEventListener("compositionupdate", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by compositionupdate event handler");
todo_is(value(), "a", "composition in " + aEditor.id + " shouldn't have been modified since it's committed at second compositionupdate");
is(value(), "ab", "composition in " + aEditor.id + " should have \"ab\" since IME committed with it");
clear();
// Committing at second text (eCompositionChange)
@ -108,7 +117,7 @@ SimpleTest.waitForFocus(()=>{
caret: { start: 2, length: 0 }, key: { key: "b" }});
aEditor.removeEventListener("text", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by text event handler");
todo_is(value(), "a", "composition in " + aEditor.id + " shouldn't have been modified since it's committed at second text");
is(value(), "ab", "composition in " + aEditor.id + " should have \"ab\" since IME committed with it");
clear();
}
runTest(document.getElementById("input"));

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

@ -12,6 +12,7 @@
#include "nsDebug.h" // for NS_ERROR
#include "nsPoint.h" // for nsPoint
#include "nsRect.h" // for nsRect
#include "nsRectAbsolute.h" // for nsRectAbsolute
#include "base/basictypes.h"
using namespace mozilla::gfx;
@ -68,6 +69,17 @@ AppendToString(std::stringstream& aStream, const nsRect& r,
aStream << sfx;
}
void
AppendToString(std::stringstream& aStream, const nsRectAbsolute& r,
const char* pfx, const char* sfx)
{
aStream << pfx;
aStream << nsPrintfCString(
"(l=%d, t=%d, r=%d, b=%d)",
r.Left(), r.Top(), r.Right(), r.Bottom()).get();
aStream << sfx;
}
void
AppendToString(std::stringstream& aStream, const wr::ColorF& c,
const char* pfx, const char* sfx)

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

@ -19,6 +19,8 @@
#include "nsRegion.h" // for nsRegion, nsIntRegion
#include "nscore.h" // for nsACString, etc
struct nsRectAbsolute;
namespace mozilla {
namespace gfx {
@ -49,6 +51,10 @@ void
AppendToString(std::stringstream& aStream, const nsRect& r,
const char* pfx="", const char* sfx="");
void
AppendToString(std::stringstream& aStream, const nsRectAbsolute& r,
const char* pfx="", const char* sfx="");
template<class T>
void
AppendToString(std::stringstream& aStream, const mozilla::gfx::PointTyped<T>& p,
@ -97,6 +103,30 @@ AppendToString(std::stringstream& aStream, const mozilla::gfx::IntRectTyped<T>&
aStream << sfx;
}
template<class T>
void
AppendToString(std::stringstream& aStream, const mozilla::gfx::RectAbsoluteTyped<T>& r,
const char* pfx="", const char* sfx="")
{
aStream << pfx;
aStream << nsPrintfCString(
"(l=%f, t=%f, r=%f, b=%f)",
r.Left(), r.Top(), r.Right(), r.Bottom()).get();
aStream << sfx;
}
template<class T>
void
AppendToString(std::stringstream& aStream, const mozilla::gfx::IntRectAbsoluteTyped<T>& r,
const char* pfx="", const char* sfx="")
{
aStream << pfx;
aStream << nsPrintfCString(
"(l=%d, t=%d, r=%d, b=%d)",
r.Left(), r.Top(), r.Right(), r.Bottom()).get();
aStream << sfx;
}
void
AppendToString(std::stringstream& aStream, const wr::ColorF& c,
const char* pfx="", const char* sfx="");

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

@ -74,6 +74,16 @@ RecenterDisplayPort(mozilla::layers::FrameMetrics& aFrameMetrics)
aFrameMetrics.SetDisplayPortMargins(margins);
}
static already_AddRefed<nsIPresShell>
GetPresShell(const nsIContent* aContent)
{
nsCOMPtr<nsIPresShell> result;
if (nsIDocument* doc = aContent->GetComposedDoc()) {
result = doc->GetShell();
}
return result.forget();
}
static CSSPoint
ScrollFrameTo(nsIScrollableFrame* aFrame, const FrameMetrics& aMetrics, bool& aSuccessOut)
{
@ -146,6 +156,11 @@ ScrollFrame(nsIContent* aContent,
if (sf) {
sf->ResetScrollInfoIfGeneration(aMetrics.GetScrollGeneration());
sf->SetScrollableByAPZ(!aMetrics.IsScrollInfoLayer());
if (sf->IsRootScrollFrameOfDocument()) {
if (nsCOMPtr<nsIPresShell> shell = GetPresShell(aContent)) {
shell->SetVisualViewportOffset(CSSPoint::ToAppUnits(aMetrics.GetScrollOffset()));
}
}
}
bool scrollUpdated = false;
CSSPoint apzScrollOffset = aMetrics.GetScrollOffset();
@ -230,16 +245,6 @@ SetDisplayPortMargins(nsIPresShell* aPresShell,
nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent, base);
}
static already_AddRefed<nsIPresShell>
GetPresShell(const nsIContent* aContent)
{
nsCOMPtr<nsIPresShell> result;
if (nsIDocument* doc = aContent->GetComposedDoc()) {
result = doc->GetShell();
}
return result.forget();
}
static void
SetPaintRequestTime(nsIContent* aContent, const TimeStamp& aPaintRequestTime)
{

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

@ -240,6 +240,7 @@ static bool
IsContainerLayerItem(nsDisplayItem* aItem)
{
switch (aItem->GetType()) {
case DisplayItemType::TYPE_WRAP_LIST:
case DisplayItemType::TYPE_TRANSFORM:
case DisplayItemType::TYPE_OPACITY:
case DisplayItemType::TYPE_FILTER:
@ -518,9 +519,9 @@ struct DIGroup
combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
auto rect = transformedRect.Intersect(imageRect);
MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
GP("Layer NoChange: %s %d %d %d %d\n", aItem->Name(),
aData->mRect.x, aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
}
} else {
// XXX: this code can eventually be deleted/made debug only
@ -528,9 +529,9 @@ struct DIGroup
combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
auto rect = transformedRect.Intersect(imageRect);
MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
GP("NoChange: %s %d %d %d %d\n", aItem->Name(),
aData->mRect.x, aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
}
}
}

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

@ -71,6 +71,6 @@ freetype = { version = "0.4", default-features = false }
dwrote = "0.4.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.5"
core-graphics = "0.13"
core-text = { version = "9.2.0", default-features = false }
core-foundation = "0.6"
core-graphics = "0.14"
core-text = { version = "10", default-features = false }

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

@ -475,8 +475,8 @@ impl AlphaBatchBuilder {
// Add each run in this picture to the batch.
for run in &pic.runs {
let scroll_node = &ctx.clip_scroll_tree.nodes[run.clip_and_scroll.scroll_node_id.0];
let transform_id = ctx.transforms.get_id(scroll_node.transform_index);
let transform_id =
ctx.transforms.get_id(run.clip_and_scroll.scroll_node_id.transform_index());
self.add_run_to_batch(
run,
transform_id,
@ -670,7 +670,7 @@ impl AlphaBatchBuilder {
debug_assert!(picture.surface.is_some());
let real_xf = &ctx.clip_scroll_tree
.nodes[picture.reference_frame_index.0]
.spatial_nodes[picture.reference_frame_index.0]
.world_content_transform
.into();
let polygon = make_polygon(

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

@ -7,11 +7,11 @@ use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D, L
use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle};
use border::{ensure_no_corner_overlap};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, TransformIndex};
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId};
use ellipse::Ellipse;
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use gpu_types::{BoxShadowStretchMode};
use gpu_types::{BoxShadowStretchMode, TransformIndex};
use prim_store::{ClipData, ImageMaskData};
use render_task::to_cache_size;
use resource_cache::{ImageRequest, ResourceCache};
@ -257,19 +257,39 @@ impl ClipSource {
}
}
}
pub fn is_rect(&self) -> bool {
match *self {
ClipSource::Rectangle(..) => true,
_ => false,
}
}
pub fn is_image_or_line_decoration_clip(&self) -> bool {
match *self {
ClipSource::Image(..) | ClipSource::LineDecoration(..) => true,
_ => false,
}
}
}
#[derive(Debug)]
pub struct ClipSources {
pub clips: Vec<(ClipSource, GpuCacheHandle)>,
pub local_inner_rect: LayoutRect,
pub local_outer_rect: Option<LayoutRect>
pub local_outer_rect: Option<LayoutRect>,
pub only_rectangular_clips: bool,
pub has_image_or_line_decoration_clip: bool,
}
impl ClipSources {
pub fn new(clips: Vec<ClipSource>) -> Self {
let (local_inner_rect, local_outer_rect) = Self::calculate_inner_and_outer_rects(&clips);
let has_image_or_line_decoration_clip =
clips.iter().any(|clip| clip.is_image_or_line_decoration_clip());
let only_rectangular_clips =
!has_image_or_line_decoration_clip && clips.iter().all(|clip| clip.is_rect());
let clips = clips
.into_iter()
.map(|clip| (clip, GpuCacheHandle::new()))
@ -279,6 +299,8 @@ impl ClipSources {
clips,
local_inner_rect,
local_outer_rect,
only_rectangular_clips,
has_image_or_line_decoration_clip,
}
}

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

@ -0,0 +1,100 @@
/* 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/. */
use api::DevicePixelScale;
use clip::{ClipChain, ClipChainNode, ClipSourcesHandle, ClipStore, ClipWorkItem};
use clip_scroll_tree::{ClipChainIndex, SpatialNodeIndex};
use gpu_cache::GpuCache;
use resource_cache::ResourceCache;
use spatial_node::SpatialNode;
#[derive(Debug)]
pub struct ClipNode {
/// The node that determines how this clip node is positioned.
pub spatial_node: SpatialNodeIndex,
/// A handle to this clip nodes clips in the ClipStore.
pub handle: Option<ClipSourcesHandle>,
/// An index to a ClipChain defined by this ClipNode's hiearchy in the display
/// list.
pub clip_chain_index: ClipChainIndex,
/// The index of the parent ClipChain of this node's hiearchical ClipChain.
pub parent_clip_chain_index: ClipChainIndex,
/// A copy of the ClipChainNode this node would produce. We need to keep a copy,
/// because the ClipChain may not contain our node if is optimized out, but API
/// defined ClipChains will still need to access it.
pub clip_chain_node: Option<ClipChainNode>,
}
impl ClipNode {
const EMPTY: ClipNode = ClipNode {
spatial_node: SpatialNodeIndex(0),
handle: None,
clip_chain_index: ClipChainIndex(0),
parent_clip_chain_index: ClipChainIndex(0),
clip_chain_node: None,
};
pub fn empty() -> ClipNode {
ClipNode::EMPTY
}
pub fn update(
&mut self,
spatial_node: &SpatialNode,
device_pixel_scale: DevicePixelScale,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
clip_chains: &mut [ClipChain],
) {
let (clip_sources, weak_handle) = match self.handle {
Some(ref handle) => (clip_store.get_mut(handle), handle.weak()),
None => {
warn!("Tried to process an empty clip node");
return;
}
};
clip_sources.update(gpu_cache, resource_cache, device_pixel_scale);
let (screen_inner_rect, screen_outer_rect) = clip_sources.get_screen_bounds(
&spatial_node.world_content_transform,
device_pixel_scale,
None,
);
// All clipping SpatialNodes should have outer rectangles, because they never
// use the BorderCorner clip type and they always have at last one non-ClipOut
// Rectangle ClipSource.
let screen_outer_rect = screen_outer_rect
.expect("Clipping node didn't have outer rect.");
let local_outer_rect = clip_sources.local_outer_rect
.expect("Clipping node didn't have outer rect.");
let new_node = ClipChainNode {
work_item: ClipWorkItem {
transform_index: self.spatial_node.transform_index(),
clip_sources: weak_handle,
coordinate_system_id: spatial_node.coordinate_system_id,
},
local_clip_rect: spatial_node
.coordinate_system_relative_transform
.transform_rect(&local_outer_rect),
screen_outer_rect,
screen_inner_rect,
prev: None,
};
let mut clip_chain =
clip_chains[self.parent_clip_chain_index.0]
.new_with_added_node(&new_node);
self.clip_chain_node = Some(new_node);
clip_chain.parent_index = Some(self.parent_clip_chain_index);
clip_chains[self.clip_chain_index.0] = clip_chain;
}
}

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

@ -6,13 +6,14 @@ use api::{DeviceIntRect, DevicePixelScale, ExternalScrollId, LayoutPoint, Layout
use api::{PipelineId, ScrollClamping, ScrollLocation, ScrollNodeState};
use api::{LayoutSize, LayoutTransform, PropertyBinding, ScrollSensitivity, WorldPoint};
use clip::{ClipChain, ClipSourcesHandle, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, SpatialNodeKind, ScrollFrameInfo, StickyFrameInfo};
use clip_node::ClipNode;
use gpu_cache::GpuCache;
use gpu_types::TransformPalette;
use gpu_types::{TransformIndex, TransformPalette};
use internal_types::{FastHashMap, FastHashSet};
use print_tree::{PrintTree, PrintTreePrinter};
use resource_cache::ResourceCache;
use scene::SceneProperties;
use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo};
use util::{LayoutFastTransform, LayoutToWorldFastTransform};
pub type ScrollStates = FastHashMap<ExternalScrollId, ScrollFrameInfo>;
@ -27,19 +28,19 @@ pub type ScrollStates = FastHashMap<ExternalScrollId, ScrollFrameInfo>;
pub struct CoordinateSystemId(pub u32);
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub struct ClipScrollNodeIndex(pub usize);
pub struct SpatialNodeIndex(pub usize);
// Used to index the smaller subset of nodes in the CST that define
// new transform / positioning.
// TODO(gw): In the future if we split the CST into a positioning and
// clipping tree, this can be tidied up a bit.
#[derive(Copy, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct TransformIndex(pub u32);
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub struct ClipNodeIndex(pub usize);
const ROOT_REFERENCE_FRAME_INDEX: ClipScrollNodeIndex = ClipScrollNodeIndex(0);
const TOPMOST_SCROLL_NODE_INDEX: ClipScrollNodeIndex = ClipScrollNodeIndex(1);
impl SpatialNodeIndex {
pub fn transform_index(&self) -> TransformIndex {
TransformIndex(self.0 as u32)
}
}
const ROOT_REFERENCE_FRAME_INDEX: SpatialNodeIndex = SpatialNodeIndex(0);
const TOPMOST_SCROLL_NODE_INDEX: SpatialNodeIndex = SpatialNodeIndex(1);
impl CoordinateSystemId {
pub fn root() -> Self {
@ -59,14 +60,19 @@ impl CoordinateSystemId {
pub struct ClipChainDescriptor {
pub index: ClipChainIndex,
pub parent: Option<ClipChainIndex>,
pub clips: Vec<ClipScrollNodeIndex>,
pub clips: Vec<ClipNodeIndex>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ClipChainIndex(pub usize);
pub struct ClipScrollTree {
pub nodes: Vec<ClipScrollNode>,
/// Nodes which determine the positions (offsets and transforms) for primitives
/// and clips.
pub spatial_nodes: Vec<SpatialNode>,
/// Nodes which clip primitives.
pub clip_nodes: Vec<ClipNode>,
/// A Vec of all descriptors that describe ClipChains in the order in which they are
/// encountered during display list flattening. ClipChains are expected to never be
@ -82,10 +88,6 @@ pub struct ClipScrollTree {
/// A set of pipelines which should be discarded the next time this
/// tree is drained.
pub pipelines_to_discard: FastHashSet<PipelineId>,
/// The number of nodes in the CST that are spatial. Currently, this is all
/// nodes that are not clip nodes.
spatial_node_count: usize,
}
#[derive(Clone)]
@ -95,9 +97,6 @@ pub struct TransformUpdateState {
pub nearest_scrolling_ancestor_offset: LayoutVector2D,
pub nearest_scrolling_ancestor_viewport: LayoutRect,
/// The index of the current parent's clip chain.
pub parent_clip_chain_index: ClipChainIndex,
/// An id for keeping track of the axis-aligned space of this node. This is used in
/// order to to track what kinds of clip optimizations can be done for a particular
/// display list item, since optimizations can usually only be done among
@ -116,35 +115,35 @@ pub struct TransformUpdateState {
impl ClipScrollTree {
pub fn new() -> Self {
ClipScrollTree {
nodes: Vec::new(),
spatial_nodes: Vec::new(),
clip_nodes: Vec::new(),
clip_chains_descriptors: Vec::new(),
clip_chains: vec![ClipChain::empty(&DeviceIntRect::zero())],
pending_scroll_offsets: FastHashMap::default(),
pipelines_to_discard: FastHashSet::default(),
spatial_node_count: 0,
}
}
/// The root reference frame, which is the true root of the ClipScrollTree. Initially
/// this ID is not valid, which is indicated by ```nodes``` being empty.
pub fn root_reference_frame_index(&self) -> ClipScrollNodeIndex {
/// this ID is not valid, which is indicated by ```spatial_nodes``` being empty.
pub fn root_reference_frame_index(&self) -> SpatialNodeIndex {
// TODO(mrobinson): We should eventually make this impossible to misuse.
debug_assert!(!self.nodes.is_empty());
debug_assert!(!self.spatial_nodes.is_empty());
ROOT_REFERENCE_FRAME_INDEX
}
/// The root scroll node which is the first child of the root reference frame.
/// Initially this ID is not valid, which is indicated by ```nodes``` being empty.
pub fn topmost_scroll_node_index(&self) -> ClipScrollNodeIndex {
/// Initially this ID is not valid, which is indicated by ```spatial_nodes``` being empty.
pub fn topmost_scroll_node_index(&self) -> SpatialNodeIndex {
// TODO(mrobinson): We should eventually make this impossible to misuse.
debug_assert!(self.nodes.len() >= 1);
debug_assert!(self.spatial_nodes.len() >= 1);
TOPMOST_SCROLL_NODE_INDEX
}
pub fn get_scroll_node_state(&self) -> Vec<ScrollNodeState> {
let mut result = vec![];
for node in &self.nodes {
if let NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(info), .. } = node.node_type {
for node in &self.spatial_nodes {
if let SpatialNodeType::ScrollFrame(info) = node.node_type {
if let Some(id) = info.external_id {
result.push(ScrollNodeState { id, scroll_offset: info.offset })
}
@ -155,20 +154,20 @@ impl ClipScrollTree {
pub fn drain(&mut self) -> ScrollStates {
let mut scroll_states = FastHashMap::default();
for old_node in &mut self.nodes.drain(..) {
for old_node in &mut self.spatial_nodes.drain(..) {
if self.pipelines_to_discard.contains(&old_node.pipeline_id) {
continue;
}
match old_node.node_type {
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(info), .. } if info.external_id.is_some() => {
SpatialNodeType::ScrollFrame(info) if info.external_id.is_some() => {
scroll_states.insert(info.external_id.unwrap(), info);
}
_ => {}
}
}
self.spatial_node_count = 0;
self.clip_nodes.clear();
self.pipelines_to_discard.clear();
self.clip_chains = vec![ClipChain::empty(&DeviceIntRect::zero())];
self.clip_chains_descriptors.clear();
@ -181,7 +180,7 @@ impl ClipScrollTree {
id: ExternalScrollId,
clamp: ScrollClamping
) -> bool {
for node in &mut self.nodes {
for node in &mut self.spatial_nodes {
if node.matches_external_id(id) {
return node.set_scroll_origin(&origin, clamp);
}
@ -193,16 +192,16 @@ impl ClipScrollTree {
fn find_nearest_scrolling_ancestor(
&self,
index: Option<ClipScrollNodeIndex>
) -> ClipScrollNodeIndex {
index: Option<SpatialNodeIndex>
) -> SpatialNodeIndex {
let index = match index {
Some(index) => index,
None => return self.topmost_scroll_node_index(),
};
let node = &self.nodes[index.0];
let node = &self.spatial_nodes[index.0];
match node.node_type {
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(state), .. } if state.sensitive_to_input_events() => index,
SpatialNodeType::ScrollFrame(state) if state.sensitive_to_input_events() => index,
_ => self.find_nearest_scrolling_ancestor(node.parent)
}
}
@ -210,13 +209,13 @@ impl ClipScrollTree {
pub fn scroll_nearest_scrolling_ancestor(
&mut self,
scroll_location: ScrollLocation,
node_index: Option<ClipScrollNodeIndex>,
node_index: Option<SpatialNodeIndex>,
) -> bool {
if self.nodes.is_empty() {
if self.spatial_nodes.is_empty() {
return false;
}
let node_index = self.find_nearest_scrolling_ancestor(node_index);
self.nodes[node_index.0].scroll(scroll_location)
self.spatial_nodes[node_index.0].scroll(scroll_location)
}
pub fn update_tree(
@ -229,50 +228,54 @@ impl ClipScrollTree {
pan: WorldPoint,
scene_properties: &SceneProperties,
) -> TransformPalette {
let mut transform_palette = TransformPalette::new(self.spatial_node_count);
let mut transform_palette = TransformPalette::new(self.spatial_nodes.len());
if self.spatial_nodes.is_empty() {
return transform_palette;
}
if !self.nodes.is_empty() {
self.clip_chains[0] = ClipChain::empty(screen_rect);
self.clip_chains[0] = ClipChain::empty(screen_rect);
let root_reference_frame_index = self.root_reference_frame_index();
let mut state = TransformUpdateState {
parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(),
parent_accumulated_scroll_offset: LayoutVector2D::zero(),
nearest_scrolling_ancestor_offset: LayoutVector2D::zero(),
nearest_scrolling_ancestor_viewport: LayoutRect::zero(),
parent_clip_chain_index: ClipChainIndex(0),
current_coordinate_system_id: CoordinateSystemId::root(),
coordinate_system_relative_transform: LayoutFastTransform::identity(),
invertible: true,
};
let mut next_coordinate_system_id = state.current_coordinate_system_id.next();
self.update_node(
root_reference_frame_index,
&mut state,
&mut next_coordinate_system_id,
let root_reference_frame_index = self.root_reference_frame_index();
let mut state = TransformUpdateState {
parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(),
parent_accumulated_scroll_offset: LayoutVector2D::zero(),
nearest_scrolling_ancestor_offset: LayoutVector2D::zero(),
nearest_scrolling_ancestor_viewport: LayoutRect::zero(),
current_coordinate_system_id: CoordinateSystemId::root(),
coordinate_system_relative_transform: LayoutFastTransform::identity(),
invertible: true,
};
let mut next_coordinate_system_id = state.current_coordinate_system_id.next();
self.update_node(
root_reference_frame_index,
&mut state,
&mut next_coordinate_system_id,
&mut transform_palette,
scene_properties,
);
for clip_node in &mut self.clip_nodes {
let spatial_node = &self.spatial_nodes[clip_node.spatial_node.0];
clip_node.update(
spatial_node,
device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
&mut transform_palette,
scene_properties,
&mut self.clip_chains,
);
self.build_clip_chains(screen_rect);
}
self.build_clip_chains(screen_rect);
transform_palette
}
fn update_node(
&mut self,
node_index: ClipScrollNodeIndex,
node_index: SpatialNodeIndex,
state: &mut TransformUpdateState,
next_coordinate_system_id: &mut CoordinateSystemId,
device_pixel_scale: DevicePixelScale,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
transform_palette: &mut TransformPalette,
scene_properties: &SceneProperties,
) {
@ -280,23 +283,13 @@ impl ClipScrollTree {
// Restructure this to avoid the clones!
let mut state = state.clone();
let node_children = {
let node = match self.nodes.get_mut(node_index.0) {
let node = match self.spatial_nodes.get_mut(node_index.0) {
Some(node) => node,
None => return,
};
node.update(
&mut state,
next_coordinate_system_id,
device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
scene_properties,
&mut self.clip_chains,
);
node.push_gpu_data(transform_palette);
node.update(&mut state, next_coordinate_system_id, scene_properties);
node.push_gpu_data(transform_palette, node_index);
if node.children.is_empty() {
return;
@ -311,10 +304,6 @@ impl ClipScrollTree {
child_node_index,
&mut state,
next_coordinate_system_id,
device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
transform_palette,
scene_properties,
);
@ -324,22 +313,21 @@ impl ClipScrollTree {
pub fn build_clip_chains(&mut self, screen_rect: &DeviceIntRect) {
for descriptor in &self.clip_chains_descriptors {
// A ClipChain is an optional parent (which is another ClipChain) and a list of
// ClipScrollNode clipping nodes. Here we start the ClipChain with a clone of the
// SpatialNode clipping nodes. Here we start the ClipChain with a clone of the
// parent's node, if necessary.
let mut chain = match descriptor.parent {
Some(index) => self.clip_chains[index.0].clone(),
None => ClipChain::empty(screen_rect),
};
// Now we walk through each ClipScrollNode in the vector of clip nodes and
// extract their ClipChain nodes to construct the final list.
// Now we walk through each ClipNode in the vector and extract their ClipChain nodes to
// construct the final list.
for clip_index in &descriptor.clips {
match self.nodes[clip_index.0].node_type {
NodeType::Clip { clip_chain_node: Some(ref node), .. } => {
match self.clip_nodes[clip_index.0] {
ClipNode { clip_chain_node: Some(ref node), .. } => {
chain.add_node(node.clone());
}
NodeType::Clip { .. } => warn!("Found uninitialized clipping ClipScrollNode."),
_ => warn!("Tried to create a clip chain with non-clipping node."),
ClipNode { .. } => warn!("Found uninitialized clipping ClipNode."),
};
}
@ -349,9 +337,9 @@ impl ClipScrollTree {
}
pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) {
for node in &mut self.nodes {
for node in &mut self.spatial_nodes {
let external_id = match node.node_type {
NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ), .. } => id,
SpatialNodeType::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ) => id,
_ => continue,
};
@ -365,133 +353,131 @@ impl ClipScrollTree {
}
}
// Generate the next valid TransformIndex for the CST.
fn next_transform_index(&mut self) -> TransformIndex {
let transform_index = TransformIndex(self.spatial_node_count as u32);
self.spatial_node_count += 1;
transform_index
}
pub fn add_clip_node(
&mut self,
index: ClipScrollNodeIndex,
parent_index: ClipScrollNodeIndex,
index: ClipNodeIndex,
parent_clip_chain_index: ClipChainIndex,
spatial_node: SpatialNodeIndex,
handle: ClipSourcesHandle,
pipeline_id: PipelineId,
) -> ClipChainIndex {
let clip_chain_index = self.allocate_clip_chain();
let transform_index = self.nodes[parent_index.0].transform_index;
let node_type = NodeType::Clip {
handle,
let node = ClipNode {
parent_clip_chain_index,
spatial_node,
handle: Some(handle),
clip_chain_index,
clip_chain_node: None,
};
let node = ClipScrollNode::new(
pipeline_id,
Some(parent_index),
node_type,
transform_index,
);
self.add_node(node, index);
self.push_clip_node(node, index);
clip_chain_index
}
pub fn add_scroll_frame(
&mut self,
index: ClipScrollNodeIndex,
parent_index: ClipScrollNodeIndex,
index: SpatialNodeIndex,
parent_index: SpatialNodeIndex,
external_id: Option<ExternalScrollId>,
pipeline_id: PipelineId,
frame_rect: &LayoutRect,
content_size: &LayoutSize,
scroll_sensitivity: ScrollSensitivity,
) {
let node = ClipScrollNode::new_scroll_frame(
let node = SpatialNode::new_scroll_frame(
pipeline_id,
parent_index,
external_id,
frame_rect,
content_size,
scroll_sensitivity,
self.next_transform_index(),
);
self.add_node(node, index);
self.add_spatial_node(node, index);
}
pub fn add_reference_frame(
&mut self,
index: ClipScrollNodeIndex,
parent_index: Option<ClipScrollNodeIndex>,
index: SpatialNodeIndex,
parent_index: Option<SpatialNodeIndex>,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayoutVector2D,
pipeline_id: PipelineId,
) {
let node = ClipScrollNode::new_reference_frame(
let node = SpatialNode::new_reference_frame(
parent_index,
source_transform,
source_perspective,
origin_in_parent_reference_frame,
pipeline_id,
self.next_transform_index(),
);
self.add_node(node, index);
self.add_spatial_node(node, index);
}
pub fn add_sticky_frame(
&mut self,
index: ClipScrollNodeIndex,
parent_index: ClipScrollNodeIndex,
index: SpatialNodeIndex,
parent_index: SpatialNodeIndex,
sticky_frame_info: StickyFrameInfo,
pipeline_id: PipelineId,
) {
let node = ClipScrollNode::new_sticky_frame(
let node = SpatialNode::new_sticky_frame(
parent_index,
sticky_frame_info,
pipeline_id,
self.next_transform_index(),
);
self.add_node(node, index);
self.add_spatial_node(node, index);
}
pub fn add_clip_chain_descriptor(
&mut self,
parent: Option<ClipChainIndex>,
clips: Vec<ClipScrollNodeIndex>
clips: Vec<ClipNodeIndex>
) -> ClipChainIndex {
let index = self.allocate_clip_chain();
self.clip_chains_descriptors.push(ClipChainDescriptor { index, parent, clips });
index
}
pub fn add_node(&mut self, node: ClipScrollNode, index: ClipScrollNodeIndex) {
// When the parent node is None this means we are adding the root.
if let Some(parent_index) = node.parent {
self.nodes[parent_index.0].add_child(index);
}
if index.0 == self.nodes.len() {
self.nodes.push(node);
pub fn push_clip_node(&mut self, node: ClipNode, index: ClipNodeIndex) {
if index.0 == self.clip_nodes.len() {
self.clip_nodes.push(node);
return;
}
if let Some(empty_node) = self.nodes.get_mut(index.0) {
if let Some(empty_node) = self.clip_nodes.get_mut(index.0) {
*empty_node = node;
return
}
let length_to_reserve = index.0 + 1 - self.nodes.len();
self.nodes.reserve_exact(length_to_reserve);
let length_to_reserve = index.0 + 1 - self.clip_nodes.len();
self.clip_nodes.reserve_exact(length_to_reserve);
// We would like to use `Vec::resize` here, but the Clone trait is not supported
// for ClipScrollNodes. We can fix this either by splitting the clip nodes out into
// their own tree or when support is added for something like `Vec::resize_default`.
let length_to_extend = self.nodes.len() .. index.0;
self.nodes.extend(length_to_extend.map(|_| ClipScrollNode::empty()));
// for ClipNodes. We can fix this either when support is added for something like
// `Vec::resize_default`.
let length_to_extend = self.clip_nodes.len() .. index.0;
self.clip_nodes.extend(length_to_extend.map(|_| ClipNode::empty()));
self.clip_nodes.push(node);
}
self.nodes.push(node);
pub fn add_spatial_node(&mut self, node: SpatialNode, index: SpatialNodeIndex) {
// When the parent node is None this means we are adding the root.
if let Some(parent_index) = node.parent {
self.spatial_nodes[parent_index.0].add_child(index);
}
if index.0 == self.spatial_nodes.len() {
self.spatial_nodes.push(node);
return;
}
if let Some(empty_node) = self.spatial_nodes.get_mut(index.0) {
*empty_node = node;
return
}
debug_assert!(index.0 > self.spatial_nodes.len() - 1);
self.spatial_nodes.resize(index.0, SpatialNode::empty());
self.spatial_nodes.push(node);
}
pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
@ -500,44 +486,29 @@ impl ClipScrollTree {
fn print_node<T: PrintTreePrinter>(
&self,
index: ClipScrollNodeIndex,
index: SpatialNodeIndex,
pt: &mut T,
clip_store: &ClipStore
) {
let node = &self.nodes[index.0];
let node = &self.spatial_nodes[index.0];
match node.node_type {
NodeType::Spatial { ref kind, .. } => {
match *kind {
SpatialNodeKind::StickyFrame(ref sticky_frame_info) => {
pt.new_level(format!("StickyFrame"));
pt.add_item(format!("index: {:?}", index));
pt.add_item(format!("sticky info: {:?}", sticky_frame_info));
}
SpatialNodeKind::ScrollFrame(scrolling_info) => {
pt.new_level(format!("ScrollFrame"));
pt.add_item(format!("index: {:?}", index));
pt.add_item(format!("viewport: {:?}", scrolling_info.viewport_rect));
pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset));
}
SpatialNodeKind::ReferenceFrame(ref info) => {
pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
pt.add_item(format!("index: {:?}", index));
}
}
}
NodeType::Clip { ref handle, .. } => {
pt.new_level("Clip".to_owned());
SpatialNodeType::StickyFrame(ref sticky_frame_info) => {
pt.new_level(format!("StickyFrame"));
pt.add_item(format!("index: {:?}", index));
let clips = clip_store.get(handle).clips();
pt.new_level(format!("Clip Sources [{}]", clips.len()));
for source in clips {
pt.add_item(format!("{:?}", source));
}
pt.end_level();
pt.add_item(format!("sticky info: {:?}", sticky_frame_info));
}
NodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."),
SpatialNodeType::ScrollFrame(scrolling_info) => {
pt.new_level(format!("ScrollFrame"));
pt.add_item(format!("index: {:?}", index));
pt.add_item(format!("viewport: {:?}", scrolling_info.viewport_rect));
pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset));
}
SpatialNodeType::ReferenceFrame(ref info) => {
pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
pt.add_item(format!("index: {:?}", index));
}
SpatialNodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."),
}
pt.add_item(format!("world_viewport_transform: {:?}", node.world_viewport_transform));
@ -553,14 +524,14 @@ impl ClipScrollTree {
#[allow(dead_code)]
pub fn print(&self, clip_store: &ClipStore) {
if !self.nodes.is_empty() {
if !self.spatial_nodes.is_empty() {
let mut pt = PrintTree::new("clip_scroll tree");
self.print_with(clip_store, &mut pt);
}
}
pub fn print_with<T: PrintTreePrinter>(&self, clip_store: &ClipStore, pt: &mut T) {
if !self.nodes.is_empty() {
if !self.spatial_nodes.is_empty() {
self.print_node(self.root_reference_frame_index(), pt, clip_store);
}
}

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

@ -14,8 +14,7 @@ use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, S
use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
use api::{TransformStyle, YuvColorSpace, YuvData};
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
use clip_scroll_node::{NodeType, SpatialNodeKind, StickyFrameInfo};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
use clip_scroll_tree::{ClipChainIndex, ClipNodeIndex, ClipScrollTree, SpatialNodeIndex};
use euclid::vec2;
use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
use glyph_rasterizer::FontInstance;
@ -33,6 +32,7 @@ use render_backend::{DocumentView};
use resource_cache::{FontInstanceMap, ImageRequest};
use scene::{Scene, ScenePipeline, StackingContextHelpers};
use scene_builder::{BuiltScene, SceneRequest};
use spatial_node::{SpatialNodeType, StickyFrameInfo};
use std::{f32, mem, usize};
use tiling::{CompositeOps, ScrollbarPrimitive};
use util::{MaxRect, RectHelpers, recycle_vec};
@ -44,9 +44,16 @@ static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
a: 0.6,
};
#[derive(Clone, Copy)]
pub struct PipelineOffset {
pipeline: PipelineId,
spatial_offset: usize,
clip_offset: usize,
}
/// A data structure that keeps track of mapping between API ClipIds and the indices used
/// internally in the ClipScrollTree to avoid having to do HashMap lookups. ClipIdToIndexMapper is
/// responsible for mapping both ClipId to ClipChainIndex and ClipId to ClipScrollNodeIndex. We
/// responsible for mapping both ClipId to ClipChainIndex and ClipId to SpatialNodeIndex. We
/// also include two small LRU caches. Currently the caches are small (1 entry), but in the future
/// we could use uluru here to do something more involved.
#[derive(Default)]
@ -60,18 +67,23 @@ pub struct ClipIdToIndexMapper {
/// HashMap lookups.
cached_clip_chain_index: Option<(ClipId, ClipChainIndex)>,
/// The offset in the ClipScrollTree's array of ClipScrollNodes for a particular pipeline.
/// This is used to convert a ClipId into a ClipScrollNodeIndex.
pipeline_offsets: FastHashMap<PipelineId, usize>,
/// The offset in the ClipScrollTree's array of SpatialNodes and ClipNodes for a particular
/// pipeline. This is used to convert ClipIds into SpatialNodeIndex or ClipNodeIndex.
pipeline_offsets: FastHashMap<PipelineId, PipelineOffset>,
/// The last mapped pipeline offset for this mapper. This is used to avoid having to
/// consult `pipeline_offsets` repeatedly when flattening the display list.
cached_pipeline_offset: Option<(PipelineId, usize)>,
cached_pipeline_offset: Option<PipelineOffset>,
/// The next available pipeline offset for ClipScrollNodeIndex. When we encounter a pipeline
/// we will use this value and increment it by the total number of ClipScrollNodes in the
/// The next available pipeline offset for ClipNodeIndex. When we encounter a pipeline
/// we will use this value and increment it by the total number of clip nodes in the
/// pipeline's display list.
next_available_offset: usize,
next_available_clip_offset: usize,
/// The next available pipeline offset for SpatialNodeIndex. When we encounter a pipeline
/// we will use this value and increment it by the total number of spatial nodes in the
/// pipeline's display list.
next_available_spatial_offset: usize,
}
impl ClipIdToIndexMapper {
@ -101,39 +113,46 @@ impl ClipIdToIndexMapper {
index
}
pub fn map_clip_and_scroll(&mut self, info: &ClipAndScrollInfo) -> ScrollNodeAndClipChain {
ScrollNodeAndClipChain::new(
self.get_node_index(info.scroll_node_id),
self.get_clip_chain_index_and_cache_result(&info.clip_node_id())
)
}
pub fn simple_scroll_and_clip_chain(&mut self, id: &ClipId) -> ScrollNodeAndClipChain {
self.map_clip_and_scroll(&ClipAndScrollInfo::simple(*id))
}
pub fn initialize_for_pipeline(&mut self, pipeline: &ScenePipeline) {
debug_assert!(!self.pipeline_offsets.contains_key(&pipeline.pipeline_id));
self.pipeline_offsets.insert(pipeline.pipeline_id, self.next_available_offset);
self.next_available_offset += pipeline.display_list.total_clip_ids();
self.pipeline_offsets.insert(
pipeline.pipeline_id,
PipelineOffset {
pipeline: pipeline.pipeline_id,
spatial_offset: self.next_available_spatial_offset,
clip_offset: self.next_available_clip_offset,
}
);
self.next_available_clip_offset += pipeline.display_list.total_clip_nodes();
self.next_available_spatial_offset += pipeline.display_list.total_spatial_nodes();
}
pub fn get_node_index(&mut self, id: ClipId) -> ClipScrollNodeIndex {
let (index, pipeline_id) = match id {
ClipId::Clip(index, pipeline_id) => (index, pipeline_id),
ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."),
};
let pipeline_offset = match self.cached_pipeline_offset {
Some((last_used_id, offset)) if last_used_id == pipeline_id => offset,
pub fn get_pipeline_offet<'a>(&'a mut self, id: PipelineId) -> &'a PipelineOffset {
match self.cached_pipeline_offset {
Some(ref offset) if offset.pipeline == id => offset,
_ => {
let offset = self.pipeline_offsets[&pipeline_id];
self.cached_pipeline_offset = Some((pipeline_id, offset));
let offset = &self.pipeline_offsets[&id];
self.cached_pipeline_offset = Some(*offset);
offset
}
};
}
}
ClipScrollNodeIndex(pipeline_offset + index)
pub fn get_clip_node_index(&mut self, id: ClipId) -> ClipNodeIndex {
match id {
ClipId::Clip(index, pipeline_id) => {
let pipeline_offset = self.get_pipeline_offet(pipeline_id);
ClipNodeIndex(pipeline_offset.clip_offset + index)
}
ClipId::Spatial(..) => {
// We could theoretically map back to the containing clip node with the current
// design, but we will eventually fully separate out clipping from spatial nodes
// in the display list. We don't ever need to do this anyway.
panic!("Tried to use positioning node as clip node.");
}
ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."),
}
}
}
@ -160,7 +179,7 @@ pub struct DisplayListFlattener<'a> {
/// A stack of scroll nodes used during display list processing to properly
/// parent new scroll nodes.
reference_frame_stack: Vec<(ClipId, ClipScrollNodeIndex)>,
reference_frame_stack: Vec<(ClipId, SpatialNodeIndex)>,
/// A stack of stacking context properties.
sc_stack: Vec<FlattenedStackingContext>,
@ -273,14 +292,12 @@ impl<'a> DisplayListFlattener<'a> {
fn flatten_root(&mut self, pipeline: &'a ScenePipeline, frame_size: &LayoutSize) {
let pipeline_id = pipeline.pipeline_id;
let reference_frame_info = self.id_to_index_mapper.simple_scroll_and_clip_chain(
&ClipId::root_reference_frame(pipeline_id)
let reference_frame_info = self.simple_scroll_and_clip_chain(
&ClipId::root_reference_frame(pipeline_id),
);
let root_scroll_node = ClipId::root_scroll_node(pipeline_id);
let scroll_frame_info = self.id_to_index_mapper.simple_scroll_and_clip_chain(
&root_scroll_node,
);
let scroll_frame_info = self.simple_scroll_and_clip_chain(&root_scroll_node);
self.push_stacking_context(
pipeline_id,
@ -380,7 +397,7 @@ impl<'a> DisplayListFlattener<'a> {
info.previously_applied_offset,
);
let index = self.id_to_index_mapper.get_node_index(info.id);
let index = self.get_spatial_node_index_for_clip_id(info.id);
self.clip_scroll_tree.add_sticky_frame(
index,
clip_and_scroll.scroll_node_id, /* parent id */
@ -407,7 +424,7 @@ impl<'a> DisplayListFlattener<'a> {
);
// Just use clip rectangle as the frame rect for this scroll frame.
// This is useful when calculating scroll extents for the
// ClipScrollNode::scroll(..) API as well as for properly setting sticky
// SpatialNode::scroll(..) API as well as for properly setting sticky
// positioning offsets.
let frame_rect = item.clip_rect().translate(reference_frame_relative_offset);
let content_rect = item.rect().translate(reference_frame_relative_offset);
@ -556,7 +573,7 @@ impl<'a> DisplayListFlattener<'a> {
reference_frame_relative_offset: LayoutVector2D,
) -> Option<BuiltDisplayListIter<'a>> {
let clip_and_scroll_ids = item.clip_and_scroll();
let clip_and_scroll = self.id_to_index_mapper.map_clip_and_scroll(&clip_and_scroll_ids);
let clip_and_scroll = self.map_clip_and_scroll(&clip_and_scroll_ids);
let prim_info = item.get_layout_primitive_info(&reference_frame_relative_offset);
match *item.item() {
@ -717,7 +734,7 @@ impl<'a> DisplayListFlattener<'a> {
SpecificDisplayItem::ClipChain(ref info) => {
let items = self.get_clip_chain_items(pipeline_id, item.clip_chain_items())
.iter()
.map(|id| self.id_to_index_mapper.get_node_index(*id))
.map(|id| self.id_to_index_mapper.get_clip_node_index(*id))
.collect();
let parent = info.parent.map(|id|
self.id_to_index_mapper.get_clip_chain_index(&ClipId::ClipChain(id))
@ -883,7 +900,7 @@ impl<'a> DisplayListFlattener<'a> {
transform_style: TransformStyle,
is_backface_visible: bool,
is_pipeline_root: bool,
positioning_node: ClipId,
spatial_node: ClipId,
clipping_node: Option<ClipId>,
glyph_raster_space: GlyphRasterSpace,
) {
@ -892,7 +909,7 @@ impl<'a> DisplayListFlattener<'a> {
None => ClipChainIndex(0), // This means no clipping.
};
let clip_and_scroll = ScrollNodeAndClipChain::new(
self.id_to_index_mapper.get_node_index(positioning_node),
self.get_spatial_node_index_for_clip_id(spatial_node),
clip_chain_id
);
@ -1191,9 +1208,9 @@ impl<'a> DisplayListFlattener<'a> {
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayoutVector2D,
) -> ClipScrollNodeIndex {
let index = self.id_to_index_mapper.get_node_index(reference_frame_id);
let parent_index = parent_id.map(|id| self.id_to_index_mapper.get_node_index(id));
) -> SpatialNodeIndex {
let index = self.get_spatial_node_index_for_clip_id(reference_frame_id);
let parent_index = parent_id.map(|id| self.get_spatial_node_index_for_clip_id(id));
self.clip_scroll_tree.add_reference_frame(
index,
parent_index,
@ -1212,7 +1229,7 @@ impl<'a> DisplayListFlattener<'a> {
index
}
pub fn current_reference_frame_index(&self) -> ClipScrollNodeIndex {
pub fn current_reference_frame_index(&self) -> SpatialNodeIndex {
self.reference_frame_stack.last().unwrap().1
}
@ -1223,8 +1240,8 @@ impl<'a> DisplayListFlattener<'a> {
) {
let viewport_offset = (inner_rect.origin.to_vector().to_f32() / device_pixel_scale).round();
let root_id = self.clip_scroll_tree.root_reference_frame_index();
let root_node = &mut self.clip_scroll_tree.nodes[root_id.0];
if let NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(ref mut info), .. } = root_node.node_type {
let root_node = &mut self.clip_scroll_tree.spatial_nodes[root_id.0];
if let SpatialNodeType::ReferenceFrame(ref mut info) = root_node.node_type {
info.resolved_transform =
LayoutVector2D::new(viewport_offset.x, viewport_offset.y).into();
}
@ -1261,19 +1278,21 @@ impl<'a> DisplayListFlattener<'a> {
new_node_id: ClipId,
parent_id: ClipId,
clip_region: ClipRegion,
) -> ClipScrollNodeIndex {
) {
let clip_sources = ClipSources::from(clip_region);
let handle = self.clip_store.insert(clip_sources);
let node_index = self.id_to_index_mapper.get_node_index(new_node_id);
let node_index = self.id_to_index_mapper.get_clip_node_index(new_node_id);
let parent_clip_chain_index =
self.id_to_index_mapper.get_clip_chain_index_and_cache_result(&parent_id);
let spatial_node = self.get_spatial_node_index_for_clip_id(parent_id);
let clip_chain_index = self.clip_scroll_tree.add_clip_node(
node_index,
self.id_to_index_mapper.get_node_index(parent_id),
parent_clip_chain_index,
spatial_node,
handle,
new_node_id.pipeline_id(),
);
self.id_to_index_mapper.add_clip_chain(new_node_id, clip_chain_index);
node_index
}
pub fn add_scroll_frame(
@ -1285,11 +1304,12 @@ impl<'a> DisplayListFlattener<'a> {
frame_rect: &LayoutRect,
content_size: &LayoutSize,
scroll_sensitivity: ScrollSensitivity,
) -> ClipScrollNodeIndex {
let node_index = self.id_to_index_mapper.get_node_index(new_node_id);
) -> SpatialNodeIndex {
let node_index = self.get_spatial_node_index_for_clip_id(new_node_id);
let parent_node_index = self.get_spatial_node_index_for_clip_id(parent_id);
self.clip_scroll_tree.add_scroll_frame(
node_index,
self.id_to_index_mapper.get_node_index(parent_id),
parent_node_index,
external_id,
pipeline_id,
frame_rect,
@ -1933,6 +1953,31 @@ impl<'a> DisplayListFlattener<'a> {
PrimitiveContainer::Brush(prim),
);
}
pub fn map_clip_and_scroll(&mut self, info: &ClipAndScrollInfo) -> ScrollNodeAndClipChain {
ScrollNodeAndClipChain::new(
self.get_spatial_node_index_for_clip_id(info.scroll_node_id),
self.id_to_index_mapper.get_clip_chain_index_and_cache_result(&info.clip_node_id())
)
}
pub fn simple_scroll_and_clip_chain(&mut self, id: &ClipId) -> ScrollNodeAndClipChain {
self.map_clip_and_scroll(&ClipAndScrollInfo::simple(*id))
}
pub fn get_spatial_node_index_for_clip_id(&mut self, id: ClipId,) -> SpatialNodeIndex {
match id {
ClipId::Spatial(index, pipeline_id) => {
let pipeline_offset = self.id_to_index_mapper.get_pipeline_offet(pipeline_id);
SpatialNodeIndex(pipeline_offset.spatial_offset + index)
}
ClipId::Clip(..) => {
let clip_node_index = self.id_to_index_mapper.get_clip_node_index(id);
self.clip_scroll_tree.clip_nodes[clip_node_index.0].spatial_node
}
ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."),
}
}
}
pub fn build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltScene {
@ -1987,4 +2032,4 @@ struct FlattenedStackingContext {
}
#[derive(Debug)]
pub struct ScrollbarInfo(pub ClipScrollNodeIndex, pub LayoutRect);
pub struct ScrollbarInfo(pub SpatialNodeIndex, pub LayoutRect);

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

@ -6,11 +6,10 @@ use api::{BuiltDisplayList, ColorF, DeviceIntPoint, DeviceIntRect, DevicePixelSc
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, FontRenderMode};
use api::{LayoutPoint, LayoutRect, LayoutSize, PipelineId, WorldPoint};
use clip::{ClipChain, ClipStore};
use clip_scroll_node::{ClipScrollNode};
use clip_scroll_tree::{ClipScrollNodeIndex, ClipScrollTree};
use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
use display_list_flattener::{DisplayListFlattener};
use gpu_cache::GpuCache;
use gpu_types::{PrimitiveHeaders, TransformData, UvRectKind};
use gpu_types::{PrimitiveHeaders, TransformData, TransformIndex, UvRectKind};
use hit_test::{HitTester, HitTestingRun};
use internal_types::{FastHashMap};
use picture::PictureSurface;
@ -20,6 +19,7 @@ use render_backend::FrameId;
use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
use resource_cache::{ResourceCache};
use scene::{ScenePipeline, SceneProperties};
use spatial_node::SpatialNode;
use std::{mem, f32};
use std::sync::Arc;
use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext};
@ -88,7 +88,7 @@ pub struct FrameBuildingState<'a> {
pub struct PictureContext<'a> {
pub pipeline_id: PipelineId,
pub prim_runs: Vec<PrimitiveRun>,
pub original_reference_frame_index: Option<ClipScrollNodeIndex>,
pub original_reference_frame_index: Option<SpatialNodeIndex>,
pub display_list: &'a BuiltDisplayList,
pub inv_world_transform: Option<WorldToLayoutFastTransform>,
pub apply_local_clip_rect: bool,
@ -114,20 +114,23 @@ impl PictureState {
pub struct PrimitiveRunContext<'a> {
pub clip_chain: &'a ClipChain,
pub scroll_node: &'a ClipScrollNode,
pub scroll_node: &'a SpatialNode,
pub transform_index: TransformIndex,
pub local_clip_rect: LayoutRect,
}
impl<'a> PrimitiveRunContext<'a> {
pub fn new(
clip_chain: &'a ClipChain,
scroll_node: &'a ClipScrollNode,
scroll_node: &'a SpatialNode,
transform_index: TransformIndex,
local_clip_rect: LayoutRect,
) -> Self {
PrimitiveRunContext {
clip_chain,
scroll_node,
local_clip_rect,
transform_index,
}
}
}
@ -195,11 +198,11 @@ impl FrameBuilder {
}
// The root picture is always the first one added.
let root_clip_scroll_node =
&clip_scroll_tree.nodes[clip_scroll_tree.root_reference_frame_index().0];
let root_spatial_node =
&clip_scroll_tree.spatial_nodes[clip_scroll_tree.root_reference_frame_index().0];
let display_list = &pipelines
.get(&root_clip_scroll_node.pipeline_id)
.get(&root_spatial_node.pipeline_id)
.expect("No display list?")
.display_list;
@ -229,7 +232,7 @@ impl FrameBuilder {
};
let pic_context = PictureContext {
pipeline_id: root_clip_scroll_node.pipeline_id,
pipeline_id: root_spatial_node.pipeline_id,
prim_runs: mem::replace(&mut self.prim_store.pictures[0].runs, Vec::new()),
original_reference_frame_index: None,
display_list,
@ -270,7 +273,7 @@ impl FrameBuilder {
for scrollbar_prim in &self.scrollbar_prims {
let metadata = &mut self.prim_store.cpu_metadata[scrollbar_prim.prim_index.0];
let scroll_frame = &clip_scroll_tree.nodes[scrollbar_prim.scroll_frame_index.0];
let scroll_frame = &clip_scroll_tree.spatial_nodes[scrollbar_prim.scroll_frame_index.0];
// Invalidate what's in the cache so it will get rebuilt.
gpu_cache.invalidate(&metadata.gpu_location);

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

@ -4,7 +4,6 @@
use api::{DevicePoint, DeviceSize, DeviceRect, LayoutRect, LayoutToWorldTransform};
use api::{PremultipliedColorF, WorldToLayoutTransform};
use clip_scroll_tree::TransformIndex;
use gpu_cache::{GpuCacheAddress, GpuDataRequest};
use prim_store::{EdgeAaSegmentMask};
use render_task::RenderTaskAddress;
@ -400,6 +399,11 @@ pub struct TransformPalette {
metadata: Vec<TransformMetadata>,
}
#[derive(Copy, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct TransformIndex(pub u32);
impl TransformPalette {
pub fn new(spatial_node_count: usize) -> TransformPalette {
TransformPalette {

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

@ -5,8 +5,8 @@
use api::{BorderRadius, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag, LayoutPoint};
use api::{LayoutPrimitiveInfo, LayoutRect, PipelineId, WorldPoint};
use clip::{ClipSource, ClipStore, rounded_rectangle_contains_point};
use clip_scroll_node::{ClipScrollNode, NodeType};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
use clip_node::ClipNode;
use clip_scroll_tree::{ClipChainIndex, ClipNodeIndex, SpatialNodeIndex, ClipScrollTree};
use internal_types::FastHashMap;
use prim_store::ScrollNodeAndClipChain;
use util::LayoutToWorldFastTransform;
@ -14,14 +14,10 @@ use util::LayoutToWorldFastTransform;
/// A copy of important clip scroll node data to use during hit testing. This a copy of
/// data from the ClipScrollTree that will persist as a new frame is under construction,
/// allowing hit tests consistent with the currently rendered frame.
pub struct HitTestClipScrollNode {
pub struct HitTestSpatialNode {
/// The pipeline id of this node.
pipeline_id: PipelineId,
/// A particular point must be inside all of these regions to be considered clipped in
/// for the purposes of a hit test.
regions: Vec<HitTestRegion>,
/// World transform for content transformed by this node.
world_content_transform: LayoutToWorldFastTransform,
@ -29,6 +25,15 @@ pub struct HitTestClipScrollNode {
world_viewport_transform: LayoutToWorldFastTransform,
}
pub struct HitTestClipNode {
/// The positioning node for this clip node.
spatial_node: SpatialNodeIndex,
/// A particular point must be inside all of these regions to be considered clipped in
/// for the purposes of a hit test.
regions: Vec<HitTestRegion>,
}
/// A description of a clip chain in the HitTester. This is used to describe
/// hierarchical clip scroll nodes as well as ClipChains, so that they can be
/// handled the same way during hit testing. Once we represent all ClipChains
@ -37,7 +42,7 @@ pub struct HitTestClipScrollNode {
#[derive(Clone)]
struct HitTestClipChainDescriptor {
parent: Option<ClipChainIndex>,
clips: Vec<ClipScrollNodeIndex>,
clips: Vec<ClipNodeIndex>,
}
impl HitTestClipChainDescriptor {
@ -93,9 +98,10 @@ impl HitTestRegion {
pub struct HitTester {
runs: Vec<HitTestingRun>,
nodes: Vec<HitTestClipScrollNode>,
spatial_nodes: Vec<HitTestSpatialNode>,
clip_nodes: Vec<HitTestClipNode>,
clip_chains: Vec<HitTestClipChainDescriptor>,
pipeline_root_nodes: FastHashMap<PipelineId, ClipScrollNodeIndex>,
pipeline_root_nodes: FastHashMap<PipelineId, SpatialNodeIndex>,
}
impl HitTester {
@ -106,7 +112,8 @@ impl HitTester {
) -> HitTester {
let mut hit_tester = HitTester {
runs: runs.clone(),
nodes: Vec::new(),
spatial_nodes: Vec::new(),
clip_nodes: Vec::new(),
clip_chains: Vec::new(),
pipeline_root_nodes: FastHashMap::default(),
};
@ -119,33 +126,41 @@ impl HitTester {
clip_scroll_tree: &ClipScrollTree,
clip_store: &ClipStore
) {
self.nodes.clear();
self.spatial_nodes.clear();
self.clip_chains.clear();
self.clip_chains.resize(
clip_scroll_tree.clip_chains.len(),
HitTestClipChainDescriptor::empty()
);
for (index, node) in clip_scroll_tree.nodes.iter().enumerate() {
let index = ClipScrollNodeIndex(index);
for (index, node) in clip_scroll_tree.spatial_nodes.iter().enumerate() {
let index = SpatialNodeIndex(index);
// If we haven't already seen a node for this pipeline, record this one as the root
// node.
self.pipeline_root_nodes.entry(node.pipeline_id).or_insert(index);
self.nodes.push(HitTestClipScrollNode {
self.spatial_nodes.push(HitTestSpatialNode {
pipeline_id: node.pipeline_id,
regions: get_regions_for_clip_scroll_node(node, clip_store),
world_content_transform: node.world_content_transform,
world_viewport_transform: node.world_viewport_transform,
});
}
if let NodeType::Clip { clip_chain_index, .. } = node.node_type {
let clip_chain = self.clip_chains.get_mut(clip_chain_index.0).unwrap();
clip_chain.parent =
clip_scroll_tree.get_clip_chain(clip_chain_index).parent_index;
clip_chain.clips = vec![index];
}
for (index, node) in clip_scroll_tree.clip_nodes.iter().enumerate() {
let regions = match get_regions_for_clip_node(node, clip_store) {
Some(regions) => regions,
None => continue,
};
self.clip_nodes.push(HitTestClipNode {
spatial_node: node.spatial_node,
regions,
});
let clip_chain = self.clip_chains.get_mut(node.clip_chain_index.0).unwrap();
clip_chain.parent =
clip_scroll_tree.get_clip_chain(node.clip_chain_index).parent_index;
clip_chain.clips = vec![ClipNodeIndex(index)];
}
for descriptor in &clip_scroll_tree.clip_chains_descriptors {
@ -177,7 +192,7 @@ impl HitTester {
}
for clip_node_index in &descriptor.clips {
if !self.is_point_clipped_in_for_node(point, *clip_node_index, test) {
if !self.is_point_clipped_in_for_clip_node(point, *clip_node_index, test) {
test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::NotClippedIn);
return false;
}
@ -187,18 +202,18 @@ impl HitTester {
true
}
fn is_point_clipped_in_for_node(
fn is_point_clipped_in_for_clip_node(
&self,
point: WorldPoint,
node_index: ClipScrollNodeIndex,
node_index: ClipNodeIndex,
test: &mut HitTest
) -> bool {
if let Some(clipped_in) = test.node_cache.get(&node_index) {
return *clipped_in == ClippedIn::ClippedIn;
}
let node = &self.nodes[node_index.0];
let transform = node.world_viewport_transform;
let node = &self.clip_nodes[node_index.0];
let transform = self.spatial_nodes[node.spatial_node.0].world_viewport_transform;
let transformed_point = match transform.inverse() {
Some(inverted) => inverted.transform_point2d(&point),
None => {
@ -218,12 +233,12 @@ impl HitTester {
true
}
pub fn find_node_under_point(&self, mut test: HitTest) -> Option<ClipScrollNodeIndex> {
pub fn find_node_under_point(&self, mut test: HitTest) -> Option<SpatialNodeIndex> {
let point = test.get_absolute_point(self);
for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
let scroll_node_id = clip_and_scroll.scroll_node_id;
let scroll_node = &self.nodes[scroll_node_id.0];
let scroll_node = &self.spatial_nodes[scroll_node_id.0];
let transform = scroll_node.world_content_transform;
let point_in_layer = match transform.inverse() {
Some(inverted) => inverted.transform_point2d(&point),
@ -257,7 +272,7 @@ impl HitTester {
let mut result = HitTestResult::default();
for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
let scroll_node_id = clip_and_scroll.scroll_node_id;
let scroll_node = &self.nodes[scroll_node_id.0];
let scroll_node = &self.spatial_nodes[scroll_node_id.0];
let pipeline_id = scroll_node.pipeline_id;
match (test.pipeline_id, pipeline_id) {
(Some(id), node_id) if node_id != id => continue,
@ -296,7 +311,7 @@ impl HitTester {
// the pipeline of the hit item. If we cannot get a transformed point, we are
// in a situation with an uninvertible transformation so we should just skip this
// result.
let root_node = &self.nodes[self.pipeline_root_nodes[&pipeline_id].0];
let root_node = &self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0];
let point_in_viewport = match root_node.world_viewport_transform.inverse() {
Some(inverted) => inverted.transform_point2d(&point),
None => continue,
@ -318,21 +333,25 @@ impl HitTester {
result
}
pub fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestClipScrollNode {
&self.nodes[self.pipeline_root_nodes[&pipeline_id].0]
pub fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestSpatialNode {
&self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0]
}
}
fn get_regions_for_clip_scroll_node(
node: &ClipScrollNode,
fn get_regions_for_clip_node(
node: &ClipNode,
clip_store: &ClipStore
) -> Vec<HitTestRegion> {
let clips = match node.node_type {
NodeType::Clip{ ref handle, .. } => clip_store.get(handle).clips(),
_ => return Vec::new(),
) -> Option<Vec<HitTestRegion>> {
let handle = match node.handle.as_ref() {
Some(handle) => handle,
None => {
warn!("Encountered an empty clip node unexpectedly.");
return None;
}
};
clips.iter().map(|source| {
let clips = clip_store.get(handle).clips();
Some(clips.iter().map(|source| {
match source.0 {
ClipSource::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode),
ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
@ -343,7 +362,7 @@ fn get_regions_for_clip_scroll_node(
unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration");
}
}
}).collect()
}).collect())
}
#[derive(Clone, Copy, PartialEq)]
@ -356,7 +375,7 @@ pub struct HitTest {
pipeline_id: Option<PipelineId>,
point: WorldPoint,
flags: HitTestFlags,
node_cache: FastHashMap<ClipScrollNodeIndex, ClippedIn>,
node_cache: FastHashMap<ClipNodeIndex, ClippedIn>,
clip_chain_cache: Vec<Option<ClippedIn>>,
}

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

@ -60,7 +60,7 @@ mod box_shadow;
#[cfg(any(feature = "capture", feature = "replay"))]
mod capture;
mod clip;
mod clip_scroll_node;
mod clip_node;
mod clip_scroll_tree;
mod debug_colors;
#[cfg(feature = "debug_renderer")]
@ -98,6 +98,7 @@ mod scene;
mod scene_builder;
mod segment;
mod shade;
mod spatial_node;
mod texture_allocator;
mod texture_cache;
mod tiling;

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

@ -6,8 +6,8 @@ use api::{DeviceRect, FilterOp, MixBlendMode, PipelineId, PremultipliedColorF};
use api::{DeviceIntRect, DeviceIntSize, DevicePoint, LayoutPoint, LayoutRect};
use api::{DevicePixelScale, PictureIntPoint, PictureIntRect, PictureIntSize};
use box_shadow::{BLUR_SAMPLE_SCALE};
use clip_scroll_node::ClipScrollNode;
use clip_scroll_tree::ClipScrollNodeIndex;
use spatial_node::SpatialNode;
use clip_scroll_tree::SpatialNodeIndex;
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PrimitiveRunContext};
use gpu_cache::{GpuCacheHandle};
use gpu_types::UvRectKind;
@ -150,7 +150,7 @@ pub struct PicturePrimitive {
// The original reference frame ID for this picture.
// It is only different if this is part of a 3D
// rendering context.
pub reference_frame_index: ClipScrollNodeIndex,
pub reference_frame_index: SpatialNodeIndex,
pub real_local_rect: LayoutRect,
// An optional cache handle for storing extra data
// in the GPU cache, depending on the type of
@ -183,7 +183,7 @@ impl PicturePrimitive {
composite_mode: Option<PictureCompositeMode>,
is_in_3d_context: bool,
pipeline_id: PipelineId,
reference_frame_index: ClipScrollNodeIndex,
reference_frame_index: SpatialNodeIndex,
frame_output_pipeline_id: Option<PipelineId>,
apply_local_clip_rect: bool,
) -> Self {
@ -590,18 +590,18 @@ impl PicturePrimitive {
// Calculate a single screen-space UV for a picture.
fn calculate_screen_uv(
local_pos: &LayoutPoint,
clip_scroll_node: &ClipScrollNode,
spatial_node: &SpatialNode,
rendered_rect: &DeviceRect,
device_pixel_scale: DevicePixelScale,
) -> DevicePoint {
let world_pos = clip_scroll_node
let world_pos = spatial_node
.world_content_transform
.transform_point2d(local_pos);
let mut device_pos = world_pos * device_pixel_scale;
// Apply snapping for axis-aligned scroll nodes, as per prim_shared.glsl.
if clip_scroll_node.transform_kind == TransformedRectKind::AxisAligned {
if spatial_node.transform_kind == TransformedRectKind::AxisAligned {
device_pos.x = (device_pos.x + 0.5).floor();
device_pos.y = (device_pos.y + 0.5).floor();
}
@ -616,7 +616,7 @@ fn calculate_screen_uv(
// vertex positions of a picture.
fn calculate_uv_rect_kind(
local_rect: &LayoutRect,
clip_scroll_node: &ClipScrollNode,
spatial_node: &SpatialNode,
rendered_rect: &DeviceIntRect,
device_pixel_scale: DevicePixelScale,
) -> UvRectKind {
@ -624,28 +624,28 @@ fn calculate_uv_rect_kind(
let top_left = calculate_screen_uv(
&local_rect.origin,
clip_scroll_node,
spatial_node,
&rendered_rect,
device_pixel_scale,
);
let top_right = calculate_screen_uv(
&local_rect.top_right(),
clip_scroll_node,
spatial_node,
&rendered_rect,
device_pixel_scale,
);
let bottom_left = calculate_screen_uv(
&local_rect.bottom_left(),
clip_scroll_node,
spatial_node,
&rendered_rect,
device_pixel_scale,
);
let bottom_right = calculate_screen_uv(
&local_rect.bottom_right(),
clip_scroll_node,
spatial_node,
&rendered_rect,
device_pixel_scale,
);

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

@ -187,7 +187,7 @@ fn new_ct_font_with_variations(cg_font: &CGFont, size: f64, variations: &[FontVa
}
let tag_val = match axis.find(kCTFontVariationAxisIdentifierKey as *const _) {
Some(tag_ptr) => {
let tag: CFNumber = TCFType::wrap_under_get_rule(tag_ptr as CFNumberRef);
let tag: CFNumber = TCFType::wrap_under_get_rule(*tag_ptr as CFNumberRef);
if !tag.instance_of::<CFNumber>() {
return ct_font;
}
@ -204,7 +204,7 @@ fn new_ct_font_with_variations(cg_font: &CGFont, size: f64, variations: &[FontVa
};
let name: CFString = match axis.find(kCTFontVariationAxisNameKey as *const _) {
Some(name_ptr) => TCFType::wrap_under_get_rule(name_ptr as CFStringRef),
Some(name_ptr) => TCFType::wrap_under_get_rule(*name_ptr as CFStringRef),
None => return ct_font,
};
if !name.instance_of::<CFString>() {
@ -213,7 +213,7 @@ fn new_ct_font_with_variations(cg_font: &CGFont, size: f64, variations: &[FontVa
let min_val = match axis.find(kCTFontVariationAxisMinimumValueKey as *const _) {
Some(min_ptr) => {
let min: CFNumber = TCFType::wrap_under_get_rule(min_ptr as CFNumberRef);
let min: CFNumber = TCFType::wrap_under_get_rule(*min_ptr as CFNumberRef);
if !min.instance_of::<CFNumber>() {
return ct_font;
}
@ -226,7 +226,7 @@ fn new_ct_font_with_variations(cg_font: &CGFont, size: f64, variations: &[FontVa
};
let max_val = match axis.find(kCTFontVariationAxisMaximumValueKey as *const _) {
Some(max_ptr) => {
let max: CFNumber = TCFType::wrap_under_get_rule(max_ptr as CFNumberRef);
let max: CFNumber = TCFType::wrap_under_get_rule(*max_ptr as CFNumberRef);
if !max.instance_of::<CFNumber>() {
return ct_font;
}
@ -239,7 +239,7 @@ fn new_ct_font_with_variations(cg_font: &CGFont, size: f64, variations: &[FontVa
};
let def_val = match axis.find(kCTFontVariationAxisDefaultValueKey as *const _) {
Some(def_ptr) => {
let def: CFNumber = TCFType::wrap_under_get_rule(def_ptr as CFNumberRef);
let def: CFNumber = TCFType::wrap_under_get_rule(*def_ptr as CFNumberRef);
if !def.instance_of::<CFNumber>() {
return ct_font;
}

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

@ -11,8 +11,7 @@ use api::{BorderWidths, LayoutToWorldScale, NormalBorder};
use app_units::Au;
use border::{BorderCacheKey, BorderRenderTaskInfo};
use box_shadow::BLUR_SAMPLE_SCALE;
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
use clip_scroll_node::ClipScrollNode;
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, SpatialNodeIndex};
use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource};
use clip::{ClipSourcesHandle, ClipWorkItem};
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
@ -31,9 +30,10 @@ use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
use resource_cache::{ImageProperties, ImageRequest, ResourceCache};
use scene::SceneProperties;
use segment::SegmentBuilder;
use spatial_node::SpatialNode;
use std::{mem, usize};
use std::sync::Arc;
use util::{MatrixHelpers, WorldToLayoutFastTransform, calculate_screen_bounding_rect};
use util::{MatrixHelpers, calculate_screen_bounding_rect};
use util::{pack_as_float, recycle_vec};
@ -42,13 +42,13 @@ pub const VECS_PER_SEGMENT: usize = 2;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ScrollNodeAndClipChain {
pub scroll_node_id: ClipScrollNodeIndex,
pub scroll_node_id: SpatialNodeIndex,
pub clip_chain_index: ClipChainIndex,
}
impl ScrollNodeAndClipChain {
pub fn new(
scroll_node_id: ClipScrollNodeIndex,
scroll_node_id: SpatialNodeIndex,
clip_chain_index: ClipChainIndex
) -> Self {
ScrollNodeAndClipChain { scroll_node_id, clip_chain_index }
@ -1242,7 +1242,7 @@ impl PrimitiveStore {
composite_mode: Option<PictureCompositeMode>,
is_in_3d_context: bool,
pipeline_id: PipelineId,
reference_frame_index: ClipScrollNodeIndex,
reference_frame_index: SpatialNodeIndex,
frame_output_pipeline_id: Option<PipelineId>,
apply_local_clip_rect: bool,
) -> PictureIndex {
@ -1994,7 +1994,6 @@ impl PrimitiveStore {
prim_run_context: &PrimitiveRunContext,
clips: &Vec<ClipWorkItem>,
has_clips_from_other_coordinate_systems: bool,
frame_context: &FrameBuildingContext,
frame_state: &mut FrameBuildingState,
) {
match brush.segment_desc {
@ -2047,19 +2046,39 @@ impl PrimitiveStore {
}
let local_clips = frame_state.clip_store.get_opt(&clip_item.clip_sources).expect("bug");
rect_clips_only = rect_clips_only && local_clips.only_rectangular_clips;
// TODO(gw): We can easily extend the segment builder to support these clip sources in
// the future, but they are rarely used.
// We must do this check here in case we continue early below.
if local_clips.has_image_or_line_decoration_clip {
clip_mask_kind = BrushClipMaskKind::Global;
}
// If this clip item is positioned by another positioning node, its relative position
// could change during scrolling. This means that we would need to resegment. Instead
// of doing that, only segment with clips that have the same positioning node.
// TODO(mrobinson, #2858): It may make sense to include these nodes, resegmenting only
// when necessary while scrolling.
if clip_item.transform_index != prim_run_context.transform_index {
// We don't need to generate a global clip mask for rectangle clips because we are
// in the same coordinate system and rectangular clips are handled by the local
// clip chain rectangle.
if !local_clips.only_rectangular_clips {
clip_mask_kind = BrushClipMaskKind::Global;
}
continue;
}
for &(ref clip, _) in &local_clips.clips {
let (local_clip_rect, radius, mode) = match *clip {
ClipSource::RoundedRectangle(rect, radii, clip_mode) => {
rect_clips_only = false;
(rect, Some(radii), clip_mode)
}
ClipSource::Rectangle(rect, mode) => {
(rect, None, mode)
}
ClipSource::BoxShadow(ref info) => {
rect_clips_only = false;
// For inset box shadows, we can clip out any
// pixels that are inside the shadow region
// and are beyond the inner rect, as they can't
@ -2084,35 +2103,7 @@ impl PrimitiveStore {
continue;
}
ClipSource::LineDecoration(..) |
ClipSource::Image(..) => {
rect_clips_only = false;
// TODO(gw): We can easily extend the segment builder
// to support these clip sources in the
// future, but they are rarely used.
clip_mask_kind = BrushClipMaskKind::Global;
continue;
}
};
// If the scroll node transforms are different between the clip
// node and the primitive, we need to get the clip rect in the
// local space of the primitive, in order to generate correct
// local segments.
let local_clip_rect = if clip_item.transform_index == prim_run_context.scroll_node.transform_index {
local_clip_rect
} else {
let clip_transform = frame_context
.transforms[clip_item.transform_index.0 as usize]
.transform;
let prim_transform = &prim_run_context.scroll_node.world_content_transform;
let relative_transform = prim_transform
.inverse()
.unwrap_or(WorldToLayoutFastTransform::identity())
.pre_mul(&clip_transform.into());
relative_transform.transform_rect(&local_clip_rect)
ClipSource::LineDecoration(..) | ClipSource::Image(..) => continue,
};
segment_builder.push_clip_rect(local_clip_rect, radius, mode);
@ -2181,7 +2172,6 @@ impl PrimitiveStore {
prim_run_context,
clips,
has_clips_from_other_coordinate_systems,
frame_context,
frame_state,
);
@ -2303,11 +2293,11 @@ impl PrimitiveStore {
Arc::new(ClipChainNode {
work_item: ClipWorkItem {
transform_index: prim_run_context.scroll_node.transform_index,
transform_index: prim_run_context.transform_index,
clip_sources: clip_sources.weak(),
coordinate_system_id: prim_coordinate_system_id,
},
// The local_clip_rect a property of ClipChain nodes that are ClipScrollNodes.
// The local_clip_rect a property of ClipChain nodes that are ClipNodes.
// It's used to calculate a local clipping rectangle before we reach this
// point, so we can set it to zero here. It should be unused from this point
// on.
@ -2649,7 +2639,7 @@ impl PrimitiveStore {
// lookups ever show up in a profile).
let scroll_node = &frame_context
.clip_scroll_tree
.nodes[run.clip_and_scroll.scroll_node_id.0];
.spatial_nodes[run.clip_and_scroll.scroll_node_id.0];
let clip_chain = frame_context
.clip_scroll_tree
.get_clip_chain(run.clip_and_scroll.clip_chain_index);
@ -2685,7 +2675,7 @@ impl PrimitiveStore {
.and_then(|original_reference_frame_index| {
frame_context
.clip_scroll_tree
.nodes[original_reference_frame_index.0]
.spatial_nodes[original_reference_frame_index.0]
.world_content_transform
.inverse()
})
@ -2722,6 +2712,7 @@ impl PrimitiveStore {
let child_prim_run_context = PrimitiveRunContext::new(
clip_chain,
scroll_node,
run.clip_and_scroll.scroll_node_id.transform_index(),
local_clip_chain_rect,
);
@ -2914,7 +2905,7 @@ fn convert_clip_chain_to_clip_vector(
}
fn get_local_clip_rect_for_nodes(
scroll_node: &ClipScrollNode,
scroll_node: &SpatialNode,
clip_chain: &ClipChain,
) -> Option<LayoutRect> {
ClipChainNodeIter { current: clip_chain.nodes.clone() }

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