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

This commit is contained in:
Cosmin Sabou 2018-04-04 21:07:07 +03:00
commit 0ffa0aefd9
139 changed files with 4207 additions and 2797 deletions

24
Cargo.lock generated
View File

@ -54,7 +54,7 @@ version = "0.2.3"
dependencies = [
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -90,7 +90,7 @@ version = "0.2.2"
dependencies = [
"audioipc 0.2.3",
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -407,10 +407,10 @@ dependencies = [
[[package]]
name = "cubeb"
version = "0.5.0"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cubeb-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb-core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -418,16 +418,16 @@ name = "cubeb-backend"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cubeb-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb-core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cubeb-core"
version = "0.5.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -442,7 +442,7 @@ dependencies = [
[[package]]
name = "cubeb-sys"
version = "0.5.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
@ -740,7 +740,7 @@ dependencies = [
"audioipc-server 0.2.2",
"cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb-pulse 0.2.0",
"cubeb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cubeb-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding_c 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding_glue 0.1.0",
"geckoservo 0.0.1",
@ -2264,10 +2264,10 @@ dependencies = [
"checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df"
"checksum cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b6557bdb1dc9647eae1cf7f5601b14cd45fc3c7ccf2df618387416fe542da6ea"
"checksum cstr-macros 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f9f316203d1ea36f4f18316822806f6999aa3dc5ed1adf51e35b77e3b3933d78"
"checksum cubeb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7923bed2d5a1a64ba0c3e8b6badc360508ba488d1f2f59f16a688802e755bb85"
"checksum cubeb 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8a3502aafa1bf95c524f65d2ba46d8741700c6a8a9543ea52c6da3d8b69a2896"
"checksum cubeb-backend 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcac95519416d9ec814db2dc40e6293e7da25b906023d93f48b87f0587ab138"
"checksum cubeb-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "169d9a36f5daa60f9c4597905132aef056cf62693addd4a3421492853ccad565"
"checksum cubeb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e405ad4dff2c1a7cbfa6998e5925e8ceafe900feeacfcad35a3e3790bea0f2aa"
"checksum cubeb-core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37f7b20f757a4e4b6aa28863236551bff77682dc6db192eba15af615492b5445"
"checksum cubeb-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "653b9e245d35dbe2a2da7c4586275cee75ff656ddeb02d4a73b4afdfa6d67502"
"checksum darling 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d3effd06d4057f275cb7858889f4952920bab78dd8ff0f6e7dfe0c8d2e67ed89"
"checksum darling_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "167dd3e235c2f1da16a635c282630452cdf49191eb05711de1bcd1d3d5068c00"
"checksum darling_macro 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c53edaba455f6073a10c27c72440860eb3f60444f8c8660a391032eeae744d82"

View File

@ -18,8 +18,6 @@
<!DOCTYPE page [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
<!ENTITY % preferencesDTD SYSTEM
"chrome://browser/locale/preferences/preferences.dtd">
<!ENTITY % selectBookmarkDTD SYSTEM
"chrome://browser/locale/preferences/selectBookmark.dtd">
<!ENTITY % languagesDTD SYSTEM "chrome://browser/locale/preferences/languages.dtd">
@ -45,7 +43,6 @@
<!ENTITY % aboutDialogDTD SYSTEM "chrome://browser/locale/aboutDialog.dtd" >
%aboutDialogDTD;
%brandDTD;
%preferencesDTD;
%selectBookmarkDTD;
%languagesDTD;
%fontDTD;

View File

@ -118,7 +118,7 @@ var gSyncPane = {
// Use cached values while we wait for the up-to-date values
let cachedComputerName = Services.prefs.getCharPref("services.sync.client.name", "");
document.querySelector(".fxaEmailAddress").value = username;
document.getElementById("fxaEmailAddress").textContent = username;
this._populateComputerName(cachedComputerName);
this.page = FXA_PAGE_LOGGED_IN;
},
@ -259,7 +259,7 @@ var gSyncPane = {
.wrappedJSObject;
let displayNameLabel = document.getElementById("fxaDisplayName");
let fxaEmailAddressLabels = document.querySelectorAll(".fxaEmailAddress");
let fxaEmailAddressLabels = document.querySelectorAll(".l10nArgsEmailAddress");
displayNameLabel.hidden = true;
// determine the fxa status...
@ -288,8 +288,11 @@ var gSyncPane = {
syncReady = true;
}
fxaEmailAddressLabels.forEach((label) => {
label.value = state.email;
let l10nAttrs = document.l10n.getAttributes(label);
document.l10n.setAttributes(l10nAttrs.id, {email: state.email});
});
document.getElementById("fxaEmailAddress").textContent = state.email;
this._populateComputerName(Weave.Service.clientsEngine.localName);
let engines = document.getElementById("fxaSyncEngines");
for (let checkbox of engines.querySelectorAll("checkbox")) {

View File

@ -11,7 +11,7 @@
class="subcategory"
hidden="true"
data-category="paneSync">
<label class="header-name" flex="1">&paneSync1.title;</label>
<label class="header-name" flex="1" data-l10n-id="pane-sync-title" />
</hbox>
<deck id="weavePrefsDeck" data-category="paneSync" hidden="true"
@ -19,8 +19,8 @@
<groupbox id="noFxaAccount">
<hbox>
<vbox flex="1">
<caption><label id="noFxaCaption">&signedOut.caption;</label></caption>
<description id="noFxaDescription" flex="1">&signedOut.description;</description>
<caption><label id="noFxaCaption" data-l10n-id="sync-signedout-caption"/></caption>
<description id="noFxaDescription" flex="1" data-l10n-id="sync-signedout-description"/>
</vbox>
<vbox>
<image class="fxaSyncIllustration"/>
@ -33,17 +33,16 @@
<vbox flex="1">
<hbox align="center" flex="1">
<hbox align="center" flex="1">
<caption><label id="signedOutAccountBoxTitle">&signedOut.accountBox.title;</label></caption>
<caption><label id="signedOutAccountBoxTitle" data-l10n-id="sync-signedout-account-title"/></caption>
</hbox>
<button id="noFxaSignIn"
class="accessory-button"
label="&signedOut.accountBox.signin2;"
accesskey="&signedOut.accountBox.signin2.accesskey;"/>
data-l10n-id="sync-signedout-account-signin"/>
</hbox>
<hbox align="center" flex="1">
<html:a id="noFxaSignUp"
class="openLink"
accesskey="&signedOut.accountBox.create2.accesskey;">&signedOut.accountBox.create2;</html:a>
data-l10n-id="sync-signedout-account-create" />
</hbox>
</vbox>
</hbox>
@ -64,7 +63,7 @@
<hbox>
<vbox id="fxaContentWrapper" flex="1">
<groupbox id="fxaGroup">
<caption class="search-header" hidden="true"><label>&paneSync1.title;</label></caption>
<caption class="search-header" hidden="true"><label data-l10n-id="pane-sync-title"/></caption>
<deck id="fxaLoginStatus" flex="1">
@ -74,20 +73,19 @@
role="button"
onclick="gSyncPane.openChangeProfileImage(event);"
onkeypress="gSyncPane.openChangeProfileImage(event);"
tooltiptext="&profilePicture.tooltip;"/>
data-l10n-id="sync-profile-picture"/>
<vbox flex="1" pack="center">
<hbox flex="1" align="baseline">
<caption><label id="fxaDisplayName" hidden="true"/></caption>
<label class="fxaEmailAddress" flex="1" crop="end"/>
<label id="fxaEmailAddress" flex="1" crop="end"/>
<button id="fxaUnlinkButton"
class="accessory-button"
label="&disconnect3.label;"
accesskey="&disconnect3.accesskey;"/>
data-l10n-id="sync-disconnect"/>
</hbox>
<hbox>
<html:a id="verifiedManage" class="openLink"
accesskey="&verifiedManage.accesskey;"
onkeypress="gSyncPane.openManageFirefoxAccount(event);">&verifiedManage.label;</html:a>
data-l10n-id="sync-manage-account"
onkeypress="gSyncPane.openManageFirefoxAccount(event);"/>
</hbox>
</vbox>
</hbox>
@ -100,15 +98,14 @@
<vbox flex="1" pack="center">
<hbox>
<image class="fxaLoginRejectedWarning"/>
<description flex="1">
&signedInUnverified.beforename.label;
<label class="fxaEmailAddress"/>
&signedInUnverified.aftername.label;
</description>
<description flex="1"
class="l10nArgsEmailAddress"
data-l10n-id="sync-signedin-unverified"
data-l10n-args='{"email": ""}'/>
</hbox>
<hbox class="fxaAccountBoxButtons">
<button id="verifyFxaAccount" label="&resendVerification.label;" accesskey="&resendVerification.accesskey;"></button>
<button id="unverifiedUnlinkFxaAccount" label="&removeAccount.label;" accesskey="&removeAccount.accesskey;"></button>
<button id="verifyFxaAccount" data-l10n-id="sync-resend-verification"/>
<button id="unverifiedUnlinkFxaAccount" data-l10n-id="sync-remove-account"/>
</hbox>
</vbox>
</hbox>
@ -121,63 +118,42 @@
<vbox flex="1" pack="center">
<hbox>
<image class="fxaLoginRejectedWarning"/>
<description flex="1">
&signedInLoginFailure.beforename.label;
<label class="fxaEmailAddress"/>
&signedInLoginFailure.aftername.label;
</description>
<description flex="1"
class="l10nArgsEmailAddress"
data-l10n-id="sync-signedin-login-failure"
data-l10n-args='{"email": ""}'/>
</hbox>
<hbox class="fxaAccountBoxButtons">
<button id="rejectReSignIn" label="&signIn.label;" accesskey="&signIn.accesskey;"></button>
<button id="rejectUnlinkFxaAccount" label="&removeAccount.label;" accesskey="&removeAccount.accesskey;"></button>
<button id="rejectReSignIn" data-l10n-id="sync-sign-in"/>
<button id="rejectUnlinkFxaAccount" data-l10n-id="sync-remove-account"/>
</hbox>
</vbox>
</hbox>
</deck>
</groupbox>
<groupbox id="syncOptions">
<caption><label>&signedIn.settings.label;</label></caption>
<description>&signedIn.settings.description;</description>
<caption><label data-l10n-id="sync-signedin-settings-header"/></caption>
<description data-l10n-id="sync-signedin-settings-desc"/>
<hbox id="fxaSyncEngines">
<vbox flex="1">
<!-- by design, no tooltip for bookmarks or history -->
<checkbox label="&engine.bookmarks.label;"
accesskey="&engine.bookmarks.accesskey;"
<checkbox data-l10n-id="sync-engine-bookmarks"
preference="engine.bookmarks"/>
<checkbox label="&engine.history.label;"
accesskey="&engine.history.accesskey;"
<checkbox data-l10n-id="sync-engine-history"
preference="engine.history"/>
<checkbox label="&engine.tabs.label2;"
tooltiptext="&engine.tabs.title;"
accesskey="&engine.tabs.accesskey;"
<checkbox data-l10n-id="sync-engine-tabs"
preference="engine.tabs"/>
<checkbox label="&engine.logins.label;"
tooltiptext="&engine.logins.title;"
accesskey="&engine.logins.accesskey;"
<checkbox data-l10n-id="sync-engine-logins"
preference="engine.passwords"/>
</vbox>
<vbox flex="1">
<checkbox label="&engine.addresses.label;"
accesskey="&engine.addresses.accesskey;"
tooltiptext="&engine.addresses.title;"
<checkbox data-l10n-id="sync-engine-addresses"
preference="engine.addresses"/>
<checkbox label="&engine.creditcards.label;"
tooltiptext="&engine.creditcards.title;"
accesskey="&engine.creditcards.accesskey;"
<checkbox data-l10n-id="sync-engine-creditcards"
preference="engine.creditcards"/>
<checkbox label="&engine.addons.label;"
tooltiptext="&engine.addons.title;"
accesskey="&engine.addons.accesskey;"
<checkbox data-l10n-id="sync-engine-addons"
preference="engine.addons"/>
<checkbox
#ifdef XP_WIN
label="&engine.prefsWin.label;"
accesskey="&engine.prefsWin.accesskey;"
#else
label="&engine.prefs.label;"
accesskey="&engine.prefs.accesskey;"
#endif
tooltiptext="&engine.prefs.title;"
<checkbox data-l10n-id="sync-engine-prefs"
preference="engine.prefs"/>
</vbox>
<spacer/>
@ -187,38 +163,29 @@
</hbox>
<groupbox>
<caption>
<label control="fxaSyncComputerName">
&fxaSyncDeviceName.label;
</label>
<label control="fxaSyncComputerName" data-l10n-id="sync-device-name-header"/>
</caption>
<hbox id="fxaDeviceName">
<textbox id="fxaSyncComputerName" flex="1" disabled="true"/>
<button id="fxaChangeDeviceName"
label="&changeSyncDeviceName2.label;"
accesskey="&changeSyncDeviceName2.accesskey;"/>
data-l10n-id="sync-device-name-change"/>
<button id="fxaCancelChangeDeviceName"
label="&cancelChangeSyncDeviceName.label;"
accesskey="&cancelChangeSyncDeviceName.accesskey;"
data-l10n-id="sync-device-name-cancel"
hidden="true"/>
<button id="fxaSaveChangeDeviceName"
label="&saveChangeSyncDeviceName.label;"
accesskey="&saveChangeSyncDeviceName.accesskey;"
data-l10n-id="sync-device-name-save"
hidden="true"/>
</hbox>
</groupbox>
<vbox align="start">
<label id="mobilePromo-singledevice"
class="text-link fxaMobilePromo">&mobilepromo.singledevice;</label>
class="text-link fxaMobilePromo" data-l10n-id="sync-mobilepromo-single"/>
<label id="mobilePromo-multidevice"
class="text-link fxaMobilePromo">&mobilepromo.multidevice;</label>
class="text-link fxaMobilePromo" data-l10n-id="sync-mobilepromo-multi"/>
</vbox>
<vbox id="tosPP-small" align="start">
<label id="tosPP-small-ToS" class="text-link">
&prefs.tosLink.label;
</label>
<label id="tosPP-small-PP" class="text-link">
&fxaPrivacyNotice.link.label;
</label>
<label id="tosPP-small-ToS" class="text-link" data-l10n-id="sync-tos-link"/>
<label id="tosPP-small-PP" class="text-link" data-l10n-id="sync-fxa-privacy-notice"/>
</vbox>
</vbox>
</deck>

View File

@ -7,3 +7,7 @@
# “Sync” can be localized, “Firefox” must be treated as a brand,
# and kept in English.
-sync-brand-name = Firefox Sync
# “Account” can be localized, “Firefox” must be treated as a brand,
# and kept in English.
-fxaccount-brand-name = Firefox Account

View File

@ -471,6 +471,113 @@ containers-preferences-button =
containers-remove-button =
.label = Remove
## Sync Section - Signed out
sync-signedout-caption = Take Your Web With You
sync-signedout-description = Synchronize your bookmarks, history, tabs, passwords, add-ons, and preferences across all your devices.
sync-signedout-account-title = Connect with a { -fxaccount-brand-name }
sync-signedout-account-create = Dont have an account? Get started
.accesskey = c
sync-signedout-account-signin =
.label = Sign In…
.accesskey = I
## Sync Section - Signed in
sync-profile-picture =
.tooltiptext = Change profile picture
sync-disconnect =
.label = Disconnect…
.accesskey = D
sync-manage-account = Manage account
.accesskey = o
sync-signedin-unverified = { $email } is not verified.
sync-signedin-login-failure = Please sign in to reconnect { $email }
sync-resend-verification =
.label = Resend Verification
.accesskey = d
sync-remove-account =
.label = Remove Account
.accesskey = R
sync-sign-in =
.label = Sign in
.accesskey = g
sync-signedin-settings-header = Sync Settings
sync-signedin-settings-desc = Choose what to synchronize on your devices using { -brand-short-name }
sync-engine-bookmarks =
.label = Bookmarks
.accesskey = m
sync-engine-history =
.label = History
.accesskey = r
sync-engine-tabs =
.label = Open tabs
.tooltiptext = A list of whats open on all synced devices
.accesskey = t
sync-engine-logins =
.label = Logins
.tooltiptext = Usernames and passwords youve saved
.accesskey = L
sync-engine-addresses =
.label = Addresses
.tooltiptext = Postal addresses youve saved (desktop only)
.accesskey = e
sync-engine-creditcards =
.label = Credit cards
.tooltiptext = Names, numbers and expiry dates (desktop only)
.accesskey = C
sync-engine-addons =
.label = Add-ons
.tooltiptext = Extensions and themes for Firefox desktop
.accesskey = A
sync-engine-prefs =
.label =
{ PLATFORM() ->
[windows] Options
*[other] Preferences
}
.tooltiptext = General, Privacy, and Security settings youve changed
.accesskey = s
sync-device-name-header = Device Name
sync-device-name-change =
.label = Change Device Name…
.accesskey = h
sync-device-name-cancel =
.label = Cancel
.accesskey = n
sync-device-name-save =
.label = Save
.accesskey = v
sync-mobilepromo-single = Connect another device
sync-mobilepromo-multi = Manage devices
sync-tos-link = Terms of Service
sync-fxa-privacy-notice = Privacy Notice
## Privacy Section
privacy-header = Browser Privacy

View File

@ -1,6 +0,0 @@
<!-- 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/. -->
<!-- LOCALIZATION NOTE (paneSync1.title): This should match syncBrand.fxAccount.label in ../syncBrand.dtd -->
<!ENTITY paneSync1.title "Firefox Account">

View File

@ -2,90 +2,6 @@
- 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/. -->
<!-- The page shown when logged in... -->
<!ENTITY engine.bookmarks.label "Bookmarks">
<!ENTITY engine.bookmarks.accesskey "m">
<!ENTITY engine.tabs.label2 "Open tabs">
<!ENTITY engine.tabs.title "A list of whats open on all synced devices">
<!ENTITY engine.tabs.accesskey "T">
<!ENTITY engine.history.label "History">
<!ENTITY engine.history.accesskey "r">
<!ENTITY engine.logins.label "Logins">
<!ENTITY engine.logins.title "Usernames and passwords youve saved">
<!ENTITY engine.logins.accesskey "L">
<!-- On Windows we use the term "Options" to describe settings, but
on Linux and Mac OS X we use "Preferences" - carry that distinction
over into this string, used as the checkbox which indicates if prefs are synced
-->
<!ENTITY engine.prefsWin.label "Options">
<!ENTITY engine.prefsWin.accesskey "S">
<!ENTITY engine.prefs.label "Preferences">
<!ENTITY engine.prefs.accesskey "S">
<!ENTITY engine.prefs.title "General, Privacy, and Security settings youve changed">
<!ENTITY engine.addons.label "Add-ons">
<!ENTITY engine.addons.title "Extensions and themes for Firefox desktop">
<!ENTITY engine.addons.accesskey "A">
<!ENTITY engine.addresses.label "Addresses">
<!ENTITY engine.addresses.title "Postal addresses youve saved (desktop only)">
<!ENTITY engine.addresses.accesskey "e">
<!ENTITY engine.creditcards.label "Credit cards">
<!ENTITY engine.creditcards.title "Names, numbers and expiry dates (desktop only)">
<!ENTITY engine.creditcards.accesskey "C">
<!-- Device Settings -->
<!ENTITY fxaSyncDeviceName.label "Device Name">
<!ENTITY changeSyncDeviceName2.label "Change Device Name…">
<!ENTITY changeSyncDeviceName2.accesskey "h">
<!ENTITY cancelChangeSyncDeviceName.label "Cancel">
<!ENTITY cancelChangeSyncDeviceName.accesskey "n">
<!ENTITY saveChangeSyncDeviceName.label "Save">
<!ENTITY saveChangeSyncDeviceName.accesskey "v">
<!-- Footer stuff -->
<!ENTITY prefs.tosLink.label "Terms of Service">
<!ENTITY fxaPrivacyNotice.link.label "Privacy Notice">
<!-- LOCALIZATION NOTE (signedInUnverified.beforename.label,
signedInUnverified.aftername.label): these two string are used respectively
before and after the account email address. Localizers can use one of them, or
both, to better adapt this sentence to their language.
-->
<!ENTITY signedInUnverified.beforename.label "">
<!ENTITY signedInUnverified.aftername.label "is not verified.">
<!-- LOCALIZATION NOTE (signedInLoginFailure.beforename.label,
signedInLoginFailure.aftername.label): these two string are used respectively
before and after the account email address. Localizers can use one of them, or
both, to better adapt this sentence to their language.
-->
<!ENTITY signedInLoginFailure.beforename.label "Please sign in to reconnect">
<!ENTITY signedInLoginFailure.aftername.label "">
<!ENTITY notSignedIn.label "You are not signed in.">
<!ENTITY signIn.label "Sign in">
<!ENTITY signIn.accesskey "g">
<!ENTITY profilePicture.tooltip "Change profile picture">
<!ENTITY verifiedManage.label "Manage account">
<!ENTITY verifiedManage.accesskey "o">
<!ENTITY disconnect3.label "Disconnect…">
<!ENTITY disconnect3.accesskey "D">
<!ENTITY resendVerification.label "Resend Verification">
<!ENTITY resendVerification.accesskey "d">
<!ENTITY removeAccount.label "Remove Account">
<!ENTITY removeAccount.accesskey "R">
<!ENTITY signedOut.caption "Take Your Web With You">
<!ENTITY signedOut.description "Synchronize your bookmarks, history, tabs, passwords, add-ons, and preferences across all your devices.">
<!ENTITY signedOut.accountBox.title "Connect with a &syncBrand.fxAccount.label;">
<!ENTITY signedOut.accountBox.create2 "Dont have an account? Get started">
<!ENTITY signedOut.accountBox.create2.accesskey "C">
<!ENTITY signedOut.accountBox.signin2 "Sign In…">
<!ENTITY signedOut.accountBox.signin2.accesskey "I">
<!ENTITY signedIn.settings.label "Sync Settings">
<!ENTITY signedIn.settings.description "Choose what to synchronize on your devices using &brandShortName;.">
<!-- LOCALIZATION NOTE (mobilePromo3.*): the following strings will be used to
create a single sentence with active links.
The resulting sentence in English is: "Download Firefox for
@ -101,6 +17,3 @@ both, to better adapt this sentence to their language.
<!ENTITY mobilePromo3.iOSLink "iOS">
<!ENTITY mobilePromo3.end " to sync with your mobile device.">
<!ENTITY mobilepromo.singledevice "Connect another device">
<!ENTITY mobilepromo.multidevice "Manage devices">

View File

@ -70,7 +70,6 @@
locale/browser/preferences/languages.dtd (%chrome/browser/preferences/languages.dtd)
locale/browser/preferences/main.dtd (%chrome/browser/preferences/main.dtd)
locale/browser/preferences/permissions.dtd (%chrome/browser/preferences/permissions.dtd)
locale/browser/preferences/preferences.dtd (%chrome/browser/preferences/preferences.dtd)
locale/browser/preferences/preferences.properties (%chrome/browser/preferences/preferences.properties)
locale/browser/preferences/privacy.dtd (%chrome/browser/preferences/privacy.dtd)
locale/browser/preferences/security.dtd (%chrome/browser/preferences/security.dtd)

View File

@ -604,10 +604,6 @@ button > hbox > label {
margin-inline-end: 10px !important;
}
.fxaEmailAddress {
margin-inline-end: 8px !important;
}
.fxaLoginRejectedWarning {
list-style-image: url(chrome://browser/skin/warning.svg);
margin-inline-start: 4px;

View File

@ -1083,12 +1083,9 @@ public:
bool VisitNamedDecl(NamedDecl *D) {
SourceLocation Loc = D->getLocation();
if (isa<EnumConstantDecl>(D) && SM.isMacroBodyExpansion(Loc)) {
// for enum constants generated by macro expansion, update location
// to point to the expansion location as that is more useful. We might
// want to do this for more token types but until we have good regression
// testing for the Indexer it's best to be as conservative and explicit
// as possible with the changes.
// If the token is from a macro expansion and the expansion location
// is interesting, use that instead as it tends to be more useful.
if (SM.isMacroBodyExpansion(Loc)) {
Loc = SM.getFileLoc(Loc);
}

View File

@ -130,6 +130,7 @@ support-files =
[browser_dbg-asm.js]
[browser_dbg-async-stepping.js]
[browser_dbg-babel-breakpoint-console.js]
[browser_dbg-babel-scopes.js]
[browser_dbg-babel-stepping.js]
[browser_dbg-babel-preview.js]

View File

@ -0,0 +1,95 @@
async function evalInConsoleAtPoint(dbg, fixture, { line, column }, statements) {
const { selectors: { getBreakpoint, getBreakpoints }, getState } = dbg;
const filename = `fixtures/${fixture}/input.js`;
await waitForSources(dbg, filename);
ok(true, "Original sources exist");
const source = findSource(dbg, filename);
await selectSource(dbg, source);
// Test that breakpoint is not off by a line.
await addBreakpoint(dbg, source, line);
is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
ok(
getBreakpoint(getState(), { sourceId: source.id, line, column }),
"Breakpoint has correct line"
);
const fnName = fixture.replace(/-([a-z])/g, (s, c) => c.toUpperCase());
const invokeResult = invokeInTab(fnName);
let invokeFailed = await Promise.race([
waitForPaused(dbg),
invokeResult.then(() => new Promise(() => {}), () => true)
]);
if (invokeFailed) {
return invokeResult;
}
assertPausedLocation(dbg);
await assertConsoleEval(dbg, statements);
await removeBreakpoint(dbg, source.id, line, column);
is(getBreakpoints(getState()).size, 0, "Breakpoint reverted");
await resume(dbg);
// If the invoke errored later somehow, capture here so the error is reported nicely.
await invokeResult;
ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`);
}
async function assertConsoleEval(dbg, statements) {
const jsterm = (await dbg.toolbox.selectTool("webconsole")).hud.jsterm;
for (const [index, statement] of statements.entries()) {
await dbg.client.evaluate(`
window.TEST_RESULT = false;
`);
await jsterm.execute(`
TEST_RESULT = ${statement};
`);
const result = await dbg.client.evaluate(`window.TEST_RESULT`);
is(result.result, true, `'${statement}' evaluates to true`);
}
}
add_task(async function() {
await pushPref("devtools.debugger.features.map-scopes", true);
const dbg = await initDebugger("doc-babel.html");
await evalInConsoleAtPoint(dbg, "eval-source-maps", { line: 14, column: 4 }, [
"one === 1",
"two === 4",
"three === 5",
]);
await evalInConsoleAtPoint(dbg, "imported-bindings", { line: 20, column: 2 }, [
`aDefault === "a-default"`,
`anAliased === "an-original"`,
`aNamed === "a-named"`,
`aDefault2 === "a-default2"`,
`anAliased2 === "an-original2"`,
`aNamed2 === "a-named2"`,
`aDefault3 === "a-default3"`,
`anAliased3 === "an-original3"`,
`aNamed3 === "a-named3"`,
]);
await evalInConsoleAtPoint(dbg, "shadowed-vars", { line: 18, column: 6 }, [
`aVar === "var3"`,
`aLet === "let3"`,
`aConst === "const3"`,
]);
});

View File

@ -131,6 +131,12 @@ DebuggerPanel.prototype = {
// DebuggerPanel API
getMappedExpression(expression) {
// No-op implementation since this feature doesn't exist in the older
// debugger implementation.
return expression;
},
isPaused() {
let framesController = this.panelWin.DebuggerController.StackFrames;
let thread = framesController.activeThread;

View File

@ -83,10 +83,10 @@ const TEST_DATA_INNER = [
["u", "style=\"background:unset", 19, 23, true],
["r", "style=\"background:url", 20, 21, false],
["l", "style=\"background:url", 21, 21, false],
["(", "style=\"background:url(", 22, 22, false],
["'", "style=\"background:url('", 23, 23, false],
["1", "style=\"background:url('1", 24, 24, false],
["'", "style=\"background:url('1'", 25, 25, false],
["(", "style=\"background:url()", 22, 22, false],
["'", "style=\"background:url(')", 23, 23, false],
["1", "style=\"background:url('1)", 24, 24, false],
["'", "style=\"background:url('1')", 25, 25, false],
[")", "style=\"background:url('1')", 26, 26, false],
[";", "style=\"background:url('1');", 27, 27, false],
[" ", "style=\"background:url('1'); ", 28, 28, false],

View File

@ -16,7 +16,7 @@
* in view of the tree map.
*/
const { debounce } = require("devtools/shared/debounce");
const EventEmitter = require("devtools/shared/old-event-emitter");
const EventEmitter = require("devtools/shared/event-emitter");
const HTML_NS = "http://www.w3.org/1999/xhtml";
const FULLSCREEN_STYLE = {

View File

@ -6,7 +6,7 @@
const { debounce } = require("devtools/shared/debounce");
const { lerp } = require("devtools/client/memory/utils");
const EventEmitter = require("devtools/shared/old-event-emitter");
const EventEmitter = require("devtools/shared/event-emitter");
const LERP_SPEED = 0.5;
const ZOOM_SPEED = 0.01;

View File

@ -4,7 +4,7 @@
"use strict";
const EventEmitter = require("devtools/shared/old-event-emitter");
const EventEmitter = require("devtools/shared/event-emitter");
const { MemoryFront } = require("devtools/shared/fronts/memory");
const HeapAnalysesClient = require("devtools/shared/heapsnapshot/HeapAnalysesClient");

View File

@ -26,6 +26,8 @@
const Services = require("Services");
const focusManager = Services.focus;
const {KeyCodes} = require("devtools/client/shared/keycodes");
const EventEmitter = require("devtools/shared/event-emitter");
const { findMostRelevantCssPropertyIndex } = require("./suggestion-picker");
loader.lazyRequireGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm", true);
@ -44,8 +46,10 @@ const MAX_POPUP_ENTRIES = 500;
const FOCUS_FORWARD = focusManager.MOVEFOCUS_FORWARD;
const FOCUS_BACKWARD = focusManager.MOVEFOCUS_BACKWARD;
const EventEmitter = require("devtools/shared/event-emitter");
const { findMostRelevantCssPropertyIndex } = require("./suggestion-picker");
const WORD_REGEXP = /\w/;
const isWordChar = function(str) {
return str && WORD_REGEXP.test(str);
};
/**
* Helper to check if the provided key matches one of the expected keys.
@ -1051,6 +1055,10 @@ InplaceEditor.prototype = {
let key = event.keyCode;
let input = this.input;
// We want to autoclose some characters, remember the pressed key in order to process
// it later on in maybeSuggestionCompletion().
this._pressedKey = event.key;
let multilineNavigation = !this._isSingleLine() &&
isKeyIn(key, "UP", "DOWN", "LEFT", "RIGHT");
let isPlainText = this.contentType == CONTENT_TYPES.PLAIN_TEXT;
@ -1293,6 +1301,7 @@ InplaceEditor.prototype = {
if (!this.input) {
return;
}
let preTimeoutQuery = this.input.value;
// Since we are calling this method from a keypress event handler, the
@ -1477,12 +1486,58 @@ InplaceEditor.prototype = {
} else {
this._hideAutocompletePopup();
}
this._autocloseParenthesis();
// This emit is mainly for the purpose of making the test flow simpler.
this.emit("after-suggest");
this._doValidation();
}, 0);
},
/**
* Automatically add closing parenthesis and skip closing parenthesis when needed.
*/
_autocloseParenthesis: function() {
// Split the current value at the cursor index to rebuild the string.
let parts = this._splitStringAt(this.input.value, this.input.selectionStart);
// Lookup the character following the caret to know if the string should be modified.
let nextChar = parts[1][0];
// Autocomplete closing parenthesis if the last key pressed was "(" and the next
// character is not a "word" character.
if (this._pressedKey == "(" && !isWordChar(nextChar)) {
this._updateValue(parts[0] + ")" + parts[1]);
}
// Skip inserting ")" if the next character is already a ")" (note that we actually
// insert and remove the extra ")" here, as the input has already been modified).
if (this._pressedKey == ")" && nextChar == ")") {
this._updateValue(parts[0] + parts[1].substring(1));
}
this._pressedKey = null;
},
/**
* Update the current value of the input while preserving the caret position.
*/
_updateValue: function(str) {
let start = this.input.selectionStart;
this.input.value = str;
this.input.setSelectionRange(start, start);
this._updateSize();
},
/**
* Split the provided string at the provided index. Returns an array of two strings.
* _splitStringAt("1234567", 3) will return ["123", "4567"]
*/
_splitStringAt: function(str, index) {
return [str.substring(0, index), str.substring(index, str.length)];
},
/**
* Check if the current input is displaying more than one line of text.
*

View File

@ -147,6 +147,7 @@ skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
[browser_html_tooltip_xul-wrapper.js]
[browser_inplace-editor-01.js]
[browser_inplace-editor-02.js]
[browser_inplace-editor_autoclose_parentheses.js]
[browser_inplace-editor_autocomplete_01.js]
[browser_inplace-editor_autocomplete_02.js]
[browser_inplace-editor_autocomplete_offset.js]

View File

@ -0,0 +1,73 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from helper_inplace_editor.js */
"use strict";
const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
loadHelperScript("helper_inplace_editor.js");
// Test the inplace-editor closes parentheses automatically.
// format :
// [
// what key to press,
// expected input box value after keypress,
// selected suggestion index (-1 if popup is hidden),
// number of suggestions in the popup (0 if popup is hidden),
// ]
const testData = [
["u", "u", -1, 0],
["r", "ur", -1, 0],
["l", "url", -1, 0],
["(", "url()", -1, 0],
["v", "url(v)", -1, 0],
["a", "url(va)", -1, 0],
["r", "url(var)", -1, 0],
["(", "url(var())", -1, 0],
["-", "url(var(-))", -1, 0],
["-", "url(var(--))", -1, 0],
["a", "url(var(--a))", -1, 0],
[")", "url(var(--a))", -1, 0],
[")", "url(var(--a))", -1, 0],
];
add_task(async function() {
await addTab("data:text/html;charset=utf-8," +
"inplace editor parentheses autoclose");
let [host, win, doc] = await createHost();
let xulDocument = win.top.document;
let popup = new AutocompletePopup(xulDocument, { autoSelect: true });
await new Promise(resolve => {
createInplaceEditorAndClick({
start: runPropertyAutocompletionTest,
contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
property: {
name: "background-image"
},
cssVariables: new Map(),
done: resolve,
popup: popup
}, doc);
});
popup.destroy();
host.destroy();
gBrowser.removeCurrentTab();
});
let runPropertyAutocompletionTest = async function(editor) {
info("Starting to test for css property completion");
// No need to test autocompletion here, return an empty array.
editor._getCSSValuesForPropertyName = () => [];
for (let data of testData) {
await testCompletion(data, editor);
}
EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
};

View File

@ -27,16 +27,16 @@ const testData = [
["v", "v", -1, 0, null],
["a", "va", -1, 0, null],
["r", "var", -1, 0, null],
["(", "var(", -1, 0, null],
["-", "var(--abc", 0, 4, "blue"],
["VK_BACK_SPACE", "var(-", -1, 0, null],
["-", "var(--abc", 0, 4, "blue"],
["VK_DOWN", "var(--def", 1, 4, "red"],
["VK_DOWN", "var(--ghi", 2, 4, "green"],
["VK_DOWN", "var(--jkl", 3, 4, "yellow"],
["VK_DOWN", "var(--abc", 0, 4, "blue"],
["VK_DOWN", "var(--def", 1, 4, "red"],
["VK_LEFT", "var(--def", -1, 0, null],
["(", "var()", -1, 0, null],
["-", "var(--abc)", 0, 4, "blue"],
["VK_BACK_SPACE", "var(-)", -1, 0, null],
["-", "var(--abc)", 0, 4, "blue"],
["VK_DOWN", "var(--def)", 1, 4, "red"],
["VK_DOWN", "var(--ghi)", 2, 4, "green"],
["VK_DOWN", "var(--jkl)", 3, 4, "yellow"],
["VK_DOWN", "var(--abc)", 0, 4, "blue"],
["VK_DOWN", "var(--def)", 1, 4, "red"],
["VK_LEFT", "var(--def)", -1, 0, null],
];
const CSS_VARIABLES = [

View File

@ -482,6 +482,20 @@ WebConsole.prototype = {
return panel.getFrames();
},
async getMappedExpression(expression) {
let toolbox = gDevTools.getToolbox(this.target);
if (!toolbox) {
return expression;
}
let panel = toolbox.getPanel("jsdebugger");
if (!panel) {
return expression;
}
return panel.getMappedExpression(expression);
},
/**
* Retrieves the current selection from the Inspector, if such a selection
* exists. This is used to pass the ID of the selected actor to the Web

View File

@ -436,7 +436,7 @@ JSTerm.prototype = {
* @returns Promise
* Resolves with the message once the result is displayed.
*/
execute: function(executeString, callback) {
execute: async function(executeString, callback) {
let deferred = defer();
let resultCallback;
if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
@ -456,6 +456,21 @@ JSTerm.prototype = {
return null;
}
// Append a new value in the history of executed code, or overwrite the most
// recent entry. The most recent entry may contain the last edited input
// value that was not evaluated yet.
this.history[this.historyIndex++] = executeString;
this.historyPlaceHolder = this.history.length;
if (this.history.length > this.inputHistoryCount) {
this.history.splice(0, this.history.length - this.inputHistoryCount);
this.historyIndex = this.historyPlaceHolder = this.history.length;
}
this.storeHistory();
WebConsoleUtils.usageCount++;
this.setInputValue("");
this.clearCompletion();
let selectedNodeActor = null;
let inspectorSelection = this.hud.owner.getInspectorSelection();
if (inspectorSelection && inspectorSelection.nodeFront) {
@ -482,22 +497,9 @@ JSTerm.prototype = {
selectedNodeActor: selectedNodeActor,
};
this.requestEvaluation(executeString, options).then(onResult, onResult);
const mappedString = await this.hud.owner.getMappedExpression(executeString);
this.requestEvaluation(mappedString, options).then(onResult, onResult);
// Append a new value in the history of executed code, or overwrite the most
// recent entry. The most recent entry may contain the last edited input
// value that was not evaluated yet.
this.history[this.historyIndex++] = executeString;
this.historyPlaceHolder = this.history.length;
if (this.history.length > this.inputHistoryCount) {
this.history.splice(0, this.history.length - this.inputHistoryCount);
this.historyIndex = this.historyPlaceHolder = this.history.length;
}
this.storeHistory();
WebConsoleUtils.usageCount++;
this.setInputValue("");
this.clearCompletion();
return deferred.promise;
},

View File

@ -13,10 +13,10 @@ const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 817834";
add_task(async function() {
let hud = await openNewTabAndConsole(TEST_URI);
testEditedInputHistory(hud);
await testEditedInputHistory(hud);
});
function testEditedInputHistory(hud) {
async function testEditedInputHistory(hud) {
let jsterm = hud.jsterm;
let inputNode = jsterm.inputNode;
@ -30,7 +30,7 @@ function testEditedInputHistory(hud) {
EventUtils.synthesizeKey("KEY_ArrowDown");
is(jsterm.getInputValue(), '"first item"', "null test history down");
jsterm.execute();
await jsterm.execute();
is(jsterm.getInputValue(), "", "cleared input line after submit");
jsterm.setInputValue('"editing input 1"');
@ -41,7 +41,7 @@ function testEditedInputHistory(hud) {
"test history down restores in-progress input");
jsterm.setInputValue('"second item"');
jsterm.execute();
await jsterm.execute();
jsterm.setInputValue('"editing input 2"');
EventUtils.synthesizeKey("KEY_ArrowUp");
is(jsterm.getInputValue(), '"second item"', "test history up");

View File

@ -23,7 +23,7 @@ add_task(async function() {
testSingleLineInputNavNoHistory(jsterm);
testMultiLineInputNavNoHistory(jsterm);
testNavWithHistory(jsterm);
await testNavWithHistory(jsterm);
});
function testSingleLineInputNavNoHistory(jsterm) {
@ -151,7 +151,7 @@ function testMultiLineInputNavNoHistory(jsterm) {
}
}
function testNavWithHistory(jsterm) {
async function testNavWithHistory(jsterm) {
let inputNode = jsterm.inputNode;
// NOTE: Tests does NOT currently define behaviour for ctrl-p/ctrl-n with
@ -165,7 +165,7 @@ function testNavWithHistory(jsterm) {
// submit to history
for (let i = 0; i < values.length; i++) {
jsterm.setInputValue(values[i]);
jsterm.execute();
await jsterm.execute();
}
is(inputNode.selectionStart, 0, "caret location at start of empty line");

View File

@ -20,7 +20,7 @@ add_task(async function() {
ok(false, "popup shown");
};
jsterm.execute(`window.foobarBug660806 = {
await jsterm.execute(`window.foobarBug660806 = {
'location': 'value0',
'locationbar': 'value1'
}`);
@ -36,6 +36,9 @@ add_task(async function() {
EventUtils.synthesizeKey("KEY_Enter");
// Wait for the execution to complete and clear the value.
await waitFor(() => !jsterm.lastInputValue);
let onSetInputValue = jsterm.once("set-input-value");
EventUtils.synthesizeKey("KEY_ArrowUp");
await onSetInputValue;

View File

@ -49,7 +49,7 @@ add_task(async function() {
// Set input value separately from execute so UP arrow accurately navigates
// history.
hud3.jsterm.setInputValue('"hello from third tab"');
hud3.jsterm.execute();
await hud3.jsterm.execute();
is(JSON.stringify(hud1.jsterm.history),
'["0","1","2","3","4","5","6","7","8","9"]',

View File

@ -61,6 +61,8 @@ add_task(async function() {
hud.jsterm.setInputValue(input);
EventUtils.synthesizeKey("VK_RETURN", { shiftKey });
await waitFor(() => !hud.jsterm.getInputValue());
let inputValue = hud.jsterm.getInputValue();
is(inputNode.selectionStart, 0, "selection starts/ends at 0");
is(inputNode.selectionEnd, 0, "selection starts/ends at 0");

View File

@ -28,7 +28,7 @@ add_task(async function() {
info("Execute each test value in the console");
for (let value of TEST_VALUES) {
jsterm.setInputValue(value);
jsterm.execute();
await jsterm.execute();
}
performTests(jsterm);

View File

@ -53,7 +53,7 @@ add_task(function* () {
// Set input value separately from execute so UP arrow accurately navigates
// history.
hud3.jsterm.setInputValue('"hello from third tab"');
hud3.jsterm.execute();
yield hud3.jsterm.execute();
is(JSON.stringify(hud1.jsterm.history),
'["0","1","2","3","4","5","6","7","8","9"]',
@ -98,7 +98,7 @@ function* populateInputHistory(hud) {
// Set input value separately from execute so UP arrow accurately navigates
// history.
jsterm.setInputValue(i);
jsterm.execute();
yield jsterm.execute();
}
}

View File

@ -40,7 +40,7 @@ function test() {
yield toolbox.selectTool("webconsole");
// This is the meat of the test: evaluate the optimized out variable.
hud.jsterm.execute("upvar");
yield hud.jsterm.execute("upvar");
yield waitForMessages({
webconsole: hud,
messages: [{

View File

@ -15,13 +15,13 @@ add_task(function* () {
let hud = yield openConsole();
setup(hud);
yield setup(hud);
performTests();
jsterm = inputNode = values = null;
});
function setup(HUD) {
function* setup(HUD) {
jsterm = HUD.jsterm;
inputNode = jsterm.inputNode;
@ -35,7 +35,7 @@ function setup(HUD) {
// Execute each of the values;
for (let i = 0; i < values.length; i++) {
jsterm.setInputValue(values[i]);
jsterm.execute();
yield jsterm.execute();
}
}

View File

@ -17,12 +17,12 @@ add_task(function* () {
let hud = yield openConsole();
doTests(hud);
yield doTests(hud);
jsterm = inputNode = null;
});
function doTests(HUD) {
function* doTests(HUD) {
jsterm = HUD.jsterm;
inputNode = jsterm.inputNode;
ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
@ -31,7 +31,7 @@ function doTests(HUD) {
testSingleLineInputNavNoHistory();
testMultiLineInputNavNoHistory();
testNavWithHistory();
yield testNavWithHistory();
}
function testSingleLineInputNavNoHistory() {
@ -157,7 +157,7 @@ function testMultiLineInputNavNoHistory() {
}
}
function testNavWithHistory() {
function* testNavWithHistory() {
// NOTE: Tests does NOT currently define behaviour for ctrl-p/ctrl-n with
// caret placed _within_ single line input
let values = ['"single line input"',
@ -167,7 +167,7 @@ function testNavWithHistory() {
// submit to history
for (let i = 0; i < values.length; i++) {
jsterm.setInputValue(values[i]);
jsterm.execute();
yield jsterm.execute();
}
is(inputNode.selectionStart, 0, "caret location at start of empty line");

View File

@ -11,15 +11,15 @@
const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 817834";
add_task(function* () {
yield loadTab(TEST_URI);
add_task(async function () {
await loadTab(TEST_URI);
let hud = yield openConsole();
let hud = await openConsole();
testEditedInputHistory(hud);
await testEditedInputHistory(hud);
});
function testEditedInputHistory(HUD) {
async function testEditedInputHistory(HUD) {
let jsterm = HUD.jsterm;
let inputNode = jsterm.inputNode;
ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
@ -32,7 +32,7 @@ function testEditedInputHistory(HUD) {
EventUtils.synthesizeKey("KEY_ArrowDown");
is(jsterm.getInputValue(), '"first item"', "null test history down");
jsterm.execute();
await jsterm.execute();
is(jsterm.getInputValue(), "", "cleared input line after submit");
jsterm.setInputValue('"editing input 1"');
@ -43,7 +43,7 @@ function testEditedInputHistory(HUD) {
"test history down restores in-progress input");
jsterm.setInputValue('"second item"');
jsterm.execute();
await jsterm.execute();
jsterm.setInputValue('"editing input 2"');
EventUtils.synthesizeKey("KEY_ArrowUp");
is(jsterm.getInputValue(), '"second item"', "test history up");

View File

@ -61,6 +61,9 @@ add_task(function* () {
for (let test of SHOULD_EXECUTE) {
hud.jsterm.setInputValue(test.input);
EventUtils.synthesizeKey("VK_RETURN", { shiftKey: test.shiftKey });
yield waitFor(() => !hud.jsterm.getInputValue());
let inputValue = hud.jsterm.getInputValue();
is(inputNode.selectionStart, 0, "selection starts/ends at 0");
is(inputNode.selectionEnd, 0, "selection starts/ends at 0");

View File

@ -129,6 +129,25 @@ function afterAllTabsLoaded(callback, win) {
}
}
/**
* Wait for a predicate to return a result.
*
* @param function condition
* Invoked once in a while until it returns a truthy value. This should be an
* idempotent function, since we have to run it a second time after it returns
* true in order to return the value.
* @param string message [optional]
* A message to output if the condition fails.
* @param number interval [optional]
* How often the predicate is invoked, in milliseconds.
* @return object
* A promise that is resolved with the result of the condition.
*/
async function waitFor(condition, message = "waitFor", interval = 10, maxTries = 500) {
await BrowserTestUtils.waitForCondition(condition, message, interval, maxTries);
return condition();
}
/**
* Check if a log entry exists in the HUD output node.
*

View File

@ -0,0 +1,85 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
* Creates an actor for the specified ArrayBuffer.
*
* @param buffer ArrayBuffer
* The buffer.
*/
function ArrayBufferActor(buffer) {
this.buffer = buffer;
this.bufferLength = buffer.byteLength;
}
ArrayBufferActor.prototype = {
actorPrefix: "arrayBuffer",
rawValue: function() {
return this.buffer;
},
destroy: function() {
},
grip() {
return {
"type": "arrayBuffer",
"length": this.bufferLength,
"actor": this.actorID
};
},
onSlice({start, count}) {
let slice = new Uint8Array(this.buffer, start, count);
let parts = [], offset = 0;
const PortionSize = 0x6000; // keep it divisible by 3 for btoa() and join()
while (offset + PortionSize < count) {
parts.push(btoa(
String.fromCharCode.apply(null, slice.subarray(offset, offset + PortionSize))));
offset += PortionSize;
}
parts.push(btoa(String.fromCharCode.apply(null, slice.subarray(offset, count))));
return {
"from": this.actorID,
"encoded": parts.join(""),
};
}
};
ArrayBufferActor.prototype.requestTypes = {
"slice": ArrayBufferActor.prototype.onSlice,
};
/**
* Create a grip for the given ArrayBuffer.
*
* @param buffer ArrayBuffer
* The ArrayBuffer we are creating a grip for.
* @param pool ActorPool
* The actor pool where the new actor will be added.
*/
function arrayBufferGrip(buffer, pool) {
if (!pool.arrayBufferActors) {
pool.arrayBufferActors = new WeakMap();
}
if (pool.arrayBufferActors.has(buffer)) {
return pool.arrayBufferActors.get(buffer).grip();
}
let actor = new ArrayBufferActor(buffer);
pool.addActor(actor);
pool.arrayBufferActors.set(buffer, actor);
return actor.grip();
}
module.exports = {
ArrayBufferActor,
arrayBufferGrip,
};

View File

@ -8,7 +8,7 @@
/* global Debugger */
const { ActorClassWithSpec } = require("devtools/shared/protocol");
const { createValueGrip } = require("devtools/server/actors/object");
const { createValueGrip } = require("devtools/server/actors/object/utils");
const { environmentSpec } = require("devtools/shared/specs/environment");
/**

View File

@ -7,7 +7,7 @@
"use strict";
const { ActorPool } = require("devtools/server/actors/common");
const { createValueGrip } = require("devtools/server/actors/object");
const { createValueGrip } = require("devtools/server/actors/object/utils");
const { ActorClassWithSpec } = require("devtools/shared/protocol");
const { frameSpec } = require("devtools/shared/specs/frame");

View File

@ -9,6 +9,7 @@ DIRS += [
'emulation',
'highlighters',
'inspector',
'object',
'utils',
'webconsole',
'worker',
@ -21,6 +22,7 @@ DevToolsModules(
'addon.js',
'addons.js',
'animation.js',
'array-buffer.js',
'breakpoint.js',
'call-watcher.js',
'canvas.js',

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,115 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { DebuggerServer } = require("devtools/server/main");
/**
* Creates an actor for the specified "very long" string. "Very long" is specified
* at the server's discretion.
* There is a newer, protocol.js based LongString actor in
* devtools/server/actors/string.js and if you can you should use this one.
*
* @param string String
* The string.
*/
function LongStringActor(string) {
this.string = string;
this.stringLength = string.length;
}
LongStringActor.prototype = {
actorPrefix: "longString",
rawValue: function() {
return this.string;
},
destroy: function() {
// Because longStringActors is not a weak map, we won't automatically leave
// it so we need to manually leave on destroy so that we don't leak
// memory.
this._releaseActor();
},
/**
* Returns a grip for this actor for returning in a protocol message.
*/
grip: function() {
return {
"type": "longString",
"initial": this.string.substring(
0, DebuggerServer.LONG_STRING_INITIAL_LENGTH),
"length": this.stringLength,
"actor": this.actorID
};
},
/**
* Handle a request to extract part of this actor's string.
*
* @param request object
* The protocol request object.
*/
onSubstring: function(request) {
return {
"from": this.actorID,
"substring": this.string.substring(request.start, request.end)
};
},
/**
* Handle a request to release this LongStringActor instance.
*/
onRelease: function() {
// TODO: also check if registeredPool === threadActor.threadLifetimePool
// when the web console moves away from manually releasing pause-scoped
// actors.
this._releaseActor();
this.registeredPool.removeActor(this);
return {};
},
_releaseActor: function() {
if (this.registeredPool && this.registeredPool.longStringActors) {
delete this.registeredPool.longStringActors[this.string];
}
}
};
LongStringActor.prototype.requestTypes = {
"substring": LongStringActor.prototype.onSubstring,
"release": LongStringActor.prototype.onRelease
};
/**
* Create a grip for the given string.
*
* @param str String
* The string we are creating a grip for.
* @param pool ActorPool
* The actor pool where the new actor will be added.
*/
function longStringGrip(str, pool) {
if (!pool.longStringActors) {
pool.longStringActors = {};
}
if (pool.longStringActors.hasOwnProperty(str)) {
return pool.longStringActors[str].grip();
}
let actor = new LongStringActor(str);
pool.addActor(actor);
pool.longStringActors[str] = actor;
return actor.grip();
}
module.exports = {
LongStringActor,
longStringGrip,
};

View File

@ -0,0 +1,13 @@
# 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/.
DevToolsModules(
'long-string.js',
'previewers.js',
'property-iterator.js',
'stringifiers.js',
'symbol-iterator.js',
'symbol.js',
'utils.js',
)

View File

@ -0,0 +1,819 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Cu, Ci } = require("chrome");
const { DebuggerServer } = require("devtools/server/main");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "ObjectUtils", "devtools/server/actors/object/utils");
loader.lazyRequireGetter(this, "PropertyIterators", "devtools/server/actors/object/property-iterator");
// Number of items to preview in objects, arrays, maps, sets, lists,
// collections, etc.
const OBJECT_PREVIEW_MAX_ITEMS = 10;
/**
* Functions for adding information to ObjectActor grips for the purpose of
* having customized output. This object holds arrays mapped by
* Debugger.Object.prototype.class.
*
* In each array you can add functions that take three
* arguments:
* - the ObjectActor instance and its hooks to make a preview for,
* - the grip object being prepared for the client,
* - the raw JS object after calling Debugger.Object.unsafeDereference(). This
* argument is only provided if the object is safe for reading properties and
* executing methods. See DevToolsUtils.isSafeJSObject().
*
* Functions must return false if they cannot provide preview
* information for the debugger object, or true otherwise.
*/
const previewers = {
String: [function(objectActor, grip, rawObj) {
return wrappedPrimitivePreviewer("String", String, objectActor, grip, rawObj);
}],
Boolean: [function(objectActor, grip, rawObj) {
return wrappedPrimitivePreviewer("Boolean", Boolean, objectActor, grip, rawObj);
}],
Number: [function(objectActor, grip, rawObj) {
return wrappedPrimitivePreviewer("Number", Number, objectActor, grip, rawObj);
}],
Symbol: [function(objectActor, grip, rawObj) {
return wrappedPrimitivePreviewer("Symbol", Symbol, objectActor, grip, rawObj);
}],
Function: [function({obj, hooks}, grip) {
if (obj.name) {
grip.name = obj.name;
}
if (obj.displayName) {
grip.displayName = obj.displayName.substr(0, 500);
}
if (obj.parameterNames) {
grip.parameterNames = obj.parameterNames;
}
// Check if the developer has added a de-facto standard displayName
// property for us to use.
let userDisplayName;
try {
userDisplayName = obj.getOwnPropertyDescriptor("displayName");
} catch (e) {
// The above can throw "permission denied" errors when the debuggee
// does not subsume the function's compartment.
}
if (userDisplayName && typeof userDisplayName.value == "string" &&
userDisplayName.value) {
grip.userDisplayName = hooks.createValueGrip(userDisplayName.value);
}
let dbgGlobal = hooks.getGlobalDebugObject();
if (dbgGlobal) {
let script = dbgGlobal.makeDebuggeeValue(obj.unsafeDereference()).script;
if (script) {
grip.location = {
url: script.url,
line: script.startLine
};
}
}
return true;
}],
RegExp: [function({obj, hooks}, grip) {
let str = DevToolsUtils.callPropertyOnObject(obj, "toString");
if (typeof str != "string") {
return false;
}
grip.displayString = hooks.createValueGrip(str);
return true;
}],
Date: [function({obj, hooks}, grip) {
let time = DevToolsUtils.callPropertyOnObject(obj, "getTime");
if (typeof time != "number") {
return false;
}
grip.preview = {
timestamp: hooks.createValueGrip(time),
};
return true;
}],
Array: [function({obj, hooks}, grip) {
let length = ObjectUtils.getArrayLength(obj);
grip.preview = {
kind: "ArrayLike",
length: length,
};
if (hooks.getGripDepth() > 1) {
return true;
}
let raw = obj.unsafeDereference();
let items = grip.preview.items = [];
for (let i = 0; i < length; ++i) {
// Array Xrays filter out various possibly-unsafe properties (like
// functions, and claim that the value is undefined instead. This
// is generally the right thing for privileged code accessing untrusted
// objects, but quite confusing for Object previews. So we manually
// override this protection by waiving Xrays on the array, and re-applying
// Xrays on any indexed value props that we pull off of it.
let desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(raw), i);
if (desc && !desc.get && !desc.set) {
let value = Cu.unwaiveXrays(desc.value);
value = ObjectUtils.makeDebuggeeValueIfNeeded(obj, value);
items.push(hooks.createValueGrip(value));
} else {
items.push(null);
}
if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
return true;
}],
Set: [function(objectActor, grip) {
let size = DevToolsUtils.getProperty(objectActor.obj, "size");
if (typeof size != "number") {
return false;
}
grip.preview = {
kind: "ArrayLike",
length: size,
};
// Avoid recursive object grips.
if (objectActor.hooks.getGripDepth() > 1) {
return true;
}
let items = grip.preview.items = [];
for (let item of PropertyIterators.enumSetEntries(objectActor)) {
items.push(item);
if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
return true;
}],
WeakSet: [function(objectActor, grip) {
let enumEntries = PropertyIterators.enumWeakSetEntries(objectActor);
grip.preview = {
kind: "ArrayLike",
length: enumEntries.size
};
// Avoid recursive object grips.
if (objectActor.hooks.getGripDepth() > 1) {
return true;
}
let items = grip.preview.items = [];
for (let item of enumEntries) {
items.push(item);
if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
return true;
}],
Map: [function(objectActor, grip) {
let size = DevToolsUtils.getProperty(objectActor.obj, "size");
if (typeof size != "number") {
return false;
}
grip.preview = {
kind: "MapLike",
size: size,
};
if (objectActor.hooks.getGripDepth() > 1) {
return true;
}
let entries = grip.preview.entries = [];
for (let entry of PropertyIterators.enumMapEntries(objectActor)) {
entries.push(entry);
if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
return true;
}],
WeakMap: [function(objectActor, grip) {
let enumEntries = PropertyIterators.enumWeakMapEntries(objectActor);
grip.preview = {
kind: "MapLike",
size: enumEntries.size
};
if (objectActor.hooks.getGripDepth() > 1) {
return true;
}
let entries = grip.preview.entries = [];
for (let entry of enumEntries) {
entries.push(entry);
if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
return true;
}],
DOMStringMap: [function({obj, hooks}, grip, rawObj) {
if (!rawObj) {
return false;
}
let keys = obj.getOwnPropertyNames();
grip.preview = {
kind: "MapLike",
size: keys.length,
};
if (hooks.getGripDepth() > 1) {
return true;
}
let entries = grip.preview.entries = [];
for (let key of keys) {
let value = ObjectUtils.makeDebuggeeValueIfNeeded(obj, rawObj[key]);
entries.push([key, hooks.createValueGrip(value)]);
if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
return true;
}],
Proxy: [function({obj, hooks}, grip, rawObj) {
// The `isProxy` getter of the debuggee object only detects proxies without
// security wrappers. If false, the target and handler are not available.
let hasTargetAndHandler = obj.isProxy;
if (hasTargetAndHandler) {
grip.proxyTarget = hooks.createValueGrip(obj.proxyTarget);
grip.proxyHandler = hooks.createValueGrip(obj.proxyHandler);
}
grip.preview = {
kind: "Object",
ownProperties: Object.create(null),
ownPropertiesLength: 2 * hasTargetAndHandler
};
if (hooks.getGripDepth() > 1) {
return true;
}
if (hasTargetAndHandler) {
grip.preview.ownProperties["<target>"] = {value: grip.proxyTarget};
grip.preview.ownProperties["<handler>"] = {value: grip.proxyHandler};
}
return true;
}],
};
/**
* Generic previewer for classes wrapping primitives, like String,
* Number and Boolean.
*
* @param string className
* Class name to expect.
* @param object classObj
* The class to expect, eg. String. The valueOf() method of the class is
* invoked on the given object.
* @param ObjectActor objectActor
* The object actor
* @param Object grip
* The result grip to fill in
* @return Booolean true if the object was handled, false otherwise
*/
function wrappedPrimitivePreviewer(className, classObj, objectActor, grip, rawObj) {
let {obj, hooks} = objectActor;
let v = null;
try {
v = classObj.prototype.valueOf.call(rawObj);
} catch (ex) {
// valueOf() can throw if the raw JS object is "misbehaved".
return false;
}
if (v === null) {
return false;
}
let canHandle = GenericObject(objectActor, grip, rawObj, className === "String");
if (!canHandle) {
return false;
}
grip.preview.wrappedValue =
hooks.createValueGrip(ObjectUtils.makeDebuggeeValueIfNeeded(obj, v));
return true;
}
function GenericObject(objectActor, grip, rawObj, specialStringBehavior = false) {
let {obj, hooks} = objectActor;
if (grip.preview || grip.displayString || hooks.getGripDepth() > 1) {
return false;
}
let i = 0, names = [], symbols = [];
let preview = grip.preview = {
kind: "Object",
ownProperties: Object.create(null),
ownSymbols: [],
};
try {
names = obj.getOwnPropertyNames();
symbols = obj.getOwnPropertySymbols();
} catch (ex) {
// Calling getOwnPropertyNames() on some wrapped native prototypes is not
// allowed: "cannot modify properties of a WrappedNative". See bug 952093.
}
preview.ownPropertiesLength = names.length;
preview.ownSymbolsLength = symbols.length;
let length;
if (specialStringBehavior) {
length = DevToolsUtils.getProperty(obj, "length");
if (typeof length != "number") {
specialStringBehavior = false;
}
}
for (let name of names) {
if (specialStringBehavior && /^[0-9]+$/.test(name)) {
let num = parseInt(name, 10);
if (num.toString() === name && num >= 0 && num < length) {
continue;
}
}
let desc = objectActor._propertyDescriptor(name, true);
if (!desc) {
continue;
}
preview.ownProperties[name] = desc;
if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
for (let symbol of symbols) {
let descriptor = objectActor._propertyDescriptor(symbol, true);
if (!descriptor) {
continue;
}
preview.ownSymbols.push(Object.assign({
descriptor
}, hooks.createValueGrip(symbol)));
if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
if (i < OBJECT_PREVIEW_MAX_ITEMS) {
preview.safeGetterValues = objectActor._findSafeGetterValues(
Object.keys(preview.ownProperties),
OBJECT_PREVIEW_MAX_ITEMS - i);
}
return true;
}
// Preview functions that do not rely on the object class.
previewers.Object = [
function TypedArray({obj, hooks}, grip) {
if (!ObjectUtils.isTypedArray(obj)) {
return false;
}
grip.preview = {
kind: "ArrayLike",
length: ObjectUtils.getArrayLength(obj),
};
if (hooks.getGripDepth() > 1) {
return true;
}
let raw = obj.unsafeDereference();
let global = Cu.getGlobalForObject(DebuggerServer);
let classProto = global[obj.class].prototype;
// The Xray machinery for TypedArrays denies indexed access on the grounds
// that it's slow, and advises callers to do a structured clone instead.
let safeView = Cu.cloneInto(classProto.subarray.call(raw, 0,
OBJECT_PREVIEW_MAX_ITEMS), global);
let items = grip.preview.items = [];
for (let i = 0; i < safeView.length; i++) {
items.push(safeView[i]);
}
return true;
},
function Error({obj, hooks}, grip) {
switch (obj.class) {
case "Error":
case "EvalError":
case "RangeError":
case "ReferenceError":
case "SyntaxError":
case "TypeError":
case "URIError":
let name = DevToolsUtils.getProperty(obj, "name");
let msg = DevToolsUtils.getProperty(obj, "message");
let stack = DevToolsUtils.getProperty(obj, "stack");
let fileName = DevToolsUtils.getProperty(obj, "fileName");
let lineNumber = DevToolsUtils.getProperty(obj, "lineNumber");
let columnNumber = DevToolsUtils.getProperty(obj, "columnNumber");
grip.preview = {
kind: "Error",
name: hooks.createValueGrip(name),
message: hooks.createValueGrip(msg),
stack: hooks.createValueGrip(stack),
fileName: hooks.createValueGrip(fileName),
lineNumber: hooks.createValueGrip(lineNumber),
columnNumber: hooks.createValueGrip(columnNumber),
};
return true;
default:
return false;
}
},
function CSSMediaRule({obj, hooks}, grip, rawObj) {
if (isWorker || !rawObj || obj.class != "CSSMediaRule") {
return false;
}
grip.preview = {
kind: "ObjectWithText",
text: hooks.createValueGrip(rawObj.conditionText),
};
return true;
},
function CSSStyleRule({obj, hooks}, grip, rawObj) {
if (isWorker || !rawObj || obj.class != "CSSStyleRule") {
return false;
}
grip.preview = {
kind: "ObjectWithText",
text: hooks.createValueGrip(rawObj.selectorText),
};
return true;
},
function ObjectWithURL({obj, hooks}, grip, rawObj) {
if (isWorker || !rawObj || !(obj.class == "CSSImportRule" ||
obj.class == "CSSStyleSheet" ||
obj.class == "Location" ||
rawObj instanceof Ci.nsIDOMWindow)) {
return false;
}
let url;
if (rawObj instanceof Ci.nsIDOMWindow && rawObj.location) {
url = rawObj.location.href;
} else if (rawObj.href) {
url = rawObj.href;
} else {
return false;
}
grip.preview = {
kind: "ObjectWithURL",
url: hooks.createValueGrip(url),
};
return true;
},
function ArrayLike({obj, hooks}, grip, rawObj) {
if (isWorker || !rawObj ||
obj.class != "DOMStringList" &&
obj.class != "DOMTokenList" &&
obj.class != "CSSRuleList" &&
obj.class != "MediaList" &&
obj.class != "StyleSheetList" &&
obj.class != "CSSValueList" &&
obj.class != "NamedNodeMap" &&
obj.class != "FileList" &&
obj.class != "NodeList") {
return false;
}
if (typeof rawObj.length != "number") {
return false;
}
grip.preview = {
kind: "ArrayLike",
length: rawObj.length,
};
if (hooks.getGripDepth() > 1) {
return true;
}
let items = grip.preview.items = [];
for (let i = 0; i < rawObj.length &&
items.length < OBJECT_PREVIEW_MAX_ITEMS; i++) {
let value = ObjectUtils.makeDebuggeeValueIfNeeded(obj, rawObj[i]);
items.push(hooks.createValueGrip(value));
}
return true;
},
function CSSStyleDeclaration({obj, hooks}, grip, rawObj) {
if (isWorker || !rawObj ||
(obj.class != "CSSStyleDeclaration" &&
obj.class != "CSS2Properties")) {
return false;
}
grip.preview = {
kind: "MapLike",
size: rawObj.length,
};
let entries = grip.preview.entries = [];
for (let i = 0; i < OBJECT_PREVIEW_MAX_ITEMS &&
i < rawObj.length; i++) {
let prop = rawObj[i];
let value = rawObj.getPropertyValue(prop);
entries.push([prop, hooks.createValueGrip(value)]);
}
return true;
},
function DOMNode({obj, hooks}, grip, rawObj) {
if (isWorker || obj.class == "Object" || !rawObj ||
!(rawObj instanceof Ci.nsIDOMNode)) {
return false;
}
let preview = grip.preview = {
kind: "DOMNode",
nodeType: rawObj.nodeType,
nodeName: rawObj.nodeName,
isConnected: rawObj.isConnected === true,
};
if (rawObj instanceof Ci.nsIDOMDocument && rawObj.location) {
preview.location = hooks.createValueGrip(rawObj.location.href);
} else if (rawObj instanceof Ci.nsIDOMDocumentFragment) {
preview.childNodesLength = rawObj.childNodes.length;
if (hooks.getGripDepth() < 2) {
preview.childNodes = [];
for (let node of rawObj.childNodes) {
let actor = hooks.createValueGrip(obj.makeDebuggeeValue(node));
preview.childNodes.push(actor);
if (preview.childNodes.length == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
}
} else if (rawObj instanceof Ci.nsIDOMElement) {
// For HTML elements (in an HTML document, at least), the nodeName is an
// uppercased version of the actual element name. Check for HTML
// elements, that is elements in the HTML namespace, and lowercase the
// nodeName in that case.
if (rawObj.namespaceURI == "http://www.w3.org/1999/xhtml") {
preview.nodeName = preview.nodeName.toLowerCase();
}
// Add preview for DOM element attributes.
preview.attributes = {};
preview.attributesLength = rawObj.attributes.length;
for (let attr of rawObj.attributes) {
preview.attributes[attr.nodeName] = hooks.createValueGrip(attr.value);
}
} else if (obj.class == "Attr") {
preview.value = hooks.createValueGrip(rawObj.value);
} else if (obj.class == "Text" ||
obj.class == "CDATASection" ||
obj.class == "Comment") {
preview.textContent = hooks.createValueGrip(rawObj.textContent);
}
return true;
},
function DOMEvent({obj, hooks}, grip, rawObj) {
if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMEvent)) {
return false;
}
let preview = grip.preview = {
kind: "DOMEvent",
type: rawObj.type,
properties: Object.create(null),
};
if (hooks.getGripDepth() < 2) {
let target = obj.makeDebuggeeValue(rawObj.target);
preview.target = hooks.createValueGrip(target);
}
let props = [];
if (obj.class == "MouseEvent" ||
obj.class == "DragEvent" ||
obj.class == "PointerEvent" ||
obj.class == "SimpleGestureEvent" ||
obj.class == "WheelEvent") {
props.push("buttons", "clientX", "clientY", "layerX", "layerY");
} else if (obj.class == "KeyboardEvent") {
let modifiers = [];
if (rawObj.altKey) {
modifiers.push("Alt");
}
if (rawObj.ctrlKey) {
modifiers.push("Control");
}
if (rawObj.metaKey) {
modifiers.push("Meta");
}
if (rawObj.shiftKey) {
modifiers.push("Shift");
}
preview.eventKind = "key";
preview.modifiers = modifiers;
props.push("key", "charCode", "keyCode");
} else if (obj.class == "TransitionEvent") {
props.push("propertyName", "pseudoElement");
} else if (obj.class == "AnimationEvent") {
props.push("animationName", "pseudoElement");
} else if (obj.class == "ClipboardEvent") {
props.push("clipboardData");
}
// Add event-specific properties.
for (let prop of props) {
let value = rawObj[prop];
if (value && (typeof value == "object" || typeof value == "function")) {
// Skip properties pointing to objects.
if (hooks.getGripDepth() > 1) {
continue;
}
value = obj.makeDebuggeeValue(value);
}
preview.properties[prop] = hooks.createValueGrip(value);
}
// Add any properties we find on the event object.
if (!props.length) {
let i = 0;
for (let prop in rawObj) {
let value = rawObj[prop];
if (prop == "target" || prop == "type" || value === null ||
typeof value == "function") {
continue;
}
if (value && typeof value == "object") {
if (hooks.getGripDepth() > 1) {
continue;
}
value = obj.makeDebuggeeValue(value);
}
preview.properties[prop] = hooks.createValueGrip(value);
if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
}
return true;
},
function DOMException({obj, hooks}, grip, rawObj) {
if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMDOMException)) {
return false;
}
grip.preview = {
kind: "DOMException",
name: hooks.createValueGrip(rawObj.name),
message: hooks.createValueGrip(rawObj.message),
code: hooks.createValueGrip(rawObj.code),
result: hooks.createValueGrip(rawObj.result),
filename: hooks.createValueGrip(rawObj.filename),
lineNumber: hooks.createValueGrip(rawObj.lineNumber),
columnNumber: hooks.createValueGrip(rawObj.columnNumber),
};
return true;
},
function PseudoArray({obj, hooks}, grip, rawObj) {
// An object is considered a pseudo-array if all the following apply:
// - All its properties are array indices except, optionally, a "length" property.
// - At least it has the "0" array index.
// - The array indices are consecutive.
// - The value of "length", if present, is the number of array indices.
let keys;
try {
keys = obj.getOwnPropertyNames();
} catch (err) {
// The above can throw when the debuggee does not subsume the object's
// compartment, or for some WrappedNatives like Cu.Sandbox.
return false;
}
let {length} = keys;
if (length === 0) {
return false;
}
// Array indices should be sorted at the beginning, from smallest to largest.
// Other properties should be at the end, so check if the last one is "length".
if (keys[length - 1] === "length") {
--length;
if (length === 0 || length !== DevToolsUtils.getProperty(obj, "length")) {
return false;
}
}
// Check that the last key is the array index expected at that position.
let lastKey = keys[length - 1];
if (!ObjectUtils.isArrayIndex(lastKey) || +lastKey !== length - 1) {
return false;
}
grip.preview = {
kind: "ArrayLike",
length: length,
};
// Avoid recursive object grips.
if (hooks.getGripDepth() > 1) {
return true;
}
let items = grip.preview.items = [];
let numItems = Math.min(OBJECT_PREVIEW_MAX_ITEMS, length);
for (let i = 0; i < numItems; ++i) {
let desc = obj.getOwnPropertyDescriptor(i);
if (desc && "value" in desc) {
items.push(hooks.createValueGrip(desc.value));
} else {
items.push(null);
}
}
return true;
},
function Object(objectActor, grip, rawObj) {
return GenericObject(objectActor, grip, rawObj, /* specialStringBehavior = */ false);
},
];
module.exports = previewers;

View File

@ -0,0 +1,417 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Cu } = require("chrome");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "ChromeUtils");
loader.lazyRequireGetter(this, "ObjectUtils", "devtools/server/actors/object/utils");
/**
* Creates an actor to iterate over an object's property names and values.
*
* @param objectActor ObjectActor
* The object actor.
* @param options Object
* A dictionary object with various boolean attributes:
* - enumEntries Boolean
* If true, enumerates the entries of a Map or Set object
* instead of enumerating properties.
* - ignoreIndexedProperties Boolean
* If true, filters out Array items.
* e.g. properties names between `0` and `object.length`.
* - ignoreNonIndexedProperties Boolean
* If true, filters out items that aren't array items
* e.g. properties names that are not a number between `0`
* and `object.length`.
* - sort Boolean
* If true, the iterator will sort the properties by name
* before dispatching them.
* - query String
* If non-empty, will filter the properties by names and values
* containing this query string. The match is not case-sensitive.
* Regarding value filtering it just compare to the stringification
* of the property value.
*/
function PropertyIteratorActor(objectActor, options) {
if (!DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
this.iterator = {
size: 0,
propertyName: index => undefined,
propertyDescription: index => undefined,
};
} else if (options.enumEntries) {
let cls = objectActor.obj.class;
if (cls == "Map") {
this.iterator = enumMapEntries(objectActor);
} else if (cls == "WeakMap") {
this.iterator = enumWeakMapEntries(objectActor);
} else if (cls == "Set") {
this.iterator = enumSetEntries(objectActor);
} else if (cls == "WeakSet") {
this.iterator = enumWeakSetEntries(objectActor);
} else {
throw new Error("Unsupported class to enumerate entries from: " + cls);
}
} else if (
ObjectUtils.isArray(objectActor.obj)
&& options.ignoreNonIndexedProperties
&& !options.query
) {
this.iterator = enumArrayProperties(objectActor, options);
} else {
this.iterator = enumObjectProperties(objectActor, options);
}
}
PropertyIteratorActor.prototype = {
actorPrefix: "propertyIterator",
grip() {
return {
type: this.actorPrefix,
actor: this.actorID,
count: this.iterator.size
};
},
names({ indexes }) {
let list = [];
for (let idx of indexes) {
list.push(this.iterator.propertyName(idx));
}
return {
names: indexes
};
},
slice({ start, count }) {
let ownProperties = Object.create(null);
for (let i = start, m = start + count; i < m; i++) {
let name = this.iterator.propertyName(i);
ownProperties[name] = this.iterator.propertyDescription(i);
}
return {
ownProperties
};
},
all() {
return this.slice({ start: 0, count: this.iterator.size });
}
};
PropertyIteratorActor.prototype.requestTypes = {
"names": PropertyIteratorActor.prototype.names,
"slice": PropertyIteratorActor.prototype.slice,
"all": PropertyIteratorActor.prototype.all,
};
/**
* Helper function to create a grip from a Map/Set entry
*/
function gripFromEntry({ obj, hooks }, entry) {
return hooks.createValueGrip(
ObjectUtils.makeDebuggeeValueIfNeeded(obj, Cu.unwaiveXrays(entry)));
}
function enumArrayProperties(objectActor, options) {
return {
size: ObjectUtils.getArrayLength(objectActor.obj),
propertyName(index) {
return index;
},
propertyDescription(index) {
return objectActor._propertyDescriptor(index);
}
};
}
function enumObjectProperties(objectActor, options) {
let names = [];
try {
names = objectActor.obj.getOwnPropertyNames();
} catch (ex) {
// Calling getOwnPropertyNames() on some wrapped native prototypes is not
// allowed: "cannot modify properties of a WrappedNative". See bug 952093.
}
if (options.ignoreNonIndexedProperties || options.ignoreIndexedProperties) {
let length = DevToolsUtils.getProperty(objectActor.obj, "length");
let sliceIndex;
const isLengthTrustworthy = isUint32(length)
&& (!length || ObjectUtils.isArrayIndex(names[length - 1]))
&& !ObjectUtils.isArrayIndex(names[length]);
if (!isLengthTrustworthy) {
// The length property may not reflect what the object looks like, let's find
// where indexed properties end.
if (!ObjectUtils.isArrayIndex(names[0])) {
// If the first item is not a number, this means there is no indexed properties
// in this object.
sliceIndex = 0;
} else {
sliceIndex = names.length;
while (sliceIndex > 0) {
if (ObjectUtils.isArrayIndex(names[sliceIndex - 1])) {
break;
}
sliceIndex--;
}
}
} else {
sliceIndex = length;
}
// It appears that getOwnPropertyNames always returns indexed properties
// first, so we can safely slice `names` for/against indexed properties.
// We do such clever operation to optimize very large array inspection,
// like webaudio buffers.
if (options.ignoreIndexedProperties) {
// Keep items after `sliceIndex` index
names = names.slice(sliceIndex);
} else if (options.ignoreNonIndexedProperties) {
// Keep `sliceIndex` first items
names.length = sliceIndex;
}
}
let safeGetterValues = objectActor._findSafeGetterValues(names, 0);
let safeGetterNames = Object.keys(safeGetterValues);
// Merge the safe getter values into the existing properties list.
for (let name of safeGetterNames) {
if (!names.includes(name)) {
names.push(name);
}
}
if (options.query) {
let { query } = options;
query = query.toLowerCase();
names = names.filter(name => {
// Filter on attribute names
if (name.toLowerCase().includes(query)) {
return true;
}
// and then on attribute values
let desc;
try {
desc = objectActor.obj.getOwnPropertyDescriptor(name);
} catch (e) {
// Calling getOwnPropertyDescriptor on wrapped native prototypes is not
// allowed (bug 560072).
}
if (desc && desc.value &&
String(desc.value).includes(query)) {
return true;
}
return false;
});
}
if (options.sort) {
names.sort();
}
return {
size: names.length,
propertyName(index) {
return names[index];
},
propertyDescription(index) {
let name = names[index];
let desc = objectActor._propertyDescriptor(name);
if (!desc) {
desc = safeGetterValues[name];
} else if (name in safeGetterValues) {
// Merge the safe getter values into the existing properties list.
let { getterValue, getterPrototypeLevel } = safeGetterValues[name];
desc.getterValue = getterValue;
desc.getterPrototypeLevel = getterPrototypeLevel;
}
return desc;
}
};
}
function enumMapEntries(objectActor) {
// Iterating over a Map via .entries goes through various intermediate
// objects - an Iterator object, then a 2-element Array object, then the
// actual values we care about. We don't have Xrays to Iterator objects,
// so we get Opaque wrappers for them. And even though we have Xrays to
// Arrays, the semantics often deny access to the entires based on the
// nature of the values. So we need waive Xrays for the iterator object
// and the tupes, and then re-apply them on the underlying values until
// we fix bug 1023984.
//
// Even then though, we might want to continue waiving Xrays here for the
// same reason we do so for Arrays above - this filtering behavior is likely
// to be more confusing than beneficial in the case of Object previews.
let raw = objectActor.obj.unsafeDereference();
let keys = [...Cu.waiveXrays(Map.prototype.keys.call(raw))];
return {
[Symbol.iterator]: function* () {
for (let key of keys) {
let value = Map.prototype.get.call(raw, key);
yield [ key, value ].map(val => gripFromEntry(objectActor, val));
}
},
size: keys.length,
propertyName(index) {
return index;
},
propertyDescription(index) {
let key = keys[index];
let val = Map.prototype.get.call(raw, key);
return {
enumerable: true,
value: {
type: "mapEntry",
preview: {
key: gripFromEntry(objectActor, key),
value: gripFromEntry(objectActor, val)
}
}
};
}
};
}
function enumWeakMapEntries(objectActor) {
// We currently lack XrayWrappers for WeakMap, so when we iterate over
// the values, the temporary iterator objects get created in the target
// compartment. However, we _do_ have Xrays to Object now, so we end up
// Xraying those temporary objects, and filtering access to |it.value|
// based on whether or not it's Xrayable and/or callable, which breaks
// the for/of iteration.
//
// This code is designed to handle untrusted objects, so we can safely
// waive Xrays on the iterable, and relying on the Debugger machinery to
// make sure we handle the resulting objects carefully.
let raw = objectActor.obj.unsafeDereference();
let keys = Cu.waiveXrays(
ChromeUtils.nondeterministicGetWeakMapKeys(raw));
return {
[Symbol.iterator]: function* () {
for (let key of keys) {
let value = WeakMap.prototype.get.call(raw, key);
yield [ key, value ].map(val => gripFromEntry(objectActor, val));
}
},
size: keys.length,
propertyName(index) {
return index;
},
propertyDescription(index) {
let key = keys[index];
let val = WeakMap.prototype.get.call(raw, key);
return {
enumerable: true,
value: {
type: "mapEntry",
preview: {
key: gripFromEntry(objectActor, key),
value: gripFromEntry(objectActor, val)
}
}
};
}
};
}
function enumSetEntries(objectActor) {
// We currently lack XrayWrappers for Set, so when we iterate over
// the values, the temporary iterator objects get created in the target
// compartment. However, we _do_ have Xrays to Object now, so we end up
// Xraying those temporary objects, and filtering access to |it.value|
// based on whether or not it's Xrayable and/or callable, which breaks
// the for/of iteration.
//
// This code is designed to handle untrusted objects, so we can safely
// waive Xrays on the iterable, and relying on the Debugger machinery to
// make sure we handle the resulting objects carefully.
let raw = objectActor.obj.unsafeDereference();
let values = [...Cu.waiveXrays(Set.prototype.values.call(raw))];
return {
[Symbol.iterator]: function* () {
for (let item of values) {
yield gripFromEntry(objectActor, item);
}
},
size: values.length,
propertyName(index) {
return index;
},
propertyDescription(index) {
let val = values[index];
return {
enumerable: true,
value: gripFromEntry(objectActor, val)
};
}
};
}
function enumWeakSetEntries(objectActor) {
// We currently lack XrayWrappers for WeakSet, so when we iterate over
// the values, the temporary iterator objects get created in the target
// compartment. However, we _do_ have Xrays to Object now, so we end up
// Xraying those temporary objects, and filtering access to |it.value|
// based on whether or not it's Xrayable and/or callable, which breaks
// the for/of iteration.
//
// This code is designed to handle untrusted objects, so we can safely
// waive Xrays on the iterable, and relying on the Debugger machinery to
// make sure we handle the resulting objects carefully.
let raw = objectActor.obj.unsafeDereference();
let keys = Cu.waiveXrays(
ChromeUtils.nondeterministicGetWeakSetKeys(raw));
return {
[Symbol.iterator]: function* () {
for (let item of keys) {
yield gripFromEntry(objectActor, item);
}
},
size: keys.length,
propertyName(index) {
return index;
},
propertyDescription(index) {
let val = keys[index];
return {
enumerable: true,
value: gripFromEntry(objectActor, val)
};
}
};
}
/**
* Returns true if the parameter can be stored as a 32-bit unsigned integer.
* If so, it will be suitable for use as the length of an array object.
*
* @param num Number
* The number to test.
* @return Boolean
*/
function isUint32(num) {
return num >>> 0 === num;
}
module.exports = {
PropertyIteratorActor,
enumMapEntries,
enumSetEntries,
enumWeakMapEntries,
enumWeakSetEntries,
};

View File

@ -0,0 +1,187 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "ObjectUtils", "devtools/server/actors/object/utils");
/**
* Stringify a Debugger.Object based on its class.
*
* @param Debugger.Object obj
* The object to stringify.
* @return String
* The stringification for the object.
*/
function stringify(obj) {
if (!DevToolsUtils.isSafeDebuggerObject(obj)) {
if (DevToolsUtils.isCPOW(obj)) {
return "<cpow>";
}
let unwrapped = DevToolsUtils.unwrap(obj);
if (unwrapped === undefined) {
return "<invisibleToDebugger>";
} else if (unwrapped.isProxy) {
return "<proxy>";
}
// The following line should not be reached. It's there just in case somebody
// modifies isSafeDebuggerObject to return false for additional kinds of objects.
return "[object " + obj.class + "]";
} else if (obj.class == "DeadObject") {
return "<dead object>";
}
const stringifier = stringifiers[obj.class] || stringifiers.Object;
try {
return stringifier(obj);
} catch (e) {
DevToolsUtils.reportException("stringify", e);
return "<failed to stringify object>";
}
}
/**
* Create a function that can safely stringify Debugger.Objects of a given
* builtin type.
*
* @param Function ctor
* The builtin class constructor.
* @return Function
* The stringifier for the class.
*/
function createBuiltinStringifier(ctor) {
return obj => {
try {
return ctor.prototype.toString.call(obj.unsafeDereference());
} catch (err) {
// The debuggee will see a "Function" class if the object is callable and
// its compartment is not subsumed. The above will throw if it's not really
// a function, e.g. if it's a callable proxy.
return "[object " + obj.class + "]";
}
};
}
/**
* Stringify a Debugger.Object-wrapped Error instance.
*
* @param Debugger.Object obj
* The object to stringify.
* @return String
* The stringification of the object.
*/
function errorStringify(obj) {
let name = DevToolsUtils.getProperty(obj, "name");
if (name === "" || name === undefined) {
name = obj.class;
} else if (isObject(name)) {
name = stringify(name);
}
let message = DevToolsUtils.getProperty(obj, "message");
if (isObject(message)) {
message = stringify(message);
}
if (message === "" || message === undefined) {
return name;
}
return name + ": " + message;
}
// Used to prevent infinite recursion when an array is found inside itself.
var seen = null;
var stringifiers = {
Error: errorStringify,
EvalError: errorStringify,
RangeError: errorStringify,
ReferenceError: errorStringify,
SyntaxError: errorStringify,
TypeError: errorStringify,
URIError: errorStringify,
Boolean: createBuiltinStringifier(Boolean),
Function: createBuiltinStringifier(Function),
Number: createBuiltinStringifier(Number),
RegExp: createBuiltinStringifier(RegExp),
String: createBuiltinStringifier(String),
Object: obj => "[object " + obj.class + "]",
Array: obj => {
// If we're at the top level then we need to create the Set for tracking
// previously stringified arrays.
const topLevel = !seen;
if (topLevel) {
seen = new Set();
} else if (seen.has(obj)) {
return "";
}
seen.add(obj);
const len = ObjectUtils.getArrayLength(obj);
let string = "";
// Array.length is always a non-negative safe integer.
for (let i = 0; i < len; i++) {
const desc = obj.getOwnPropertyDescriptor(i);
if (desc) {
const { value } = desc;
if (value != null) {
string += isObject(value) ? stringify(value) : value;
}
}
if (i < len - 1) {
string += ",";
}
}
if (topLevel) {
seen = null;
}
return string;
},
DOMException: obj => {
const message = DevToolsUtils.getProperty(obj, "message") || "<no message>";
const result = (+DevToolsUtils.getProperty(obj, "result")).toString(16);
const code = DevToolsUtils.getProperty(obj, "code");
const name = DevToolsUtils.getProperty(obj, "name") || "<unknown>";
return '[Exception... "' + message + '" ' +
'code: "' + code + '" ' +
'nsresult: "0x' + result + " (" + name + ')"]';
},
Promise: obj => {
const { state, value, reason } = ObjectUtils.getPromiseState(obj);
let statePreview = state;
if (state != "pending") {
const settledValue = state === "fulfilled" ? value : reason;
statePreview += ": " + (typeof settledValue === "object" && settledValue !== null
? stringify(settledValue)
: settledValue);
}
return "Promise (" + statePreview + ")";
},
};
/**
* Determine if a given value is non-primitive.
*
* @param Any value
* The value to test.
* @return Boolean
* Whether the value is non-primitive.
*/
function isObject(value) {
const type = typeof value;
return type == "object" ? value !== null : type == "function";
}
module.exports = stringify;

View File

@ -0,0 +1,70 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
/**
* Creates an actor to iterate over an object's symbols.
*
* @param objectActor ObjectActor
* The object actor.
*/
function SymbolIteratorActor(objectActor) {
let symbols = [];
if (DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
try {
symbols = objectActor.obj.getOwnPropertySymbols();
} catch (err) {
// The above can throw when the debuggee does not subsume the object's
// compartment, or for some WrappedNatives like Cu.Sandbox.
}
}
this.iterator = {
size: symbols.length,
symbolDescription(index) {
const symbol = symbols[index];
return {
name: symbol.toString(),
descriptor: objectActor._propertyDescriptor(symbol)
};
}
};
}
SymbolIteratorActor.prototype = {
actorPrefix: "symbolIterator",
grip() {
return {
type: this.actorPrefix,
actor: this.actorID,
count: this.iterator.size
};
},
slice({ start, count }) {
let ownSymbols = [];
for (let i = start, m = start + count; i < m; i++) {
ownSymbols.push(this.iterator.symbolDescription(i));
}
return {
ownSymbols
};
},
all() {
return this.slice({ start: 0, count: this.iterator.size });
}
};
SymbolIteratorActor.prototype.requestTypes = {
"slice": SymbolIteratorActor.prototype.slice,
"all": SymbolIteratorActor.prototype.all,
};
exports.SymbolIteratorActor = SymbolIteratorActor;

View File

@ -0,0 +1,107 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
loader.lazyRequireGetter(this, "createValueGrip", "devtools/server/actors/object/utils", true);
/**
* Creates an actor for the specified symbol.
*
* @param symbol Symbol
* The symbol.
*/
function SymbolActor(symbol) {
this.symbol = symbol;
}
SymbolActor.prototype = {
actorPrefix: "symbol",
rawValue: function() {
return this.symbol;
},
destroy: function() {
// Because symbolActors is not a weak map, we won't automatically leave
// it so we need to manually leave on destroy so that we don't leak
// memory.
this._releaseActor();
},
/**
* Returns a grip for this actor for returning in a protocol message.
*/
grip: function() {
let form = {
type: "symbol",
actor: this.actorID,
};
let name = getSymbolName(this.symbol);
if (name !== undefined) {
// Create a grip for the name because it might be a longString.
form.name = createValueGrip(name, this.registeredPool);
}
return form;
},
/**
* Handle a request to release this SymbolActor instance.
*/
onRelease: function() {
// TODO: also check if registeredPool === threadActor.threadLifetimePool
// when the web console moves away from manually releasing pause-scoped
// actors.
this._releaseActor();
this.registeredPool.removeActor(this);
return {};
},
_releaseActor: function() {
if (this.registeredPool && this.registeredPool.symbolActors) {
delete this.registeredPool.symbolActors[this.symbol];
}
}
};
SymbolActor.prototype.requestTypes = {
"release": SymbolActor.prototype.onRelease
};
const symbolProtoToString = Symbol.prototype.toString;
function getSymbolName(symbol) {
const name = symbolProtoToString.call(symbol).slice("Symbol(".length, -1);
return name || undefined;
}
/**
* Create a grip for the given symbol.
*
* @param sym Symbol
* The symbol we are creating a grip for.
* @param pool ActorPool
* The actor pool where the new actor will be added.
*/
function symbolGrip(sym, pool) {
if (!pool.symbolActors) {
pool.symbolActors = Object.create(null);
}
if (sym in pool.symbolActors) {
return pool.symbolActors[sym].grip();
}
let actor = new SymbolActor(sym);
pool.addActor(actor);
pool.symbolActors[sym] = actor;
return actor.grip();
}
module.exports = {
SymbolActor,
symbolGrip,
};

View File

@ -0,0 +1,203 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { DebuggerServer } = require("devtools/server/main");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { assert } = DevToolsUtils;
loader.lazyRequireGetter(this, "longStringGrip", "devtools/server/actors/object/long-string", true);
loader.lazyRequireGetter(this, "symbolGrip", "devtools/server/actors/object/symbol", true);
/**
* Get thisDebugger.Object referent's `promiseState`.
*
* @returns Object
* An object of one of the following forms:
* - { state: "pending" }
* - { state: "fulfilled", value }
* - { state: "rejected", reason }
*/
function getPromiseState(obj) {
if (obj.class != "Promise") {
throw new Error(
"Can't call `getPromiseState` on `Debugger.Object`s that don't " +
"refer to Promise objects.");
}
let state = { state: obj.promiseState };
if (state.state === "fulfilled") {
state.value = obj.promiseValue;
} else if (state.state === "rejected") {
state.reason = obj.promiseReason;
}
return state;
}
/**
* Make a debuggee value for the given object, if needed. Primitive values
* are left the same.
*
* Use case: you have a raw JS object (after unsafe dereference) and you want to
* send it to the client. In that case you need to use an ObjectActor which
* requires a debuggee value. The Debugger.Object.prototype.makeDebuggeeValue()
* method works only for JS objects and functions.
*
* @param Debugger.Object obj
* @param any value
* @return object
*/
function makeDebuggeeValueIfNeeded(obj, value) {
if (value && (typeof value == "object" || typeof value == "function")) {
return obj.makeDebuggeeValue(value);
}
return value;
}
/**
* Create a grip for the given debuggee value. If the value is an
* object, will create an actor with the given lifetime.
*/
function createValueGrip(value, pool, makeObjectGrip) {
switch (typeof value) {
case "boolean":
return value;
case "string":
if (stringIsLong(value)) {
return longStringGrip(value, pool);
}
return value;
case "number":
if (value === Infinity) {
return { type: "Infinity" };
} else if (value === -Infinity) {
return { type: "-Infinity" };
} else if (Number.isNaN(value)) {
return { type: "NaN" };
} else if (!value && 1 / value === -Infinity) {
return { type: "-0" };
}
return value;
case "undefined":
return { type: "undefined" };
case "object":
if (value === null) {
return { type: "null" };
} else if (value.optimizedOut ||
value.uninitialized ||
value.missingArguments) {
// The slot is optimized out, an uninitialized binding, or
// arguments on a dead scope
return {
type: "null",
optimizedOut: value.optimizedOut,
uninitialized: value.uninitialized,
missingArguments: value.missingArguments
};
}
return makeObjectGrip(value, pool);
case "symbol":
return symbolGrip(value, pool);
default:
assert(false, "Failed to provide a grip for: " + value);
return null;
}
}
/**
* of passing the value directly over the protocol.
*
* @param str String
* The string we are checking the length of.
*/
function stringIsLong(str) {
return str.length >= DebuggerServer.LONG_STRING_LENGTH;
}
const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
"Uint32Array", "Int8Array", "Int16Array", "Int32Array",
"Float32Array", "Float64Array"];
/**
* Returns true if a debuggee object is a typed array.
*
* @param obj Debugger.Object
* The debuggee object to test.
* @return Boolean
*/
function isTypedArray(object) {
return TYPED_ARRAY_CLASSES.includes(object.class);
}
/**
* Returns true if a debuggee object is an array, including a typed array.
*
* @param obj Debugger.Object
* The debuggee object to test.
* @return Boolean
*/
function isArray(object) {
return isTypedArray(object) || object.class === "Array";
}
/**
* Returns the length of an array (or typed array).
*
* @param obj Debugger.Object
* The debuggee object of the array.
* @return Number
* @throws if the object is not an array.
*/
function getArrayLength(object) {
if (!isArray(object)) {
throw new Error("Expected an array, got a " + object.class);
}
// Real arrays have a reliable `length` own property.
if (object.class === "Array") {
return DevToolsUtils.getProperty(object, "length");
}
// For typed arrays, `DevToolsUtils.getProperty` is not reliable because the `length`
// getter could be shadowed by an own property, and `getOwnPropertyNames` is
// unnecessarily slow. Obtain the `length` getter safely and call it manually.
let typedProto = Object.getPrototypeOf(Uint8Array.prototype);
let getter = Object.getOwnPropertyDescriptor(typedProto, "length").get;
return getter.call(object.unsafeDereference());
}
/**
* Returns true if the parameter is suitable to be an array index.
*
* @param str String
* @return Boolean
*/
function isArrayIndex(str) {
// Transform the parameter to a 32-bit unsigned integer.
let num = str >>> 0;
// Check that the parameter is a canonical Uint32 index.
return num + "" === str &&
// Array indices cannot attain the maximum Uint32 value.
num != -1 >>> 0;
}
module.exports = {
getPromiseState,
makeDebuggeeValueIfNeeded,
createValueGrip,
stringIsLong,
isTypedArray,
isArray,
getArrayLength,
isArrayIndex,
};

View File

@ -7,7 +7,8 @@
const protocol = require("devtools/shared/protocol");
const { promisesSpec } = require("devtools/shared/specs/promises");
const { expectState, ActorPool } = require("devtools/server/actors/common");
const { ObjectActor, createValueGrip } = require("devtools/server/actors/object");
const { ObjectActor } = require("devtools/server/actors/object");
const { createValueGrip } = require("devtools/server/actors/object/utils");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const EventEmitter = require("devtools/shared/event-emitter");

View File

@ -10,7 +10,7 @@ const { Cc, Ci } = require("chrome");
const Services = require("Services");
const { BreakpointActor, setBreakpointAtEntryPoints } = require("devtools/server/actors/breakpoint");
const { OriginalLocation, GeneratedLocation } = require("devtools/server/actors/common");
const { createValueGrip, arrayBufferGrip } = require("devtools/server/actors/object");
const { createValueGrip } = require("devtools/server/actors/object/utils");
const { ActorClassWithSpec } = require("devtools/shared/protocol");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { assert, fetch } = DevToolsUtils;
@ -20,6 +20,7 @@ const { sourceSpec } = require("devtools/shared/specs/source");
loader.lazyRequireGetter(this, "SourceMapConsumer", "source-map", true);
loader.lazyRequireGetter(this, "SourceMapGenerator", "source-map", true);
loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
loader.lazyRequireGetter(this, "arrayBufferGrip", "devtools/server/actors/array-buffer", true);
function isEvalSource(source) {
let introType = source.introductionType;

View File

@ -9,7 +9,8 @@
const Services = require("Services");
const { Cr } = require("chrome");
const { ActorPool, GeneratedLocation } = require("devtools/server/actors/common");
const { createValueGrip, longStringGrip } = require("devtools/server/actors/object");
const { createValueGrip } = require("devtools/server/actors/object/utils");
const { longStringGrip } = require("devtools/server/actors/object/long-string");
const { ActorClassWithSpec } = require("devtools/shared/protocol");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const flags = require("devtools/shared/flags");

View File

@ -9,7 +9,7 @@ const { Cu } = require("chrome");
const protocol = require("devtools/shared/protocol");
const { CallWatcherActor } = require("devtools/server/actors/call-watcher");
const { createValueGrip } = require("devtools/server/actors/object");
const { createValueGrip } = require("devtools/server/actors/object/utils");
const AutomationTimeline = require("./utils/automation-timeline");
const {
audionodeSpec,

View File

@ -12,7 +12,9 @@ const Services = require("Services");
const { Cc, Ci, Cu } = require("chrome");
const { DebuggerServer, ActorPool } = require("devtools/server/main");
const { ThreadActor } = require("devtools/server/actors/thread");
const { ObjectActor, LongStringActor, createValueGrip, stringIsLong } = require("devtools/server/actors/object");
const { ObjectActor } = require("devtools/server/actors/object");
const { LongStringActor } = require("devtools/server/actors/object/long-string");
const { createValueGrip, stringIsLong } = require("devtools/server/actors/object/utils");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const ErrorDocs = require("devtools/server/actors/errordocs");

View File

@ -4,7 +4,7 @@
"use strict";
const { LongStringActor } = require("devtools/server/actors/object");
const { LongStringActor } = require("devtools/server/actors/object/long-string");
function run_test() {
test_LSA_destroy();

View File

@ -4,7 +4,7 @@
"use strict";
const { SymbolActor } = require("devtools/server/actors/object");
const { SymbolActor } = require("devtools/server/actors/object/symbol");
function run_test() {
test_SA_destroy();

View File

@ -12,6 +12,9 @@
#include "mozilla/Base64.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#ifndef RELEASE_OR_BETA
#include "mozilla/PerformanceUtils.h"
#endif
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/IdleDeadline.h"
@ -659,11 +662,25 @@ ChromeUtils::ClearRecentJSDevError(GlobalObject&)
ChromeUtils::RequestPerformanceMetrics(GlobalObject&)
{
MOZ_ASSERT(XRE_IsParentProcess());
// calling all content processes via IPDL (async)
nsTArray<ContentParent*> children;
ContentParent::GetAll(children);
for (uint32_t i = 0; i < children.Length(); i++) {
mozilla::Unused << children[i]->SendRequestPerformanceMetrics();
}
// collecting the current process counters and notifying them
nsTArray<PerformanceInfo> info;
CollectPerformanceInfo(info);
SystemGroup::Dispatch(TaskCategory::Performance,
NS_NewRunnableFunction(
"RequestPerformanceMetrics",
[info]() { mozilla::Unused << NS_WARN_IF(NS_FAILED(NotifyPerformanceInfo(info))); }
)
);
}
#endif

View File

@ -7,7 +7,9 @@
#ifndef nsPerformanceMetrics_h___
#define nsPerformanceMetrics_h___
#include "nsCOMArray.h"
#include "nsIPerformanceMetrics.h"
#include "nsString.h"
class PerformanceMetricsDispatchCategory final : public nsIPerformanceMetricsDispatchCategory

View File

@ -69,6 +69,9 @@
#include "mozilla/net/NeckoChild.h"
#include "mozilla/net/CookieServiceChild.h"
#include "mozilla/net/CaptivePortalService.h"
#ifndef RELEASE_OR_BETA
#include "mozilla/PerformanceUtils.h"
#endif
#include "mozilla/plugins/PluginInstanceParent.h"
#include "mozilla/plugins/PluginModuleParent.h"
#include "mozilla/widget/ScreenManager.h"
@ -81,7 +84,6 @@
#include "imgLoader.h"
#include "GMPServiceChild.h"
#include "NullPrincipal.h"
#include "nsIPerformanceMetrics.h"
#include "nsISimpleEnumerator.h"
#include "nsIWorkerDebuggerManager.h"
@ -1388,27 +1390,9 @@ mozilla::ipc::IPCResult
ContentChild::RecvRequestPerformanceMetrics()
{
#ifndef RELEASE_OR_BETA
// iterate on all WorkerDebugger
RefPtr<WorkerDebuggerManager> wdm = WorkerDebuggerManager::GetOrCreate();
if (NS_WARN_IF(!wdm)) {
return IPC_OK();
}
for (uint32_t index = 0; index < wdm->GetDebuggersLength(); index++) {
WorkerDebugger* debugger = wdm->GetDebuggerAt(index);
MOZ_ASSERT(debugger);
SendAddPerformanceMetrics(debugger->ReportPerformanceInfo());
}
// iterate on all DocGroup
nsTArray<RefPtr<TabChild>> tabs = TabChild::GetAll();
for (const auto& tabChild : tabs) {
TabGroup* tabGroup = tabChild->TabGroup();
for (auto iter = tabGroup->Iter(); !iter.Done(); iter.Next()) {
RefPtr<DocGroup> docGroup = iter.Get()->mDocGroup;
SendAddPerformanceMetrics(docGroup->ReportPerformanceInfo());
}
}
nsTArray<PerformanceInfo> info;
CollectPerformanceInfo(info);
SendAddPerformanceMetrics(info);
return IPC_OK();
#endif
#ifdef RELEASE_OR_BETA

View File

@ -184,6 +184,9 @@
#include "private/pprio.h"
#include "ContentProcessManager.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#ifndef RELEASE_OR_BETA
#include "mozilla/PerformanceUtils.h"
#endif
#include "mozilla/psm/PSMContentListener.h"
#include "nsPluginHost.h"
#include "nsPluginTags.h"
@ -193,10 +196,7 @@
#include "nsHostObjectProtocolHandler.h"
#include "nsICaptivePortalService.h"
#include "nsIObjectLoadingContent.h"
#include "nsPerformanceMetrics.h"
#include "nsIBidiKeyboard.h"
#include "nsLayoutStylesheetCache.h"
#include "mozilla/Sprintf.h"
@ -3322,41 +3322,12 @@ ContentParent::RecvFinishMemoryReport(const uint32_t& aGeneration)
}
mozilla::ipc::IPCResult
ContentParent::RecvAddPerformanceMetrics(const PerformanceInfo& aMetrics)
ContentParent::RecvAddPerformanceMetrics(nsTArray<PerformanceInfo>&& aMetrics)
{
#ifndef RELEASE_OR_BETA
// converting the data we get from a child as a notification
if (aMetrics.items().IsEmpty()) {
return IPC_OK();
}
nsCOMPtr<nsIMutableArray> xpItems = do_CreateInstance(NS_ARRAY_CONTRACTID);
if (NS_WARN_IF(!xpItems)) {
return IPC_FAIL_NO_REASON(this);
}
for (uint32_t i = 0; i<aMetrics.items().Length(); i++) {
const CategoryDispatch& entry = aMetrics.items()[i];
nsCOMPtr<nsIPerformanceMetricsDispatchCategory> item =
new PerformanceMetricsDispatchCategory(entry.category(),
entry.count());
xpItems->AppendElement(item);
}
nsCOMPtr<nsIPerformanceMetricsData> data =
new PerformanceMetricsData(aMetrics.pid(), aMetrics.wid(), aMetrics.pwid(),
aMetrics.host(), aMetrics.duration(),
aMetrics.worker(), xpItems);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return IPC_FAIL_NO_REASON(this);
}
obs->NotifyObservers(data, "performance-metrics", nullptr);
return IPC_OK();
Unused << NS_WARN_IF(NS_FAILED(mozilla::NotifyPerformanceInfo(aMetrics)));
#endif
#ifdef RELEASE_OR_BETA
return IPC_OK();
#endif
}
PCycleCollectWithLogsParent*

View File

@ -848,7 +848,7 @@ private:
mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override;
mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override;
mozilla::ipc::IPCResult RecvAddPerformanceMetrics(const PerformanceInfo& aMetrics) override;
mozilla::ipc::IPCResult RecvAddPerformanceMetrics(nsTArray<PerformanceInfo>&& aMetrics) override;
virtual bool
DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override;

View File

@ -150,7 +150,7 @@ struct PerformanceInfo
// Host of the document, if any
nsCString host;
// process id
uint16_t pid;
uint32_t pid;
// window id
uint64_t wid;
// "parent" window id

View File

@ -1136,7 +1136,7 @@ parent:
async BHRThreadHang(HangDetails aHangDetails);
async AddPerformanceMetrics(PerformanceInfo aMetrics);
async AddPerformanceMetrics(PerformanceInfo[] aMetrics);
both:
async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
Principal aPrincipal, ClonedMessageData aData);

View File

@ -18,9 +18,20 @@
namespace mozilla {
struct AudioChunk;
class AudioSegment;
}
DECLARE_USE_COPY_CONSTRUCTORS(mozilla::AudioChunk)
/**
* This allows compilation of nsTArray<AudioSegment> and
* AutoTArray<AudioSegment> since without it, static analysis fails on the
* mChunks member being a non-memmovable AutoTArray.
*
* Note that AudioSegment(const AudioSegment&) is deleted, so this should
* never come into effect.
*/
DECLARE_USE_COPY_CONSTRUCTORS(mozilla::AudioSegment)
namespace mozilla {
template<typename T>
@ -245,7 +256,7 @@ struct AudioChunk {
return static_cast<T*>(const_cast<void*>(mChannelData[aChannel]));
}
PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
const PrincipalHandle& GetPrincipalHandle() const { return mPrincipalHandle; }
StreamTime mDuration = 0; // in frames within the buffer
RefPtr<ThreadSharedObject> mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes

View File

@ -130,13 +130,20 @@ Benchmark::ReturnResult(uint32_t aDecodeFps)
mPromise.ResolveIfExists(aDecodeFps, __func__);
}
void
Benchmark::ReturnError(const MediaResult& aError)
{
MOZ_ASSERT(OnThread());
mPromise.RejectIfExists(aError, __func__);
}
void
Benchmark::Dispose()
{
MOZ_ASSERT(OnThread());
mKeepAliveUntilComplete = nullptr;
mPromise.RejectIfExists(false, __func__);
}
void
@ -184,12 +191,12 @@ BenchmarkPlayback::DemuxSamples()
mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
}
if (!mTrackDemuxer) {
MainThreadShutdown();
Error(MediaResult(NS_ERROR_FAILURE, "Can't create track demuxer"));
return;
}
DemuxNextSample();
},
[this, ref](const MediaResult& aError) { MainThreadShutdown(); });
[this, ref](const MediaResult& aError) { Error(aError); });
}
void
@ -217,7 +224,8 @@ BenchmarkPlayback::DemuxNextSample()
InitDecoder(Move(*mTrackDemuxer->GetInfo()));
break;
default:
MainThreadShutdown();
Error(aError);
break;
}
});
}
@ -230,18 +238,38 @@ BenchmarkPlayback::InitDecoder(TrackInfo&& aInfo)
RefPtr<PDMFactory> platform = new PDMFactory();
mDecoder = platform->CreateDecoder({ aInfo, mDecoderTaskQueue });
if (!mDecoder) {
MainThreadShutdown();
Error(MediaResult(NS_ERROR_FAILURE, "Failed to create decoder"));
return;
}
RefPtr<Benchmark> ref(mMainThreadState);
mDecoder->Init()->Then(
Thread(), __func__,
[this, ref](TrackInfo::TrackType aTrackType) {
InputExhausted();
},
[this, ref](const MediaResult& aError) {
MainThreadShutdown();
});
[this, ref](TrackInfo::TrackType aTrackType) { InputExhausted(); },
[this, ref](const MediaResult& aError) { Error(aError); });
}
void
BenchmarkPlayback::FinalizeShutdown()
{
MOZ_ASSERT(OnThread());
MOZ_ASSERT(!mDecoder, "mDecoder must have been shutdown already");
mDecoderTaskQueue->BeginShutdown();
mDecoderTaskQueue->AwaitShutdownAndIdle();
mDecoderTaskQueue = nullptr;
if (mTrackDemuxer) {
mTrackDemuxer->Reset();
mTrackDemuxer->BreakCycles();
mTrackDemuxer = nullptr;
}
mDemuxer = nullptr;
RefPtr<Benchmark> ref(mMainThreadState);
Thread()->AsTaskQueue()->BeginShutdown()->Then(
ref->Thread(), __func__,
[ref]() { ref->Dispose(); },
[]() { MOZ_CRASH("not reached"); });
}
void
@ -249,10 +277,8 @@ BenchmarkPlayback::MainThreadShutdown()
{
MOZ_ASSERT(OnThread());
if (mFinished) {
// Nothing more to do.
return;
}
MOZ_ASSERT(!mFinished, "We've already shutdown");
mFinished = true;
if (mDecoder) {
@ -263,25 +289,14 @@ BenchmarkPlayback::MainThreadShutdown()
mDecoder->Shutdown()->Then(
Thread(), __func__,
[ref, this]() {
mDecoderTaskQueue->BeginShutdown();
mDecoderTaskQueue->AwaitShutdownAndIdle();
mDecoderTaskQueue = nullptr;
if (mTrackDemuxer) {
mTrackDemuxer->Reset();
mTrackDemuxer->BreakCycles();
mTrackDemuxer = nullptr;
}
Thread()->AsTaskQueue()->BeginShutdown()->Then(
ref->Thread(), __func__,
[ref]() { ref->Dispose(); },
[]() { MOZ_CRASH("not reached"); });
FinalizeShutdown();
},
[]() { MOZ_CRASH("not reached"); });
mDecoder = nullptr;
},
[]() { MOZ_CRASH("not reached"); });
} else {
FinalizeShutdown();
}
}
@ -289,6 +304,8 @@ void
BenchmarkPlayback::Output(const MediaDataDecoder::DecodedData& aResults)
{
MOZ_ASSERT(OnThread());
MOZ_ASSERT(!mFinished);
RefPtr<Benchmark> ref(mMainThreadState);
mFrameCount += aResults.Length();
if (!mDecodeStartTime && mFrameCount >= ref->mParameters.mStartupFrame) {
@ -297,9 +314,8 @@ BenchmarkPlayback::Output(const MediaDataDecoder::DecodedData& aResults)
TimeStamp now = TimeStamp::Now();
int32_t frames = mFrameCount - ref->mParameters.mStartupFrame;
TimeDuration elapsedTime = now - mDecodeStartTime.refOr(now);
if (!mFinished &&
(((frames == ref->mParameters.mFramesToMeasure) && frames > 0) ||
elapsedTime >= ref->mParameters.mTimeout || mDrained)) {
if (((frames == ref->mParameters.mFramesToMeasure) && frames > 0) ||
elapsedTime >= ref->mParameters.mTimeout || mDrained) {
uint32_t decodeFps = frames / elapsedTime.ToSeconds();
MainThreadShutdown();
ref->Dispatch(
@ -309,34 +325,64 @@ BenchmarkPlayback::Output(const MediaDataDecoder::DecodedData& aResults)
}
}
void
BenchmarkPlayback::Error(const MediaResult& aError)
{
MOZ_ASSERT(OnThread());
RefPtr<Benchmark> ref(mMainThreadState);
MainThreadShutdown();
ref->Dispatch(NS_NewRunnableFunction(
"BenchmarkPlayback::Error",
[ref, aError]() { ref->ReturnError(aError); }));
}
void
BenchmarkPlayback::InputExhausted()
{
MOZ_ASSERT(OnThread());
if (mFinished || mSampleIndex >= mSamples.Length()) {
MOZ_ASSERT(!mFinished);
if (mSampleIndex >= mSamples.Length()) {
Error(MediaResult(NS_ERROR_FAILURE, "Nothing left to decode"));
return;
}
RefPtr<MediaRawData> sample = mSamples[mSampleIndex];
RefPtr<Benchmark> ref(mMainThreadState);
mDecoder->Decode(mSamples[mSampleIndex])
->Then(Thread(), __func__,
[ref, this](const MediaDataDecoder::DecodedData& aResults) {
Output(aResults);
InputExhausted();
},
[ref, this](const MediaResult& aError) { MainThreadShutdown(); });
RefPtr<MediaDataDecoder::DecodePromise> p = mDecoder->Decode(sample);
mSampleIndex++;
if (mSampleIndex == mSamples.Length()) {
if (ref->mParameters.mStopAtFrame) {
if (mSampleIndex == mSamples.Length() && !ref->mParameters.mStopAtFrame) {
// Complete current frame decode then drain if still necessary.
p->Then(Thread(), __func__,
[ref, this](const MediaDataDecoder::DecodedData& aResults) {
Output(aResults);
if (!mFinished) {
mDecoder->Drain()->Then(
Thread(), __func__,
[ref, this](const MediaDataDecoder::DecodedData& aResults) {
mDrained = true;
Output(aResults);
MOZ_ASSERT(mFinished, "We must be done now");
},
[ref, this](const MediaResult& aError) { Error(aError); });
}
},
[ref, this](const MediaResult& aError) { Error(aError); });
} else {
if (mSampleIndex == mSamples.Length() && ref->mParameters.mStopAtFrame) {
mSampleIndex = 0;
} else {
mDecoder->Drain()->Then(
Thread(), __func__,
[ref, this](const MediaDataDecoder::DecodedData& aResults) {
mDrained = true;
Output(aResults);
},
[ref, this](const MediaResult& aError) { MainThreadShutdown(); });
}
// Continue decoding
p->Then(Thread(), __func__,
[ref, this](const MediaDataDecoder::DecodedData& aResults) {
Output(aResults);
if (!mFinished) {
InputExhausted();
}
},
[ref, this](const MediaResult& aError) { Error(aError); });
}
}

View File

@ -30,8 +30,12 @@ class BenchmarkPlayback : public QueueObject
void InitDecoder(TrackInfo&& aInfo);
void Output(const MediaDataDecoder::DecodedData& aResults);
void Error(const MediaResult& aError);
void InputExhausted();
// Shutdown trackdemuxer and demuxer if any and shutdown the task queues.
void FinalizeShutdown();
Atomic<Benchmark*> mMainThreadState;
RefPtr<TaskQueue> mDecoderTaskQueue;
@ -81,7 +85,7 @@ public:
const TimeDuration mTimeout;
};
typedef MozPromise<uint32_t, bool, /* IsExclusive = */ true> BenchmarkPromise;
typedef MozPromise<uint32_t, MediaResult, /* IsExclusive = */ true> BenchmarkPromise;
explicit Benchmark(MediaDataDemuxer* aDemuxer,
const Parameters& aParameters = Parameters());
@ -93,6 +97,7 @@ private:
friend class BenchmarkPlayback;
virtual ~Benchmark();
void ReturnResult(uint32_t aDecodeFps);
void ReturnError(const MediaResult& aError);
void Dispose();
const Parameters mParameters;
RefPtr<Benchmark> mKeepAliveUntilComplete;

View File

@ -56,6 +56,16 @@ const StreamTime STREAM_TIME_MAX = MEDIA_TIME_MAX;
typedef MediaTime GraphTime;
const GraphTime GRAPH_TIME_MAX = MEDIA_TIME_MAX;
/**
* The number of chunks allocated by default for a MediaSegment.
* Appending more chunks than this will cause further allocations.
*
* 16 is an arbitrary number intended to cover the most common cases in the
* MediaStreamGraph (1 with silence and 1-2 with data for a realtime track)
* with some margin.
*/
const size_t DEFAULT_SEGMENT_CAPACITY = 16;
/**
* We pass the principal through the MediaStreamGraph by wrapping it in a thread
* safe nsMainThreadPtrHandle, since it cannot be used directly off the main
@ -74,13 +84,13 @@ inline PrincipalHandle MakePrincipalHandle(nsIPrincipal* aPrincipal)
#define PRINCIPAL_HANDLE_NONE nullptr
inline nsIPrincipal* GetPrincipalFromHandle(PrincipalHandle& aPrincipalHandle)
inline nsIPrincipal* GetPrincipalFromHandle(const PrincipalHandle& aPrincipalHandle)
{
MOZ_ASSERT(NS_IsMainThread());
return aPrincipalHandle.get();
}
inline bool PrincipalHandleMatches(PrincipalHandle& aPrincipalHandle,
inline bool PrincipalHandleMatches(const PrincipalHandle& aPrincipalHandle,
nsIPrincipal* aOther)
{
if (!aOther) {
@ -138,14 +148,14 @@ public:
/**
* Gets the last principal id that was appended to this segment.
*/
PrincipalHandle GetLastPrincipalHandle() const { return mLastPrincipalHandle; }
const PrincipalHandle& GetLastPrincipalHandle() const { return mLastPrincipalHandle; }
/**
* Called by the MediaStreamGraph as it appends a chunk with a different
* principal id than the current one.
*/
void SetLastPrincipalHandle(const PrincipalHandle& aLastPrincipalHandle)
void SetLastPrincipalHandle(PrincipalHandle aLastPrincipalHandle)
{
mLastPrincipalHandle = aLastPrincipalHandle;
mLastPrincipalHandle = Forward<PrincipalHandle>(aLastPrincipalHandle);
}
/**
@ -354,7 +364,8 @@ public:
void Clear() override
{
mDuration = 0;
mChunks.Clear();
mChunks.ClearAndRetainStorage();
mChunks.SetCapacity(DEFAULT_SEGMENT_CAPACITY);
}
class ChunkIterator {
@ -436,15 +447,24 @@ public:
}
protected:
explicit MediaSegmentBase(Type aType) : MediaSegment(aType) {}
explicit MediaSegmentBase(Type aType)
: MediaSegment(aType)
, mChunks()
{}
MediaSegmentBase(MediaSegmentBase&& aSegment)
: MediaSegment(Move(aSegment))
, mChunks(Move(aSegment.mChunks))
, mChunks()
#ifdef MOZILLA_INTERNAL_API
, mTimeStamp(Move(aSegment.mTimeStamp))
#endif
{}
{
mChunks.SwapElements(aSegment.mChunks);
MOZ_ASSERT(mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY,
"Capacity must be retained in self after swap");
MOZ_ASSERT(aSegment.mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY,
"Capacity must be retained in other after swap");
}
/**
* Appends the contents of aSource to this segment, clearing aSource.
@ -454,12 +474,20 @@ protected:
MOZ_ASSERT(aSource->mDuration >= 0);
mDuration += aSource->mDuration;
aSource->mDuration = 0;
size_t offset = 0;
if (!mChunks.IsEmpty() && !aSource->mChunks.IsEmpty() &&
mChunks[mChunks.Length() - 1].CanCombineWithFollowing(aSource->mChunks[0])) {
mChunks[mChunks.Length() - 1].mDuration += aSource->mChunks[0].mDuration;
aSource->mChunks.RemoveElementAt(0);
offset = 1;
}
mChunks.AppendElements(Move(aSource->mChunks));
for (; offset < aSource->mChunks.Length(); ++offset) {
mChunks.AppendElement(Move(aSource->mChunks[offset]));
}
aSource->mChunks.ClearAndRetainStorage();
MOZ_ASSERT(aSource->mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY,
"Capacity must be retained after appending from aSource");
}
void AppendSliceInternal(const MediaSegmentBase<C, Chunk>& aSource,
@ -512,8 +540,15 @@ protected:
t -= c->GetDuration();
chunksToRemove = i + 1 - aStartIndex;
}
mChunks.RemoveElementsAt(aStartIndex, chunksToRemove);
if (aStartIndex == 0 && chunksToRemove == mChunks.Length()) {
mChunks.ClearAndRetainStorage();
} else {
mChunks.RemoveElementsAt(aStartIndex, chunksToRemove);
}
mDuration -= aDuration - t;
MOZ_ASSERT(mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY,
"Capacity must be retained after removing chunks");
}
void RemoveTrailing(StreamTime aKeep, uint32_t aStartIndex)
@ -535,10 +570,12 @@ protected:
if (i+1 < mChunks.Length()) {
mChunks.RemoveElementsAt(i+1, mChunks.Length() - (i+1));
}
MOZ_ASSERT(mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY,
"Capacity must be retained after removing chunks");
// Caller must adjust mDuration
}
nsTArray<Chunk> mChunks;
AutoTArray<Chunk, DEFAULT_SEGMENT_CAPACITY> mChunks;
#ifdef MOZILLA_INTERNAL_API
mozilla::TimeStamp mTimeStamp;
#endif

View File

@ -243,7 +243,7 @@ MediaStreamGraphImpl::ProcessChunkMetadataForInterval(MediaStream* aStream,
if (chunk->IsNull() || offset < aStart) {
continue;
}
PrincipalHandle principalHandle = chunk->GetPrincipalHandle();
const PrincipalHandle& principalHandle = chunk->GetPrincipalHandle();
if (principalHandle != aSegment.GetLastPrincipalHandle()) {
aSegment.SetLastPrincipalHandle(principalHandle);
LOG(LogLevel::Debug,
@ -332,31 +332,32 @@ namespace {
} // namespace
bool
MediaStreamGraphImpl::AudioTrackPresent(bool& aNeedsAEC)
MediaStreamGraphImpl::AudioTrackPresent()
{
MOZ_ASSERT(OnGraphThreadOrNotRunning());
bool audioTrackPresent = false;
for (uint32_t i = 0; i < mStreams.Length() && audioTrackPresent == false; ++i) {
MediaStream* stream = mStreams[i];
SourceMediaStream* source = stream->AsSourceStream();
#ifdef MOZ_WEBRTC
if (source && source->NeedsMixing()) {
aNeedsAEC = true;
}
#endif
// If this is a AudioNodeStream, force a AudioCallbackDriver.
for (MediaStream* stream : mStreams) {
if (stream->AsAudioNodeStream()) {
audioTrackPresent = true;
} else {
for (StreamTracks::TrackIter tracks(stream->GetStreamTracks(), MediaSegment::AUDIO);
!tracks.IsEnded(); tracks.Next()) {
audioTrackPresent = true;
}
break;
}
if (source) {
if (!StreamTracks::TrackIter(
stream->GetStreamTracks(),
MediaSegment::AUDIO
).IsEnded()) {
audioTrackPresent = true;
break;
}
if (SourceMediaStream* source = stream->AsSourceStream()) {
audioTrackPresent = source->HasPendingAudioTrack();
}
if (audioTrackPresent) {
break;
}
}
// XXX For some reason, there are race conditions when starting an audio input where
@ -365,9 +366,6 @@ MediaStreamGraphImpl::AudioTrackPresent(bool& aNeedsAEC)
if (!audioTrackPresent && mInputDeviceUsers.Count() != 0) {
NS_WARNING("No audio tracks, but full-duplex audio is enabled!!!!!");
audioTrackPresent = true;
#ifdef MOZ_WEBRTC
aNeedsAEC = true;
#endif
}
return audioTrackPresent;
@ -377,8 +375,7 @@ void
MediaStreamGraphImpl::UpdateStreamOrder()
{
MOZ_ASSERT(OnGraphThreadOrNotRunning());
bool shouldAEC = false;
bool audioTrackPresent = AudioTrackPresent(shouldAEC);
bool audioTrackPresent = AudioTrackPresent();
// Note that this looks for any audio streams, input or output, and switches to a
// SystemClockDriver if there are none. However, if another is already pending, let that
@ -895,8 +892,7 @@ MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
mAudioInputs.RemoveElement(aListener);
// Switch Drivers since we're adding or removing an input (to nothing/system or output only)
bool shouldAEC = false;
bool audioTrackPresent = AudioTrackPresent(shouldAEC);
bool audioTrackPresent = AudioTrackPresent();
MonitorAutoLock mon(mMonitor);
if (LifecycleStateRef() == LIFECYCLE_RUNNING) {
@ -4148,8 +4144,7 @@ MediaStreamGraphImpl::ApplyAudioContextOperationImpl(
// This is the same logic as in UpdateStreamOrder, but it's simpler to have it
// here as well so we don't have to store the Promise(s) on the Graph.
if (aOperation != AudioContextOperation::Resume) {
bool shouldAEC = false;
bool audioTrackPresent = AudioTrackPresent(shouldAEC);
bool audioTrackPresent = AudioTrackPresent();
if (!audioTrackPresent && CurrentDriver()->AsAudioCallbackDriver()) {
CurrentDriver()->AsAudioCallbackDriver()->

View File

@ -322,9 +322,8 @@ public:
/**
* Determine if we have any audio tracks, or are about to add any audiotracks.
* Also checks if we'll need the AEC running (i.e. microphone input tracks)
*/
bool AudioTrackPresent(bool& aNeedsAEC);
bool AudioTrackPresent();
/**
* Sort mStreams so that every stream not in a cycle is after any streams

View File

@ -40,8 +40,11 @@ public:
Image* GetImage() const { return mImage; }
void SetForceBlack(bool aForceBlack) { mForceBlack = aForceBlack; }
bool GetForceBlack() const { return mForceBlack; }
void SetPrincipalHandle(const PrincipalHandle& aPrincipalHandle) { mPrincipalHandle = aPrincipalHandle; }
PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
void SetPrincipalHandle(PrincipalHandle aPrincipalHandle)
{
mPrincipalHandle = Forward<PrincipalHandle>(aPrincipalHandle);
}
const PrincipalHandle& GetPrincipalHandle() const { return mPrincipalHandle; }
const gfx::IntSize& GetIntrinsicSize() const { return mIntrinsicSize; }
void SetNull();
void TakeFrom(VideoFrame* aFrame);
@ -89,7 +92,7 @@ struct VideoChunk {
return 0;
}
PrincipalHandle GetPrincipalHandle() const { return mFrame.GetPrincipalHandle(); }
const PrincipalHandle& GetPrincipalHandle() const { return mFrame.GetPrincipalHandle(); }
StreamTime mDuration;
VideoFrame mFrame;

View File

@ -897,7 +897,7 @@ StripURIForReporting(nsIURI* aURI,
nsresult
nsCSPContext::GatherSecurityPolicyViolationEventData(
nsISupports* aBlockedContentSource,
nsIURI* aBlockedURI,
nsIURI* aOriginalURI,
nsAString& aViolatedDirective,
uint32_t aViolatedPolicyIndex,
@ -921,23 +921,9 @@ nsCSPContext::GatherSecurityPolicyViolationEventData(
aViolationEventInit.mReferrer = mReferrer;
// blocked-uri
if (aBlockedContentSource) {
if (aBlockedURI) {
nsAutoCString reportBlockedURI;
nsCOMPtr<nsIURI> uri = do_QueryInterface(aBlockedContentSource);
// could be a string or URI
if (uri) {
StripURIForReporting(uri, mSelfURI, reportBlockedURI);
} else {
nsCOMPtr<nsISupportsCString> cstr = do_QueryInterface(aBlockedContentSource);
if (cstr) {
cstr->GetData(reportBlockedURI);
}
}
if (reportBlockedURI.IsEmpty()) {
// this can happen for frame-ancestors violation where the violating
// ancestor is cross-origin.
NS_WARNING("No blocked URI (null aBlockedContentSource) for CSP violation report.");
}
StripURIForReporting(aBlockedURI, mSelfURI, reportBlockedURI);
aViolationEventInit.mBlockedURI = NS_ConvertUTF8toUTF16(reportBlockedURI);
}
@ -1266,8 +1252,10 @@ class CSPReportSenderRunnable final : public Runnable
// 0) prepare violation data
mozilla::dom::SecurityPolicyViolationEventInit init;
// mBlockedContentSource could be a URI or a string.
nsCOMPtr<nsIURI> blockedURI = do_QueryInterface(mBlockedContentSource);
rv = mCSPContext->GatherSecurityPolicyViolationEventData(
mBlockedContentSource, mOriginalURI,
blockedURI, mOriginalURI,
mViolatedDirective, mViolatedPolicyIndex,
mSourceFile, mScriptSample, mLineNum,
init);
@ -1285,8 +1273,6 @@ class CSPReportSenderRunnable final : public Runnable
mCSPContext->SendReports(init, mViolatedPolicyIndex);
// 3) log to console (one per policy violation)
// mBlockedContentSource could be a URI or a string.
nsCOMPtr<nsIURI> blockedURI = do_QueryInterface(mBlockedContentSource);
// if mBlockedContentSource is not a URI, it could be a string
nsCOMPtr<nsISupportsCString> blockedString = do_QueryInterface(mBlockedContentSource);

View File

@ -64,9 +64,8 @@ class nsCSPContext : public nsIContentSecurityPolicy
/**
* Construct SecurityPolicyViolationEventInit structure.
*
* @param aBlockedContentSource
* Either a CSP Source (like 'self', as string) or nsIURI: the source
* of the violation.
* @param aBlockedURI
* A nsIURI: the source of the violation.
* @param aOriginalUri
* The original URI if the blocked content is a redirect, else null
* @param aViolatedDirective
@ -81,7 +80,7 @@ class nsCSPContext : public nsIContentSecurityPolicy
* The output
*/
nsresult GatherSecurityPolicyViolationEventData(
nsISupports* aBlockedContentSource,
nsIURI* aBlockedURI,
nsIURI* aOriginalURI,
nsAString& aViolatedDirective,
uint32_t aViolatedPolicyIndex,

View File

@ -48,7 +48,7 @@ window.checkResults = function(reportObj) {
ok(cspReport["referrer"].startsWith("http://mochi.test:8888/tests/dom/security/test/csp/test_report.html"),
"Incorrect referrer");
is(cspReport["blocked-uri"], "self", "Incorrect blocked-uri");
is(cspReport["blocked-uri"], "", "Incorrect blocked-uri");
is(cspReport["violated-directive"], "default-src", "Incorrect violated-directive");

View File

@ -106,7 +106,7 @@ function run_test() {
createInstance(Ci.nsISupportsString);
content.data = "";
// test that inline script violations cause a report.
makeTest(0, {"blocked-uri": "self"}, false,
makeTest(0, {"blocked-uri": ""}, false,
function(csp) {
let inlineOK = true;
inlineOK = csp.getAllowsInline(Ci.nsIContentPolicy.TYPE_SCRIPT,
@ -120,7 +120,7 @@ function run_test() {
});
// test that eval violations cause a report.
makeTest(1, {"blocked-uri": "self",
makeTest(1, {"blocked-uri": "",
// JSON script-sample is UTF8 encoded
"script-sample" : "\xc2\xa3\xc2\xa5\xc2\xb5\xe5\x8c\x97\xf0\xa0\x9d\xb9"}, false,
function(csp) {
@ -153,7 +153,7 @@ function run_test() {
});
// test that inline script violations cause a report in report-only policy
makeTest(3, {"blocked-uri": "self"}, true,
makeTest(3, {"blocked-uri": ""}, true,
function(csp) {
let inlineOK = true;
let content = Cc["@mozilla.org/supports-string;1"].
@ -170,7 +170,7 @@ function run_test() {
});
// test that eval violations cause a report in report-only policy
makeTest(4, {"blocked-uri": "self"}, true,
makeTest(4, {"blocked-uri": ""}, true,
function(csp) {
let evalOK = true, oReportViolation = {'value': false};
evalOK = csp.getAllowsEval(oReportViolation);

View File

@ -9,6 +9,7 @@ support-files =
worker_bug1004814.js
geo_leak_test.html
dummy.html
ping_worker.html
test_largeAllocation.html
test_largeAllocation.html^headers^
test_largeAllocation2.html

View File

@ -4,52 +4,113 @@
* 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/. */
const TEST_URL = "http://example.com/browser/dom/tests/browser/dummy.html";
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
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";
let nextId = 0;
function jsonrpc(tab, method, params) {
let currentId = nextId++;
let messageManager = tab.linkedBrowser.messageManager;
messageManager.sendAsyncMessage("jsonrpc", {
id: currentId,
method: method,
params: params
});
return new Promise(function (resolve, reject) {
messageManager.addMessageListener("jsonrpc", function listener(event) {
let { id, result, error } = event.data;
if (id !== currentId) {
return;
}
messageManager.removeMessageListener("jsonrpc", listener);
if (error) {
reject(error);
return;
}
resolve(result);
});
});
}
function postMessageToWorker(tab, message) {
return jsonrpc(tab, "postMessageToWorker", [WORKER_URL, message]);
}
add_task(async function test() {
if (!AppConstants.RELEASE_OR_BETA) {
SpecialPowers.setBoolPref('dom.performance.enable_scheduler_timing', true);
waitForExplicitFinish();
SpecialPowers.setBoolPref('dom.performance.enable_scheduler_timing', true);
waitForExplicitFinish();
await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
async function(browser) {
// Load 3 pages and wait. The 3rd one has a worker
let page1 = await BrowserTestUtils.openNewForegroundTab({
gBrowser, opening: 'about:about', forceNewProcess: false
});
// grab events..
var events = [];
function getInfoFromService(subject, topic, value) {
subject = subject.QueryInterface(Ci.nsIPerformanceMetricsData);
if (subject.host == "example.com") {
events.push(subject);
let page2 = await BrowserTestUtils.openNewForegroundTab({
gBrowser, opening: 'about:memory', forceNewProcess: false
});
let page3 = await BrowserTestUtils.openNewForegroundTab({
gBrowser, opening: "about:performance", forceNewProcess: true
});
let parent_process_event = false;
let worker_event = false;
// load a 4th tab with a worker
await BrowserTestUtils.withNewTab({ gBrowser, url: WORKER_URL },
async function(browser) {
// grab events..
var events = [];
function getInfoFromService(subject, topic, value) {
subject = subject.QueryInterface(Ci.nsIMutableArray);
let enumerator = subject.enumerate();
while (enumerator.hasMoreElements()) {
let item = enumerator.getNext();
item = item.QueryInterface(Ci.nsIPerformanceMetricsData);
if (item.pid == Services.appinfo.processID) {
parent_process_event = true;
}
if (item.worker) {
worker_event = true;
}
events.push(item);
}
Services.obs.addObserver(getInfoFromService, "performance-metrics");
}
// trigger an IPDL call
Services.obs.addObserver(getInfoFromService, "performance-metrics");
// wait until we get some events back by triggering requestPerformanceMetrics
await BrowserTestUtils.waitForCondition(() => {
ChromeUtils.requestPerformanceMetrics();
return events.length > 10;
}, "wait for events to come in", 500, 10);
// wait until we get the events back
await BrowserTestUtils.waitForCondition(() => {
return events.length > 0;
}, "wait for events to come in", 100, 20);
// let's check the last example.com tab event we got
let last = events[0];
Assert.equal(last.host, "example.com", "host should be example.com");
Assert.ok(last.duration > 0, "Duration should be positive");
BrowserTestUtils.removeTab(page1);
BrowserTestUtils.removeTab(page2);
BrowserTestUtils.removeTab(page3);
// let's check the events
let duration = 0;
let total = 0;
for (let i=0; i < events.length; i++) {
duration += events[i].duration;
// let's look at the XPCOM data we got back
let items = last.items.QueryInterface(Ci.nsIMutableArray);
let items = events[i].items.QueryInterface(Ci.nsIMutableArray);
let enumerator = items.enumerate();
let total = 0;
while (enumerator.hasMoreElements()) {
let item = enumerator.getNext();
item = item.QueryInterface(Ci.nsIPerformanceMetricsDispatchCategory);
total += item.count;
}
Assert.ok(total > 0);
});
SpecialPowers.clearUserPref('dom.performance.enable_scheduler_timing');
}
}
Assert.ok(duration > 0, "Duration should be positive");
Assert.ok(total > 0, "Should get a positive count");
Assert.ok(parent_process_event, "parent process sent back some events");
});
SpecialPowers.clearUserPref('dom.performance.enable_scheduler_timing');
});

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<script type="text/javascript">
var myWorker;
function init() {
myWorker = new Worker('ping_worker.js');
myWorker.postMessage("ping");
}
</script>
</head>
<body onload="init()">
<h1>A page with a worker</h1>
</body>
</html>

View File

@ -0,0 +1,11 @@
/* 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";
function messageListener(event) {
postMessage("pong");
}
addEventListener("message", { handleEvent: messageListener });

View File

@ -502,12 +502,16 @@ WorkerDebugger::ReportPerformanceInfo()
uint16_t count = perf->GetTotalDispatchCount();
uint64_t duration = perf->GetExecutionDuration();
RefPtr<nsIURI> uri = mWorkerPrivate->GetResolvedScriptURI();
CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
// Workers only produce metrics for a single category - DispatchCategory::Worker.
// We still return an array of CategoryDispatch so the PerformanceInfo
// struct is common to all performance counters throughout Firefox.
FallibleTArray<CategoryDispatch> items;
CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
if (!items.AppendElement(item, fallible)) {
NS_ERROR("Could not complete the operation");
return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration,
true, items);
true, items);
}
perf->ResetPerformanceCounters();
return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration,

View File

@ -424,10 +424,14 @@ private:
return false;
}
// PerformanceStorage needs to be initialized on the worker thread before
// being used on main-thread. Let's be sure that it is created before any
// PerformanceStorage & PerformanceCounter both need to be initialized
// on the worker thread before being used on main-thread.
// Let's be sure that it is created before any
// content loading.
aWorkerPrivate->EnsurePerformanceStorage();
#ifndef RELEASE_OR_BETA
aWorkerPrivate->EnsurePerformanceCounter();
#endif
ErrorResult rv;
workerinternals::LoadMainScript(aWorkerPrivate, mScriptURL, WorkerScript, rv);
@ -5210,15 +5214,19 @@ WorkerPrivate::DumpCrashInformation(nsACString& aString)
}
#ifndef RELEASE_OR_BETA
PerformanceCounter*
WorkerPrivate::GetPerformanceCounter()
void
WorkerPrivate::EnsurePerformanceCounter()
{
AssertIsOnWorkerThread();
if (!mPerformanceCounter) {
mPerformanceCounter = new PerformanceCounter(NS_ConvertUTF16toUTF8(mWorkerName));
}
}
PerformanceCounter*
WorkerPrivate::GetPerformanceCounter()
{
MOZ_ASSERT(mPerformanceCounter);
return mPerformanceCounter;
}
#endif

View File

@ -561,6 +561,11 @@ public:
void
EnsurePerformanceStorage();
#ifndef RELEASE_OR_BETA
void
EnsurePerformanceCounter();
#endif
const ClientInfo&
GetClientInfo() const;

View File

@ -8,7 +8,7 @@ authors = [
description = "Remote Cubeb IPC"
[dependencies]
cubeb = "0.5"
cubeb = "0.5.2"
bincode = "1.0"
bytes = "0.4"
futures = "0.1.18"

View File

@ -9,7 +9,7 @@ description = "Remote cubeb server"
[dependencies]
audioipc = { path = "../audioipc" }
cubeb = "0.5"
cubeb = "0.5.2"
bytes = "0.4"
lazycell = "^0.4"
libc = "0.2"

29
mfbt/Algorithm.h Normal file
View File

@ -0,0 +1,29 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* A polyfill for `<algorithm>`. */
#ifndef mozilla_Algorithm_h
#define mozilla_Algorithm_h
namespace mozilla {
// Returns true if all elements in the range [aFirst, aLast)
// satisfy the predicate aPred.
template <class Iter, class Pred>
constexpr bool AllOf(Iter aFirst, Iter aLast, Pred aPred)
{
for (; aFirst != aLast; ++aFirst) {
if (!aPred(*aFirst)) {
return false;
}
}
return true;
}
} // namespace mozilla
#endif // mozilla_Algorithm_h

View File

@ -12,6 +12,7 @@ TEST_DIRS += ['tests']
Library('mfbt')
EXPORTS.mozilla = [
'Algorithm.h',
'Alignment.h',
'AllocPolicy.h',
'AlreadyAddRefed.h',

View File

@ -0,0 +1,44 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Algorithm.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include <iterator>
static constexpr bool even(int32_t n) { return !(n & 1); }
static constexpr bool odd(int32_t n) { return (n & 1); }
void TestAllOf()
{
using namespace mozilla;
using namespace std;
constexpr static int32_t arr1[3] = {1, 2, 3};
MOZ_RELEASE_ASSERT(!AllOf(begin(arr1), end(arr1), even));
MOZ_RELEASE_ASSERT(!AllOf(begin(arr1), end(arr1), odd));
static_assert(!AllOf(arr1, arr1 + ArrayLength(arr1), even), "1-1");
static_assert(!AllOf(arr1, arr1 + ArrayLength(arr1), odd), "1-2");
constexpr static int32_t arr2[3] = {1, 3, 5};
MOZ_RELEASE_ASSERT(!AllOf(begin(arr2), end(arr2), even));
MOZ_RELEASE_ASSERT(AllOf(begin(arr2), end(arr2), odd));
static_assert(!AllOf(arr2, arr2 + ArrayLength(arr2), even), "2-1");
static_assert(AllOf(arr2, arr2 + ArrayLength(arr2), odd), "2-2");
constexpr static int32_t arr3[3] = {2, 4, 6};
MOZ_RELEASE_ASSERT(AllOf(begin(arr3), end(arr3), even));
MOZ_RELEASE_ASSERT(!AllOf(begin(arr3), end(arr3), odd));
static_assert(AllOf(arr3, arr3 + ArrayLength(arr3), even), "3-1");
static_assert(!AllOf(arr3, arr3 + ArrayLength(arr3), odd), "3-2");
}
int
main()
{
TestAllOf();
return 0;
}

View File

@ -10,6 +10,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT']:
]
CppUnitTests([
'TestAlgorithm',
'TestArray',
'TestArrayUtils',
'TestAtomics',

View File

@ -286,26 +286,31 @@ TRRService::Observe(nsISupports *aSubject,
} else if (!strcmp(aTopic, NS_CAPTIVE_PORTAL_CONNECTIVITY)) {
nsAutoCString data = NS_ConvertUTF16toUTF8(aData);
LOG(("TRRservice captive portal was %s\n", data.get()));
if (!mTRRBLStorage) {
mTRRBLStorage = DataStorage::Get(DataStorageClass::TRRBlacklist);
if (mTRRBLStorage) {
bool storageWillPersist = true;
if (NS_FAILED(mTRRBLStorage->Init(storageWillPersist))) {
mTRRBLStorage = nullptr;
}
if (mClearTRRBLStorage) {
if (mTRRBLStorage) {
mTRRBLStorage->Clear();
if (data.Equals("clear")) {
if (!mTRRBLStorage) {
mTRRBLStorage = DataStorage::Get(DataStorageClass::TRRBlacklist);
if (mTRRBLStorage) {
bool storageWillPersist = true;
if (NS_FAILED(mTRRBLStorage->Init(storageWillPersist))) {
mTRRBLStorage = nullptr;
}
if (mClearTRRBLStorage) {
if (mTRRBLStorage) {
mTRRBLStorage->Clear();
}
mClearTRRBLStorage = false;
}
mClearTRRBLStorage = false;
}
}
if (mConfirmationState != CONFIRM_OK) {
mConfirmationState = CONFIRM_TRYING;
MaybeConfirm();
} else {
LOG(("TRRservice CP clear when already up!\n"));
}
mCaptiveIsPassed = true;
}
mConfirmationState = CONFIRM_TRYING;
MaybeConfirm();
mCaptiveIsPassed = true;
} else if (!strcmp(aTopic, kClearPrivateData) ||
!strcmp(aTopic, kPurge)) {
// flush the TRR blacklist, both in-memory and on-disk

View File

@ -7326,7 +7326,7 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st
}
// shift http to https disposition enums
chanDisposition = static_cast<ChannelDisposition>(chanDisposition + kHttpsCanceled);
} else if (mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
} else if (mLoadInfo && mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
// HTTP content the browser would upgrade to HTTPS if upgrading was enabled
upgradeKey = NS_LITERAL_CSTRING("disabledUpgrade");
} else {

4
other-licenses/nsis/Contrib/CityHash/CityHash.h Normal file → Executable file
View File

@ -29,7 +29,3 @@
#else
#define CITYHASH_API __declspec(dllimport)
#endif
#ifndef ssize_t
typedef int ssize_t;
#endif

4
other-licenses/nsis/Contrib/CityHash/cityhash/city.cpp Normal file → Executable file
View File

@ -202,13 +202,13 @@ uint64 CityHash64WithSeeds(const char *s, size_t len,
}
// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
// of any length representable in ssize_t. Based on City and Murmur.
// of any length representable in an int. Based on City and Murmur.
static uint128 CityMurmur(const char *s, size_t len, uint128 seed) {
uint64 a = Uint128Low64(seed);
uint64 b = Uint128High64(seed);
uint64 c = 0;
uint64 d = 0;
ssize_t l = len - 16;
int l = len - 16;
if (l <= 0) { // len <= 16
c = b * k1 + HashLen0to16(s, len);
d = Rotate(a + (len >= 8 ? UNALIGNED_LOAD64(s) : c), 32);

View File

@ -0,0 +1,563 @@
# coding=utf8
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
from __future__ import absolute_import
import fluent.syntax.ast as FTL
from fluent.migrate.helpers import MESSAGE_REFERENCE, EXTERNAL_ARGUMENT
from fluent.migrate import COPY, CONCAT, REPLACE
# Custom extension of the CONCAT migration tailored to concat
# two strings separated by a placeable.
class CONCAT_BEFORE_AFTER(CONCAT):
def __call__(self, ctx):
assert len(self.elements) == 3
pattern_before, placeable, pattern_after = self.elements
elem_before = pattern_before.elements[0]
elem_after = pattern_after.elements[0]
if isinstance(elem_before, FTL.TextElement) and elem_before.value[-1] != " ":
elem_before.value += " "
if isinstance(elem_after, FTL.TextElement) and elem_after.value[0] != " ":
elem_after.value = " " + elem_after.value
return super(CONCAT_BEFORE_AFTER, self).__call__(ctx)
def migrate(ctx):
"""Bug 1445694 - Migrate Preferences::Sync to Fluent, part {index}."""
ctx.add_transforms(
'browser/browser/branding/sync-brand.ftl',
'browser/browser/branding/sync-brand.ftl',
[
FTL.Term(
id=FTL.Identifier('-fxaccount-brand-name'),
value=COPY(
'browser/chrome/browser/syncBrand.dtd',
'syncBrand.fxAccount.label'
)
),
]
)
ctx.add_transforms(
'browser/browser/preferences/preferences.ftl',
'browser/browser/preferences/preferences.ftl',
[
FTL.Message(
id=FTL.Identifier('sync-signedout-caption'),
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signedOut.caption',
),
),
FTL.Message(
id=FTL.Identifier('sync-signedout-description'),
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signedOut.description',
),
),
FTL.Message(
id=FTL.Identifier('sync-signedout-account-title'),
value=REPLACE(
'browser/chrome/browser/preferences/sync.dtd',
'signedOut.accountBox.title',
{
'&syncBrand.fxAccount.label;': MESSAGE_REFERENCE('-fxaccount-brand-name')
},
),
),
FTL.Message(
id=FTL.Identifier('sync-signedout-account-create'),
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signedOut.accountBox.create2',
),
attributes=[
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signedOut.accountBox.create2.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-signedout-account-signin'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signedOut.accountBox.signin2',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signedOut.accountBox.signin2.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-profile-picture'),
attributes=[
FTL.Attribute(
FTL.Identifier('tooltiptext'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'profilePicture.tooltip',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-disconnect'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'disconnect3.label',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'disconnect3.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-manage-account'),
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'verifiedManage.label',
),
attributes=[
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'verifiedManage.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-signedin-unverified'),
value=CONCAT_BEFORE_AFTER(
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signedInUnverified.beforename.label',
),
EXTERNAL_ARGUMENT('email'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signedInUnverified.aftername.label',
),
),
),
FTL.Message(
id=FTL.Identifier('sync-signedin-login-failure'),
value=CONCAT_BEFORE_AFTER(
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signedInLoginFailure.beforename.label',
),
EXTERNAL_ARGUMENT('email'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signedInLoginFailure.aftername.label',
),
)
),
FTL.Message(
id=FTL.Identifier('sync-resend-verification'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'resendVerification.label',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'resendVerification.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-remove-account'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'removeAccount.label',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'removeAccount.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-sign-in'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signIn.label',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signIn.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-signedin-settings-header'),
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'signedIn.settings.label',
),
),
FTL.Message(
id=FTL.Identifier('sync-signedin-settings-desc'),
value=REPLACE(
'browser/chrome/browser/preferences/sync.dtd',
'signedIn.settings.description',
{
'&brandShortName;': MESSAGE_REFERENCE('-brand-short-name')
},
),
),
FTL.Message(
id=FTL.Identifier('sync-engine-bookmarks'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.bookmarks.label',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.bookmarks.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-engine-history'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.history.label',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.history.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-engine-tabs'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.tabs.label2',
),
),
FTL.Attribute(
FTL.Identifier('tooltiptext'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.tabs.title',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.tabs.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-engine-logins'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.logins.label',
),
),
FTL.Attribute(
FTL.Identifier('tooltiptext'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.logins.title',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.logins.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-engine-addresses'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.addresses.label',
),
),
FTL.Attribute(
FTL.Identifier('tooltiptext'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.addresses.title',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.addresses.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-engine-creditcards'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.creditcards.label',
),
),
FTL.Attribute(
FTL.Identifier('tooltiptext'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.creditcards.title',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.creditcards.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-engine-addons'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.addons.label',
),
),
FTL.Attribute(
FTL.Identifier('tooltiptext'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.addons.title',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.addons.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-engine-prefs'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
FTL.Pattern([
FTL.Placeable(FTL.SelectExpression(
expression=FTL.CallExpression(
callee=FTL.Identifier('PLATFORM')
),
variants=[
FTL.Variant(
key=FTL.VariantName('windows'),
default=False,
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.prefsWin.label'
)
),
FTL.Variant(
key=FTL.VariantName('other'),
default=True,
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.prefs.label'
)
)
]
)),
]),
),
FTL.Attribute(
FTL.Identifier('tooltiptext'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.prefs.title'
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'engine.prefs.accesskey'
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-device-name-header'),
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'fxaSyncDeviceName.label',
),
),
FTL.Message(
id=FTL.Identifier('sync-device-name-change'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'changeSyncDeviceName2.label',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'changeSyncDeviceName2.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-device-name-cancel'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'cancelChangeSyncDeviceName.label',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'cancelChangeSyncDeviceName.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-device-name-save'),
attributes=[
FTL.Attribute(
FTL.Identifier('label'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'saveChangeSyncDeviceName.label',
),
),
FTL.Attribute(
FTL.Identifier('accesskey'),
COPY(
'browser/chrome/browser/preferences/sync.dtd',
'saveChangeSyncDeviceName.accesskey',
),
),
],
),
FTL.Message(
id=FTL.Identifier('sync-mobilepromo-single'),
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'mobilepromo.singledevice',
),
),
FTL.Message(
id=FTL.Identifier('sync-mobilepromo-multi'),
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'mobilepromo.multidevice',
),
),
FTL.Message(
id=FTL.Identifier('sync-tos-link'),
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'prefs.tosLink.label',
),
),
FTL.Message(
id=FTL.Identifier('sync-fxa-privacy-notice'),
value=COPY(
'browser/chrome/browser/preferences/sync.dtd',
'fxaPrivacyNotice.link.label',
),
),
]
)

View File

@ -1016,23 +1016,6 @@ LoadLoadableRootsTask::Dispatch()
NS_IMETHODIMP
LoadLoadableRootsTask::Run()
{
// First we Run() on the "LoadRoots" thread, do our work, and then we Run()
// again on the main thread so we can shut down the thread (since we don't
// need it any more). We can't shut down the thread while we're *on* the
// thread, which is why we do the dispatch to the main thread. CryptoTask.cpp
// (which informs this code) says that we can't null out mThread. This appears
// to be because its refcount could be decreased while this dispatch is being
// processed, so it might get prematurely destroyed. I'm not sure this makes
// sense but it'll get cleaned up in our destructor anyway, so it's fine to
// not null it out here (as long as we don't run twice on the main thread,
// which shouldn't be possible).
if (NS_IsMainThread()) {
if (mThread) {
mThread->Shutdown();
}
return NS_OK;
}
nsresult rv = LoadLoadableRoots();
if (NS_FAILED(rv)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("LoadLoadableRoots failed"));
@ -1060,9 +1043,7 @@ LoadLoadableRootsTask::Run()
return rv;
}
}
// Go back to the main thread to clean up this worker thread.
return NS_DispatchToMainThread(this);
return NS_OK;
}
nsresult

View File

@ -144,8 +144,9 @@ SandboxBrokerClient::DoCall(const Request* aReq, const char* aPath,
// actually exist, if it's something that's optional or part of a
// search path (e.g., shared libraries). In those cases, this
// error message is expected.
SANDBOX_LOG_ERROR("Failed errno %d op %d flags 0%o path %s",
resp.mError, aReq->mOp, aReq->mFlags, path);
SANDBOX_LOG_ERROR("Failed errno %d op %s flags 0%o path %s",
resp.mError, OperationDescription[aReq->mOp],
aReq->mFlags, path);
}
if (openedFd >= 0) {
close(openedFd);

View File

@ -654,7 +654,8 @@ SandboxBroker::SymlinkPermissions(const char* aPath, const size_t aPathLen)
// We finished the translation, so we have a usable return in "perms".
return perms;
} else {
// Empty path means we got a writable dir in the chain.
// Empty path means we got a writable dir in the chain or tried
// to back out of a link target.
return 0;
}
}
@ -798,27 +799,27 @@ SandboxBroker::ThreadMain(void)
// Was it a tempdir that we can remap?
pathLen = RemapTempDirs(pathBuf, sizeof(pathBuf), pathLen);
perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
if (!perms) {
// Did we arrive from a symlink in a path that is not writable?
// Then try to figure out the original path and see if that is
// readable. Work on the original path, this reverses
// ConvertRelative above.
int symlinkPerms = SymlinkPermissions(recvBuf, first_len);
if (symlinkPerms > 0) {
perms = symlinkPerms;
}
if (!perms) {
// Now try the opposite case: translate symlinks to their
// actual destination file. Firefox always resolves symlinks,
// and in most cases we have whitelisted fixed paths that
// libraries will rely on and try to open. So this codepath
// is mostly useful for Mesa which had its kernel interface
// moved around.
pathLen = RealPath(pathBuf, sizeof(pathBuf), pathLen);
perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
}
}
if (!perms) {
// Did we arrive from a symlink in a path that is not writable?
// Then try to figure out the original path and see if that is
// readable. Work on the original path, this reverses
// ConvertRelative above.
int symlinkPerms = SymlinkPermissions(recvBuf, first_len);
if (symlinkPerms > 0) {
perms = symlinkPerms;
}
}
if (!perms) {
// Now try the opposite case: translate symlinks to their
// actual destination file. Firefox always resolves symlinks,
// and in most cases we have whitelisted fixed paths that
// libraries will rely on and try to open. So this codepath
// is mostly useful for Mesa which had its kernel interface
// moved around.
pathLen = RealPath(pathBuf, sizeof(pathBuf), pathLen);
perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
}
// Same for the second path.
pathLen2 = strnlen(pathBuf2, kMaxPathLen);
@ -1055,8 +1056,9 @@ SandboxBroker::AuditPermissive(int aOp, int aFlags, int aPerms, const char* aPat
errno = 0;
}
SANDBOX_LOG_ERROR("SandboxBroker: would have denied op=%d rflags=%o perms=%d path=%s for pid=%d" \
" permissive=1 error=\"%s\"", aOp, aFlags, aPerms,
SANDBOX_LOG_ERROR("SandboxBroker: would have denied op=%s rflags=%o perms=%d path=%s for pid=%d" \
" permissive=1 error=\"%s\"", OperationDescription[aOp],
aFlags, aPerms,
aPath, mChildPid, strerror(errno));
}

View File

@ -14,6 +14,7 @@
#include "mozilla/SandboxSettings.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/SandboxLaunch.h"
#include "mozilla/dom/ContentChild.h"
#include "nsPrintfCString.h"
#include "nsString.h"
@ -51,6 +52,7 @@ static const int rdonly = SandboxBroker::MAY_READ;
static const int wronly = SandboxBroker::MAY_WRITE;
static const int rdwr = rdonly | wronly;
static const int rdwrcr = rdwr | SandboxBroker::MAY_CREATE;
static const int access = SandboxBroker::MAY_ACCESS;
}
#endif
@ -528,6 +530,15 @@ SandboxBrokerPolicyFactory::GetContentPolicy(int aPid, bool aFileProcess)
policy->AddPath(rdonly, "/var/lib/dbus/machine-id");
}
// Bug 1434711 - AMDGPU-PRO crashes if it can't read it's marketing ids
// and various other things
if (HasAtiDrivers()) {
policy->AddDir(rdonly, "/opt/amdgpu/share");
policy->AddPath(rdonly, "/sys/module/amdgpu");
// AMDGPU-PRO's MESA version likes to readlink a lot of things here
policy->AddDir(access, "/sys");
}
// Return the common policy.
policy->FixRecursivePermissions();
return policy;

View File

@ -119,6 +119,31 @@ IsDisplayLocal()
return true;
}
bool HasAtiDrivers()
{
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
nsAutoString vendorID;
static const Array<nsresult (nsIGfxInfo::*)(nsAString&), 2> kMethods = {
&nsIGfxInfo::GetAdapterVendorID,
&nsIGfxInfo::GetAdapterVendorID2,
};
for (const auto method : kMethods) {
if (NS_SUCCEEDED((gfxInfo->*method)(vendorID))) {
// This test is based on telemetry data. The proprietary ATI
// drivers seem to use this vendor string, including for some
// newer devices that have AMD branding in the device name, such
// as those using AMDGPU-PRO drivers.
// The open-source drivers integrated into Mesa appear to use
// the vendor ID "X.Org" instead.
if (vendorID.EqualsLiteral("ATI Technologies Inc.")) {
return true;
}
}
}
return false;
}
// Content processes may need direct access to SysV IPC in certain
// uncommon use cases.
static bool
@ -138,24 +163,10 @@ ContentNeedsSysVIPC()
}
// The fglrx (ATI Catalyst) GPU drivers use SysV IPC.
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
nsAutoString vendorID;
static const Array<nsresult (nsIGfxInfo::*)(nsAString&), 2> kMethods = {
&nsIGfxInfo::GetAdapterVendorID,
&nsIGfxInfo::GetAdapterVendorID2,
};
for (const auto method : kMethods) {
if (NS_SUCCEEDED((gfxInfo->*method)(vendorID))) {
// This test is based on telemetry data. The proprietary ATI
// drivers seem to use this vendor string, including for some
// newer devices that have AMD branding in the device name.
// The open-source drivers integrated into Mesa appear to use
// the vendor ID "X.Org" instead.
if (vendorID.EqualsLiteral("ATI Technologies Inc.")) {
return true;
}
}
if (HasAtiDrivers()) {
return true;
}
return false;
}

View File

@ -17,6 +17,7 @@ namespace mozilla {
// called on the main thread in order to access prefs.
void SandboxLaunchPrepare(GeckoProcessType aType,
base::LaunchOptions* aOptions);
bool HasAtiDrivers();
} // namespace mozilla

View File

@ -614,3 +614,18 @@ macro_rules! impl_performance_entry_struct(
}
);
);
macro_rules! handle_potential_webgl_error {
($context:expr, $call:expr, $return_on_error:expr) => {
match $call {
Ok(ret) => ret,
Err(error) => {
$context.webgl_error(error);
$return_on_error
}
}
};
($context:expr, $call:expr) => {
handle_potential_webgl_error!($context, $call, ());
};
}

Some files were not shown because too many files have changed in this diff Show More