This commit is contained in:
Ryan VanderMeulen 2014-02-18 15:25:07 -05:00
Родитель 482f700272 8a4698657b
Коммит 6735b2344d
55 изменённых файлов: 728 добавлений и 511 удалений

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

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="258ae6472bd0e87ae074a6b9b464273dc9cfc8d6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac06cfbd2baf6494ffbb668cc599e3892cd5e17b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
@ -91,7 +91,7 @@
<project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
<!-- Emulator specific things -->
<project name="android-development" path="development" remote="b2g" revision="4e236e65a5d652a66ac32590f69f2123d17cb4ad"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="5be0a9c4b3c6c004786917fdb5bee248960d045b"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="e7e8734fdd8bf41e48a56c1c85e0f7dac60aaa9f"/>
<project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
<project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
<project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>

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

@ -11,7 +11,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="258ae6472bd0e87ae074a6b9b464273dc9cfc8d6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac06cfbd2baf6494ffbb668cc599e3892cd5e17b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>

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

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="258ae6472bd0e87ae074a6b9b464273dc9cfc8d6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac06cfbd2baf6494ffbb668cc599e3892cd5e17b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
@ -91,7 +91,7 @@
<project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
<!-- Emulator specific things -->
<project name="android-development" path="development" remote="b2g" revision="4e236e65a5d652a66ac32590f69f2123d17cb4ad"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="5be0a9c4b3c6c004786917fdb5bee248960d045b"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="e7e8734fdd8bf41e48a56c1c85e0f7dac60aaa9f"/>
<project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
<project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
<project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>

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

@ -1,4 +1,4 @@
{
"revision": "8d15cd3ac0d07cde9ff36ce611ae47e6ef30d9b0",
"revision": "a8b221aeef715c5d8159faa5b31f3ee15e5c3c5a",
"repo_path": "/integration/gaia-central"
}

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

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="258ae6472bd0e87ae074a6b9b464273dc9cfc8d6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac06cfbd2baf6494ffbb668cc599e3892cd5e17b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

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

@ -10,7 +10,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="258ae6472bd0e87ae074a6b9b464273dc9cfc8d6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac06cfbd2baf6494ffbb668cc599e3892cd5e17b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

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

@ -12,7 +12,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="258ae6472bd0e87ae074a6b9b464273dc9cfc8d6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac06cfbd2baf6494ffbb668cc599e3892cd5e17b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

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

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="258ae6472bd0e87ae074a6b9b464273dc9cfc8d6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac06cfbd2baf6494ffbb668cc599e3892cd5e17b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

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

@ -11,7 +11,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="258ae6472bd0e87ae074a6b9b464273dc9cfc8d6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac06cfbd2baf6494ffbb668cc599e3892cd5e17b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>

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

@ -11,7 +11,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="258ae6472bd0e87ae074a6b9b464273dc9cfc8d6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac06cfbd2baf6494ffbb668cc599e3892cd5e17b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>

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

@ -468,7 +468,6 @@ pref("browser.tabs.closeButtons", 1);
pref("browser.tabs.selectOwnerOnClose", true);
pref("browser.ctrlTab.previews", false);
pref("browser.ctrlTab.recentlyUsedLimit", 7);
// By default, do not export HTML at shutdown.
// If true, at shutdown the bookmarks in your menu and toolbar will
@ -1371,6 +1370,9 @@ pref("identity.fxaccounts.remote.uri", "https://accounts.firefox.com/?service=sy
// should be fetched. Must use HTTPS.
pref("identity.fxaccounts.remote.force_auth.uri", "https://accounts.firefox.com/force_auth?service=sync&context=fx_desktop_v1");
// The remote content URL shown for signin in. Must use HTTPS.
pref("identity.fxaccounts.remote.signin.uri", "https://accounts.firefox.com/signin?service=sync&context=fx_desktop_v1");
// The URL we take the user to when they opt to "manage" their Firefox Account.
// Note that this will always need to be in the same TLD as the
// "identity.fxaccounts.remote.uri" pref.

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

@ -250,6 +250,9 @@ function openPrefs() {
function init() {
if (window.location.href.contains("action=signin")) {
show("remote");
wrapper.init(fxAccounts.getAccountsSignInURI());
} else if (window.location.href.contains("action=signup")) {
show("remote");
wrapper.init();
} else if (window.location.href.contains("action=reauth")) {

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

@ -161,10 +161,6 @@ var ctrlTab = {
delete this.previews;
return this.previews = this.panel.getElementsByClassName("ctrlTab-preview");
},
get recentlyUsedLimit () {
delete this.recentlyUsedLimit;
return this.recentlyUsedLimit = gPrefService.getIntPref("browser.ctrlTab.recentlyUsedLimit");
},
get keys () {
var keys = {};
["close", "find", "selectAll"].forEach(function (key) {
@ -187,31 +183,7 @@ var ctrlTab = {
get canvasHeight () Math.round(this.canvasWidth * tabPreviews.aspectRatio),
get tabList () {
if (this._tabList)
return this._tabList;
// Using gBrowser.tabs instead of gBrowser.visibleTabs, as the latter
// exlcudes closing tabs, breaking the following loop in case the the
// selected tab is closing.
let list = Array.filter(gBrowser.tabs, function (tab) !tab.hidden);
// Rotate the list until the selected tab is first
while (!list[0].selected)
list.push(list.shift());
list = list.filter(function (tab) !tab.closing);
if (this.recentlyUsedLimit != 0) {
let recentlyUsedTabs = this._recentlyUsedTabs;
if (this.recentlyUsedLimit > 0)
recentlyUsedTabs = this._recentlyUsedTabs.slice(0, this.recentlyUsedLimit);
for (let i = recentlyUsedTabs.length - 1; i >= 0; i--) {
list.splice(list.indexOf(recentlyUsedTabs[i]), 1);
list.unshift(recentlyUsedTabs[i]);
}
}
return this._tabList = list;
return this._recentlyUsedTabs;
},
init: function ctrlTab_init() {
@ -340,6 +312,9 @@ var ctrlTab = {
},
attachTab: function ctrlTab_attachTab(aTab, aPos) {
if (aTab.closing)
return;
if (aPos == 0)
this._recentlyUsedTabs.unshift(aTab);
else if (aPos)
@ -347,6 +322,7 @@ var ctrlTab = {
else
this._recentlyUsedTabs.push(aTab);
},
detachTab: function ctrlTab_detachTab(aTab) {
var i = this._recentlyUsedTabs.indexOf(aTab);
if (i >= 0)
@ -417,8 +393,6 @@ var ctrlTab = {
Array.forEach(this.previews, function (preview) {
this.updatePreview(preview, null);
}, this);
this._tabList = null;
},
onKeyPress: function ctrlTab_onKeyPress(event) {
@ -472,7 +446,6 @@ var ctrlTab = {
return;
}
this._tabList = null;
this.updatePreviews();
if (this.selected.hidden)

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

@ -46,7 +46,7 @@ var gConnectionsDialog = {
proxyTypeChanged: function ()
{
var proxyTypePref = document.getElementById("network.proxy.type");
// Update http
var httpProxyURLPref = document.getElementById("network.proxy.http");
httpProxyURLPref.disabled = proxyTypePref.value != 1;
@ -58,10 +58,11 @@ var gConnectionsDialog = {
var shareProxiesPref = document.getElementById("network.proxy.share_proxy_settings");
shareProxiesPref.disabled = proxyTypePref.value != 1;
var autologinProxyPref = document.getElementById("signon.autologin.proxy");
autologinProxyPref.disabled = proxyTypePref.value == 0;
var noProxiesPref = document.getElementById("network.proxy.no_proxies_on");
noProxiesPref.disabled = proxyTypePref.value != 1;
var autoconfigURLPref = document.getElementById("network.proxy.autoconfig_url");
autoconfigURLPref.disabled = proxyTypePref.value != 2;

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

@ -44,7 +44,10 @@
<preference id="network.proxy.share_proxy_settings"
name="network.proxy.share_proxy_settings"
type="bool"/>
<preference id="signon.autologin.proxy"
name="signon.autologin.proxy"
type="bool"/>
<preference id="pref.advanced.proxies.disable_button.reload"
name="pref.advanced.proxies.disable_button.reload"
type="bool"/>
@ -159,6 +162,13 @@
</hbox>
</radiogroup>
</groupbox>
<separator class="thin"/>
<checkbox id="autologinProxy"
label="&autologinproxy.label;"
accesskey="&autologinproxy.accesskey;"
preference="signon.autologin.proxy"
tooltiptext="&autologinproxy.tooltip;"/>
<separator/>
</prefpane>
</prefwindow>

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

@ -256,6 +256,10 @@ let gSyncPane = {
window.close();
},
signUp: function() {
this.openContentInBrowser("about:accounts?action=signup");
},
signIn: function() {
this.openContentInBrowser("about:accounts?action=signin");
},

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

@ -188,7 +188,7 @@
<vbox id="noFxaAccount">
<description>&welcome.description;</description>
<label class="text-link"
onclick="gSyncPane.signIn(); return false;"
onclick="gSyncPane.signUp(); return false;"
value="&welcome.createAccount.label;"/>
<label class="text-link"
onclick="gSyncPane.signIn(); return false;"
@ -207,7 +207,7 @@
<deck id="fxaLoginStatus">
<!-- logged in and verified and all is good -->
<hbox flex="1">
<hbox>
<label id="fxaEmailAddress1"/>
<label class="text-link"
onclick="gSyncPane.manageFirefoxAccount();"

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

@ -67,7 +67,10 @@
<p class="project-validation valid">&projects.valid;</p>
<p class="project-validation warning">&projects.warning;</p>
<p class="project-validation error">&projects.error;</p>
<p class="project-type" template='{"type":"textContent","path":"type"}'></p>
</div>
<div class="project-status" template='{"type":"attribute","path":"type","name":"type"}'>
<p class="project-type hosted">&projects.hosted;</p>
<p class="project-type packaged">&projects.packaged;</p>
</div>
</div>
<span template='{"type":"textContent","path":"manifest.developer.name"}'></span>

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

@ -133,10 +133,11 @@ Spectrum.rgbToHsv = function(r, g, b, a) {
Spectrum.getOffset = function(el) {
let curleft = 0, curtop = 0;
if (el.offsetParent) {
do {
while (el) {
curleft += el.offsetLeft;
curtop += el.offsetTop;
} while (el = el.offsetParent);
el = el.offsetParent;
}
}
return {
left: curleft,

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

@ -83,6 +83,8 @@
<!ENTITY projects.valid "Valid">
<!ENTITY projects.error "Error">
<!ENTITY projects.warning "Warning">
<!ENTITY projects.hosted "Hosted">
<!ENTITY projects.packaged "Packaged">
<!ENTITY help.title "App Manager">
<!ENTITY help.close "Close">

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

@ -44,3 +44,6 @@
<!ENTITY noproxyExplain.label "Example: .mozilla.org, .net.nz, 192.168.1.0/24">
<!ENTITY shareproxy.label "Use this proxy server for all protocols">
<!ENTITY shareproxy.accesskey "s">
<!ENTITY autologinproxy.label "Do not prompt for authentication if password is saved">
<!ENTITY autologinproxy.accesskey "a">
<!ENTITY autologinproxy.tooltip "This option silently authenticates you to proxies when you have saved credentials for them. You will be prompted if authentication fails.">

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

@ -15,15 +15,32 @@
<content>
<xul:stack>
<xul:toolbarbutton anonid="progressButton" class="circularprogressindicator-progressButton"/>
<html:div anonid="progressTrack" xbl:inherits="progress" class="circularprogressindicator-progressTrack"></html:div>
<html:canvas anonid="progressRing" xbl:inherits="progress" class="circularprogressindicator-progressRing" width="40" height="40"></html:canvas>
<xul:toolbarbutton anonid="progressButton"
class="circularprogressindicator-progressButton"/>
<html:div anonid="progressTrack"
xbl:inherits="progress"
class="circularprogressindicator-progressTrack">
</html:div>
<html:canvas anonid="progressRing"
xbl:inherits="progress"
class="circularprogressindicator-progressRing"
width="40"
height="40">
</html:canvas>
<html:div anonid="progressNotification"
xbl:inherits="progress"
class="circularprogressindicator-progressNotification">
</html:div>
</xul:stack>
</content>
<implementation>
<field name="_progressCanvas">
document.getAnonymousElementByAttribute(this, "anonid", "progressRing");
document.getAnonymousElementByAttribute(this, "anonid", "progressRing");
</field>
<field name="_progressNotification">
document.getAnonymousElementByAttribute(this, "anonid",
"progressNotification");
</field>
<field name="_progressCircleCtx">null</field>
<field name="_img">null</field>
@ -92,6 +109,26 @@
]]>
</body>
</method>
<method name="notify">
<body>
<![CDATA[
this.addEventListener("transitionend", this._onNotificationEnd);
this._progressNotification.classList.add(
"progressNotification-active");
]]>
</body>
</method>
<method name="_onNotificationEnd">
<body>
<![CDATA[
this.removeEventListener("transitionend", this._onNotificationEnd);
this._progressNotification.classList.remove(
"progressNotification-active");
]]>
</body>
</method>
</implementation>
</binding>
</bindings>

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

@ -532,7 +532,11 @@ var MetroDownloadsView = {
let notn = this._progressNotification;
if (notn)
this._notificationBox.removeNotification(notn);
ContextUI.displayNavbar();
}
this._downloadProgressIndicator.notify();
break;
case "dl-failed":
download = aSubject.QueryInterface(Ci.nsIDownload);

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

@ -7,7 +7,8 @@
.circularprogressindicator-progressButton,
.circularprogressindicator-progressRing,
.circularprogressindicator-progressTrack {
.circularprogressindicator-progressTrack,
.circularprogressindicator-progressNotification {
margin: 0 @toolbar_horizontal_spacing@;
}
@ -25,6 +26,21 @@
background-image: url(chrome://browser/skin/images/progresscircle-bg.png);
}
.circularprogressindicator-progressNotification {
width: 40px;
height: 40px;
background-image: url(chrome://browser/skin/images/navbar-download-finished.png);
visibility: hidden;
}
.circularprogressindicator-progressNotification.progressNotification-active {
visibility: visible;
opacity: 0;
transform: scale(2);
transition: opacity @metro_animation_duration@,
transform @metro_animation_duration@;
}
.circularprogressindicator-progressRing:not([progress]),
.circularprogressindicator-progressRing[progress="100"],
.circularprogressindicator-progressTrack:not([progress]),

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

@ -360,10 +360,6 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
width: 0; /* Fancy cropping solution for flexbox. */
}
#PanelUI-fxa-status[signedin] {
font-weight: bold;
}
#PanelUI-help,
#PanelUI-quit {
min-width: 46px;

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

@ -337,6 +337,14 @@ strong {
display: inline;
}
.project-type {
display: none;
margin-left: 10px;
}
[type="hosted"] > .project-type.hosted,
[type="packaged"] > .project-type.packaged {
display: inline;
}
/********* PROJECT BUTTONS ***********/

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

@ -33,8 +33,8 @@
/* We want a 4px gap between the TabsToolbar and the toolbar-menubar when the
toolbar-menu is displayed, and a 16px gap when it is not. 1px is taken care
of by the (light) outer shadow of the tab, the remaining 3/15 are these margins. */
#toolbar-menubar:not([autohide="true"]) ~ #TabsToolbar,
#toolbar-menubar[autohide="true"]:not([inactive]) ~ #TabsToolbar {
#toolbar-menubar:not([moz-collapsed=true]):not([autohide=true]) ~ #TabsToolbar,
#toolbar-menubar:not([moz-collapsed=true])[autohide=true]:not([inactive]) ~ #TabsToolbar {
margin-top: 3px;
}

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

@ -42,7 +42,7 @@ ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
aPlane.mStride > 0;
}
#if 0
#ifdef MOZ_WIDGET_GONK
static bool
IsYV12Format(const VideoData::YCbCrBuffer::Plane& aYPlane,
const VideoData::YCbCrBuffer::Plane& aCbPlane,
@ -103,6 +103,45 @@ VideoData* VideoData::ShallowCopyUpdateDuration(VideoData* aOther,
return v;
}
/* static */
void VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage,
VideoInfo& aInfo,
const YCbCrBuffer &aBuffer,
const IntRect& aPicture,
bool aCopyData)
{
if (!aVideoImage) {
return;
}
const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
PlanarYCbCrData data;
data.mYChannel = Y.mData + Y.mOffset;
data.mYSize = IntSize(Y.mWidth, Y.mHeight);
data.mYStride = Y.mStride;
data.mYSkip = Y.mSkip;
data.mCbChannel = Cb.mData + Cb.mOffset;
data.mCrChannel = Cr.mData + Cr.mOffset;
data.mCbCrSize = IntSize(Cb.mWidth, Cb.mHeight);
data.mCbCrStride = Cb.mStride;
data.mCbSkip = Cb.mSkip;
data.mCrSkip = Cr.mSkip;
data.mPicX = aPicture.x;
data.mPicY = aPicture.y;
data.mPicSize = aPicture.Size();
data.mStereoMode = aInfo.mStereoMode;
aVideoImage->SetDelayedConversion(true);
if (aCopyData) {
aVideoImage->SetData(data);
} else {
aVideoImage->SetDataNoCopy(data);
}
}
/* static */
VideoData* VideoData::Create(VideoInfo& aInfo,
ImageContainer* aContainer,
Image* aImage,
@ -164,14 +203,16 @@ VideoData* VideoData::Create(VideoInfo& aInfo,
aKeyframe,
aTimecode,
aInfo.mDisplay.ToIntSize()));
#ifdef MOZ_WIDGET_GONK
const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
#endif
if (!aImage) {
// Currently our decoder only knows how to output to ImageFormat::PLANAR_YCBCR
// format.
#if 0
#ifdef MOZ_WIDGET_GONK
if (IsYV12Format(Y, Cb, Cr)) {
v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
}
@ -191,32 +232,30 @@ VideoData* VideoData::Create(VideoInfo& aInfo,
"Wrong format?");
PlanarYCbCrImage* videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
PlanarYCbCrData data;
data.mYChannel = Y.mData + Y.mOffset;
data.mYSize = IntSize(Y.mWidth, Y.mHeight);
data.mYStride = Y.mStride;
data.mYSkip = Y.mSkip;
data.mCbChannel = Cb.mData + Cb.mOffset;
data.mCrChannel = Cr.mData + Cr.mOffset;
data.mCbCrSize = IntSize(Cb.mWidth, Cb.mHeight);
data.mCbCrStride = Cb.mStride;
data.mCbSkip = Cb.mSkip;
data.mCrSkip = Cr.mSkip;
data.mPicX = aPicture.x;
data.mPicY = aPicture.y;
data.mPicSize = aPicture.Size();
data.mStereoMode = aInfo.mStereoMode;
videoImage->SetDelayedConversion(true);
if (!aImage) {
videoImage->SetData(data);
VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
true /* aCopyData */);
} else {
videoImage->SetDataNoCopy(data);
VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
false /* aCopyData */);
}
#ifdef MOZ_WIDGET_GONK
if (!videoImage->IsValid() && !aImage && IsYV12Format(Y, Cb, Cr)) {
// Failed to allocate gralloc. Try fallback.
v->mImage = aContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
if (!v->mImage) {
return nullptr;
}
videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
true /* aCopyData */);
}
#endif
return v.forget();
}
/* static */
VideoData* VideoData::Create(VideoInfo& aInfo,
ImageContainer* aContainer,
int64_t aOffset,
@ -231,6 +270,7 @@ VideoData* VideoData::Create(VideoInfo& aInfo,
aKeyframe, aTimecode, aPicture);
}
/* static */
VideoData* VideoData::Create(VideoInfo& aInfo,
Image* aImage,
int64_t aOffset,
@ -245,6 +285,7 @@ VideoData* VideoData::Create(VideoInfo& aInfo,
aKeyframe, aTimecode, aPicture);
}
/* static */
VideoData* VideoData::CreateFromImage(VideoInfo& aInfo,
ImageContainer* aContainer,
int64_t aOffset,
@ -266,6 +307,7 @@ VideoData* VideoData::CreateFromImage(VideoInfo& aInfo,
}
#ifdef MOZ_OMX_DECODER
/* static */
VideoData* VideoData::Create(VideoInfo& aInfo,
ImageContainer* aContainer,
int64_t aOffset,

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

@ -102,6 +102,7 @@ public:
namespace layers {
class GraphicBufferLocked;
class PlanarYCbCrImage;
}
class VideoInfo;
@ -113,6 +114,7 @@ public:
typedef gfx::IntSize IntSize;
typedef layers::ImageContainer ImageContainer;
typedef layers::Image Image;
typedef layers::PlanarYCbCrImage PlanarYCbCrImage;
// YCbCr data obtained from decoding the video. The index's are:
// 0 = Y
@ -202,6 +204,14 @@ public:
static VideoData* ShallowCopyUpdateDuration(VideoData* aOther,
int64_t aDuration);
// Initialize PlanarYCbCrImage. Only When aCopyData is true,
// video data is copied to PlanarYCbCrImage.
static void SetVideoDataToImage(PlanarYCbCrImage* aVideoImage,
VideoInfo& aInfo,
const YCbCrBuffer &aBuffer,
const IntRect& aPicture,
bool aCopyData);
// Constructs a duplicate VideoData object. This intrinsically tells the
// player that it does not need to update the displayed frame when this
// frame is played; this frame is identical to the previous.

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

@ -112,12 +112,16 @@ OmxVideoTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
// Send the EOS signal to OMXCodecWrapper.
if (mEndOfStream && iter.IsEnded() && !mEosSetInEncoder) {
mEosSetInEncoder = true;
uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate;
layers::Image* img = (!mLastFrame.GetImage() || mLastFrame.GetForceBlack())
? nullptr : mLastFrame.GetImage();
mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs,
OMXCodecWrapper::BUFFER_EOS);
nsresult result = mEncoder->Encode(img, mFrameWidth, mFrameHeight,
totalDurationUs,
OMXCodecWrapper::BUFFER_EOS);
// Keep sending EOS signal until OMXVideoEncoder gets it.
if (result == NS_OK) {
mEosSetInEncoder = true;
}
}
// Dequeue an encoded frame from the output buffers of OMXCodecWrapper.

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

@ -8,6 +8,7 @@
#include "TrackEncoder.h"
#include <binder/ProcessState.h>
#include <cutils/properties.h>
#include <media/ICrypto.h>
#include <media/IOMX.h>
#include <OMX_Component.h>
@ -110,6 +111,15 @@ OMXCodecWrapper::Stop()
return result;
}
// Check system property to see if we're running on emulator.
static
bool IsRunningOnEmulator()
{
char qemu[PROPERTY_VALUE_MAX];
property_get("ro.kernel.qemu", qemu, "");
return strncmp(qemu, "1", 1) == 0;
}
nsresult
OMXVideoEncoder::Configure(int aWidth, int aHeight, int aFrameRate)
{
@ -118,6 +128,19 @@ OMXVideoEncoder::Configure(int aWidth, int aHeight, int aFrameRate)
NS_ENSURE_TRUE(aWidth > 0 && aHeight > 0 && aFrameRate > 0,
NS_ERROR_INVALID_ARG);
OMX_VIDEO_AVCLEVELTYPE level = OMX_VIDEO_AVCLevel3;
OMX_VIDEO_CONTROLRATETYPE bitrateMode = OMX_Video_ControlRateConstant;
// Limitation of soft AVC/H.264 encoder running on emulator in stagefright.
static bool emu = IsRunningOnEmulator();
if (emu) {
if (aWidth > 352 || aHeight > 288) {
CODEC_ERROR("SoftAVCEncoder doesn't support resolution larger than CIF");
return NS_ERROR_INVALID_ARG;
}
level = OMX_VIDEO_AVCLevel2;
bitrateMode = OMX_Video_ControlRateVariable;
}
// Set up configuration parameters for AVC/H.264 encoder.
sp<AMessage> format = new AMessage;
// Fixed values
@ -128,8 +151,8 @@ OMXVideoEncoder::Configure(int aWidth, int aHeight, int aFrameRate)
// height is half that of Y
format->setInt32("color-format", OMX_COLOR_FormatYUV420SemiPlanar);
format->setInt32("profile", OMX_VIDEO_AVCProfileBaseline);
format->setInt32("level", OMX_VIDEO_AVCLevel3);
format->setInt32("bitrate-mode", OMX_Video_ControlRateConstant);
format->setInt32("level", level);
format->setInt32("bitrate-mode", bitrateMode);
format->setInt32("store-metadata-in-buffers", 0);
format->setInt32("prepend-sps-pps-to-idr-frames", 0);
// Input values.

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

@ -448,7 +448,7 @@ AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID)
}
// Calculating the most important active channel.
AudioChannelType higher = AUDIO_CHANNEL_LAST;
AudioChannelType higher = AUDIO_CHANNEL_DEFAULT;
// Top-Down in the hierarchy for visible elements
if (!mChannelCounters[AUDIO_CHANNEL_INT_PUBLICNOTIFICATION].IsEmpty()) {
@ -482,30 +482,19 @@ AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID)
AudioChannelType visibleHigher = higher;
// Top-Down in the hierarchy for non-visible elements
if (higher == AUDIO_CHANNEL_LAST) {
if (!mChannelCounters[AUDIO_CHANNEL_INT_PUBLICNOTIFICATION_HIDDEN].IsEmpty()) {
higher = AUDIO_CHANNEL_PUBLICNOTIFICATION;
// And we can ignore normal channel because it can't play in the background.
for (int i = AUDIO_CHANNEL_LAST - 1;
i > higher && i > AUDIO_CHANNEL_NORMAL; i--) {
if (i == AUDIO_CHANNEL_CONTENT &&
mPlayableHiddenContentChildID != CONTENT_PROCESS_ID_UNKNOWN) {
higher = static_cast<AudioChannelType>(i);
}
else if (!mChannelCounters[AUDIO_CHANNEL_INT_RINGER_HIDDEN].IsEmpty()) {
higher = AUDIO_CHANNEL_RINGER;
}
else if (!mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].IsEmpty()) {
higher = AUDIO_CHANNEL_TELEPHONY;
}
else if (!mChannelCounters[AUDIO_CHANNEL_INT_ALARM_HIDDEN].IsEmpty()) {
higher = AUDIO_CHANNEL_ALARM;
}
else if (!mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN].IsEmpty()) {
higher = AUDIO_CHANNEL_NOTIFICATION;
}
// Check whether there is any playable hidden content channel or not.
else if (mPlayableHiddenContentChildID != CONTENT_PROCESS_ID_UNKNOWN) {
higher = AUDIO_CHANNEL_CONTENT;
// Each channel type will be split to fg and bg for recording the state,
// so here need to do a translation.
if (!mChannelCounters[i * 2 + 1].IsEmpty()) {
higher = static_cast<AudioChannelType>(i);
break;
}
}
@ -513,7 +502,7 @@ AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID)
mCurrentHigherChannel = higher;
nsString channelName;
if (mCurrentHigherChannel != AUDIO_CHANNEL_LAST) {
if (mCurrentHigherChannel != AUDIO_CHANNEL_DEFAULT) {
channelName.AssignASCII(ChannelName(mCurrentHigherChannel));
} else {
channelName.AssignLiteral("none");
@ -528,7 +517,7 @@ AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID)
mCurrentVisibleHigherChannel = visibleHigher;
nsString channelName;
if (mCurrentVisibleHigherChannel != AUDIO_CHANNEL_LAST) {
if (mCurrentVisibleHigherChannel != AUDIO_CHANNEL_DEFAULT) {
channelName.AssignASCII(ChannelName(mCurrentVisibleHigherChannel));
} else {
channelName.AssignLiteral("none");

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

@ -134,6 +134,7 @@
#include "AudioChannelService.h"
#include "JavaScriptChild.h"
#include "mozilla/dom/telephony/PTelephonyChild.h"
#include "mozilla/dom/time/DateCacheCleaner.h"
#include "mozilla/net/NeckoMessageUtils.h"
using namespace base;
@ -311,6 +312,15 @@ NS_IMPL_ISUPPORTS1(SystemMessageHandledObserver, nsIObserver)
ContentChild* ContentChild::sSingleton;
// Performs initialization that is not fork-safe, i.e. that must be done after
// forking from the Nuwa process.
static void
InitOnContentProcessCreated()
{
// This will register cross-process observer.
mozilla::dom::time::InitializeDateCacheCleaner();
}
ContentChild::ContentChild()
: mID(uint64_t(-1))
#ifdef ANDROID
@ -462,6 +472,10 @@ ContentChild::InitXPCOM()
nsRefPtr<SystemMessageHandledObserver> sysMsgObserver =
new SystemMessageHandledObserver();
sysMsgObserver->Init();
#ifndef MOZ_NUWA_PROCESS
InitOnContentProcessCreated();
#endif
}
PMemoryReportRequestChild*
@ -1623,6 +1637,10 @@ public:
toplevel = toplevel->getNext();
}
// Perform other after-fork initializations.
InitOnContentProcessCreated();
return NS_OK;
}
};

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

@ -66,7 +66,7 @@ CrashReporterParent::CrashReporterParent()
#ifdef MOZ_CRASHREPORTER
mNotes(4),
#endif
mStartTime(time(nullptr))
mStartTime(::time(nullptr))
, mInitialized(false)
{
MOZ_COUNT_CTOR(CrashReporterParent);

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

@ -79,7 +79,15 @@ NetworkStatsDB.prototype = {
// to modify the keyPath is mandatory to delete the object store
// and create it again. Old data is going to be deleted because the
// networkId for each sample can not be set.
db.deleteObjectStore(DEPRECATED_STORE_NAME);
// In version 1.2 objectStore name was 'net_stats_v2', to avoid errors when
// upgrading from 1.2 to 1.3 objectStore name should be checked.
let stores = db.objectStoreNames;
if(stores.contains("net_stats_v2")) {
db.deleteObjectStore("net_stats_v2");
} else {
db.deleteObjectStore(DEPRECATED_STORE_NAME);
}
objectStore = db.createObjectStore(DEPRECATED_STORE_NAME, { keyPath: ["appId", "network", "timestamp"] });
objectStore.createIndex("appId", "appId", { unique: false });

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

@ -22,6 +22,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Sntp.jsm");
Cu.import("resource://gre/modules/systemlibs.js");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
var RIL = {};
Cu.import("resource://gre/modules/ril_consts.js", RIL);
@ -3299,6 +3300,16 @@ RadioInterface.prototype = {
// Or refresh the SNTP.
this._sntp.request();
}
} else {
// Set a sane minimum time.
let buildTime = libcutils.property_get("ro.build.date.utc", "0") * 1000;
let file = FileUtils.File("/system/b2g/b2g");
if (file.lastModifiedTime > buildTime) {
buildTime = file.lastModifiedTime;
}
if (buildTime > Date.now()) {
gTimeService.set(buildTime);
}
}
break;
case kSettingsTimezoneAutoUpdateEnabled:

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

@ -80,7 +80,7 @@ SharedSurface_Gralloc::Create(GLContext* prodGL,
gfxImageFormat format
= gfxPlatform::GetPlatform()->OptimalFormatForContent(type);
GrallocTextureClientOGL* grallocTC =
RefPtr<GrallocTextureClientOGL> grallocTC =
new GrallocTextureClientOGL(
allocator,
gfx::ImageFormatToSurfaceFormat(format),
@ -102,7 +102,6 @@ SharedSurface_Gralloc::Create(GLContext* prodGL,
LOCAL_EGL_NATIVE_BUFFER_ANDROID,
clientBuffer, attrs);
if (!image) {
grallocTC->DropTextureData()->DeallocateSharedData(allocator);
return nullptr;
}

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

@ -92,6 +92,9 @@ GrallocImage::SetData(const Data& aData)
GraphicBuffer::USAGE_SW_WRITE_OFTEN |
GraphicBuffer::USAGE_HW_TEXTURE,
&desc);
if (desc.type() == SurfaceDescriptor::T__None) {
return;
}
mBufferAllocated = true;
mGraphicBufferLocked = new GraphicBufferLocked(desc);
}

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

@ -132,7 +132,7 @@ CanvasClientSurfaceStream::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
SharedSurface_Gralloc* grallocSurf = SharedSurface_Gralloc::Cast(surf);
GrallocTextureClientOGL* grallocTextureClient =
RefPtr<GrallocTextureClientOGL> grallocTextureClient =
static_cast<GrallocTextureClientOGL*>(grallocSurf->GetTextureClient());
// If IPDLActor is null means this TextureClient didn't AddTextureClient yet

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

@ -130,7 +130,6 @@ using namespace mozilla;
using namespace mozilla::net;
using namespace mozilla::dom;
using namespace mozilla::dom::ipc;
using namespace mozilla::dom::time;
nsrefcnt nsLayoutStatics::sLayoutStaticRefcnt = 0;
@ -280,8 +279,6 @@ nsLayoutStatics::Initialize()
nsCookieService::AppClearDataObserverInit();
nsApplicationCacheService::AppClearDataObserverInit();
InitializeDateCacheCleaner();
HTMLVideoElement::Init();
CacheObserver::Init();

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

@ -29,7 +29,7 @@ bool VolatileBuffer::Init(size_t aSize, size_t aAlignment)
#if defined(MOZ_MEMORY)
posix_memalign(&mBuf, aAlignment, aSize);
#elif defined(HAVE_POSIX_MEMALIGN)
moz_posix_memalign(&mBuf, aAlignment, aSize);
(void)moz_posix_memalign(&mBuf, aAlignment, aSize);
#else
#error "No memalign implementation found"
#endif

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

@ -46,7 +46,7 @@ VolatileBuffer::Init(size_t aSize, size_t aAlignment)
}
heap_alloc:
moz_posix_memalign(&mBuf, aAlignment, aSize);
(void)moz_posix_memalign(&mBuf, aAlignment, aSize);
mHeap = true;
return !!mBuf;
}

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

@ -79,6 +79,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
@ -890,6 +891,12 @@ abstract public class BrowserApp extends GeckoApp
// Intercept key events for gamepad shortcuts
mLayerView.setOnKeyListener(this);
// Initialize the actionbar menu items on startup for both large and small tablets
if (HardwareUtils.isTablet()) {
onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, null);
invalidateOptionsMenu();
}
}
private void shareCurrentUrl() {

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

@ -44,6 +44,13 @@ var ContextMenus = {
init: function() {
document.addEventListener("contextmenu", this, false);
document.getElementById("contextmenu-open").addEventListener("click", this.open.bind(this), false);
document.getElementById("contextmenu-retry").addEventListener("click", this.retry.bind(this), false);
document.getElementById("contextmenu-remove").addEventListener("click", this.remove.bind(this), false);
document.getElementById("contextmenu-pause").addEventListener("click", this.pause.bind(this), false);
document.getElementById("contextmenu-resume").addEventListener("click", this.resume.bind(this), false);
document.getElementById("contextmenu-cancel").addEventListener("click", this.cancel.bind(this), false);
document.getElementById("contextmenu-removeall").addEventListener("click", this.removeAll.bind(this), false);
this.items = [
{ name: "open", states: [Downloads._dlmgr.DOWNLOAD_FINISHED] },
{ name: "retry", states: [Downloads._dlmgr.DOWNLOAD_FAILED, Downloads._dlmgr.DOWNLOAD_CANCELED] },
@ -599,3 +606,8 @@ let Downloads = {
return this;
}
}
document.addEventListener("DOMContentLoaded", Downloads.init.bind(Downloads), true);
window.addEventListener("unload", Downloads.uninit.bind(Downloads), false);

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

@ -24,15 +24,15 @@
<link rel="stylesheet" href="chrome://browser/skin/aboutDownloads.css" type="text/css"/>
</head>
<body dir="&locale.dir;" onload="Downloads.init();" onunload="Downloads.uninit();">
<body dir="&locale.dir;">
<menu type="context" id="downloadmenu">
<menuitem id="contextmenu-open" label="&aboutDownloads.open;" onclick="ContextMenus.open();"></menuitem>
<menuitem id="contextmenu-retry" label="&aboutDownloads.retry;" onclick="ContextMenus.retry();"></menuitem>
<menuitem id="contextmenu-remove" label="&aboutDownloads.remove;" onclick="ContextMenus.remove();"></menuitem>
<menuitem id="contextmenu-pause" label="&aboutDownloads.pause;" onclick="ContextMenus.pause();"></menuitem>
<menuitem id="contextmenu-resume" label="&aboutDownloads.resume;" onclick="ContextMenus.resume();"></menuitem>
<menuitem id="contextmenu-cancel" label="&aboutDownloads.cancel;" onclick="ContextMenus.cancel();"></menuitem>
<menuitem id="contextmenu-removeall" label="&aboutDownloads.removeAll;" onclick="ContextMenus.removeAll();"></menuitem>
<menuitem id="contextmenu-open" label="&aboutDownloads.open;"></menuitem>
<menuitem id="contextmenu-retry" label="&aboutDownloads.retry;"></menuitem>
<menuitem id="contextmenu-remove" label="&aboutDownloads.remove;"></menuitem>
<menuitem id="contextmenu-pause" label="&aboutDownloads.pause;"></menuitem>
<menuitem id="contextmenu-resume" label="&aboutDownloads.resume;"></menuitem>
<menuitem id="contextmenu-cancel" label="&aboutDownloads.cancel;"></menuitem>
<menuitem id="contextmenu-removeall" label="&aboutDownloads.removeAll;"></menuitem>
</menu>
<div class="header">

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

@ -24,6 +24,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto",
// All properties exposed by the public FxAccounts API.
let publicProperties = [
"getAccountsSignInURI",
"getAccountsURI",
"getAssertion",
"getKeys",
@ -615,6 +616,15 @@ FxAccountsInternal.prototype = {
return url;
},
// Return the URI of the remote UI flows.
getAccountsSignInURI: function() {
let url = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.signin.uri");
if (!/^https:/.test(url)) { // Comment to un-break emacs js-mode highlighting
throw new Error("Firefox Accounts server must use HTTPS");
}
return url;
},
// Returns a promise that resolves with the URL to use to force a re-signin
// of the current account.
promiseAccountsForceSigninURI: function() {

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

@ -64,14 +64,20 @@ this.TelemetryFile = {
*
* @param {object} ping The content of the ping to save.
* @param {string} file The destination file.
* @param {bool} overwrite If |true|, the file will be overwritten
* if it exists.
* @param {bool} overwrite If |true|, the file will be overwritten if it exists,
* if |false| the file will not be overwritten and no error will be reported if
* the file exists.
* @returns {promise}
*/
savePingToFile: function(ping, file, overwrite) {
let pingString = JSON.stringify(ping);
return OS.File.writeAtomic(file, pingString, {tmpPath: file + ".tmp",
noOverwrite: !overwrite});
return Task.spawn(function*() {
try {
let pingString = JSON.stringify(ping);
yield OS.File.writeAtomic(file, pingString, {tmpPath: file + ".tmp",
noOverwrite: !overwrite});
} catch(e if e.becauseExists) {
}
})
},
/**
@ -86,7 +92,7 @@ this.TelemetryFile = {
return Task.spawn(function*() {
yield getPingDirectory();
let file = pingFilePath(ping);
return this.savePingToFile(ping, file, overwrite);
yield this.savePingToFile(ping, file, overwrite);
}.bind(this));
},
@ -98,6 +104,8 @@ this.TelemetryFile = {
*/
savePendingPings: function(sessionPing) {
let p = pendingPings.reduce((p, ping) => {
// Restore the files with the previous pings if for some reason they have
// been deleted, don't overwrite them otherwise.
p.push(this.savePing(ping, false));
return p;}, [this.savePing(sessionPing, true)]);

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

@ -789,13 +789,13 @@ let Impl = {
function handler(success) {
return function(event) {
this.finishPingRequest(success, startTime, ping);
if (success) {
deferred.resolve();
} else {
deferred.reject(event);
}
this.finishPingRequest(success, startTime, ping).then(() => {
if (success) {
deferred.resolve();
} else {
deferred.reject(event);
}
});
};
}
request.addEventListener("error", handler(false).bind(this), false);

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

@ -18,6 +18,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/LightweightThemeManager.jsm", this);
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://gre/modules/TelemetryPing.jsm", this);
Cu.import("resource://gre/modules/TelemetryFile.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
Cu.import("resource://gre/modules/Promise.jsm", this);
@ -399,6 +400,14 @@ function actualTest() {
run_next_test();
}
// Ensure that not overwriting an existing file fails silently
add_task(function* test_overwritePing() {
let ping = {slug: "foo"}
yield TelemetryFile.savePing(ping, true);
yield TelemetryFile.savePing(ping, false);
yield TelemetryFile.cleanupPingFile(ping);
});
// Ensures that expired histograms are not part of the payload.
add_task(function* test_expiredHistogram() {
let histogram_id = "FOOBAR";

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

@ -91,6 +91,9 @@
testQuickFindText();
gFindBar.close();
ok(gFindBar.hidden, "Failed to close findbar after testQuickFindText");
testFindWithHighlight();
gFindBar.close();
ok(gFindBar.hidden, "Failed to close findbar after testFindWithHighlight");
testFindbarSelection();
testDrop();
testQuickFindLink();
@ -304,6 +307,52 @@
"testQuickFindLink: failed to find sample link");
}
// See bug 963925 for more details on this test.
function testFindWithHighlight() {
//clearFocus();
gFindBar._findField.value = "";
let findCommand = document.getElementById("cmd_find");
findCommand.doCommand();
let searchStr = "e";
enterStringIntoFindField(searchStr);
let a = gFindBar._findField.value;
let b = gFindBar._browser.finder._fastFind.searchString;
let c = gFindBar._browser.finder.searchString;
ok(a == b && b == c, "testFindWithHighlight: " + a + ", " + b + ", " + c + ".");
let oldGetInitialSelection = gFindBar._getInitialSelection;
let searchStr = "t";
gFindBar._getInitialSelection = () => searchStr;
findCommand.doCommand();
gFindBar._getInitialSelection = oldGetInitialSelection;
a = gFindBar._findField.value;
b = gFindBar._browser.finder._fastFind.searchString;
c = gFindBar._browser.finder.searchString;
ok(a == searchStr && b == c, "testFindWithHighlight: " + a + ", " + b + ", " + c + ".");
let highlightButton = gFindBar.getElement("highlight");
highlightButton.click();
ok(highlightButton.checked, "testFindWithHighlight: Highlight All should be checked.");
a = gFindBar._findField.value;
b = gFindBar._browser.finder._fastFind.searchString;
c = gFindBar._browser.finder.searchString;
ok(a == searchStr && b == c, "testFindWithHighlight: " + a + ", " + b + ", " + c + ".");
gFindBar.onFindAgainCommand();
a = gFindBar._findField.value;
b = gFindBar._browser.finder._fastFind.searchString;
c = gFindBar._browser.finder.searchString;
ok(a == b && b == c, "testFindWithHighlight: " + a + ", " + b + ", " + c + ".");
highlightButton.click();
ok(!highlightButton.checked, "testFindWithHighlight: Highlight All should be unchecked.");
}
function testQuickFindText() {
clearFocus();

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

@ -123,8 +123,7 @@
<handler event="compositionend"><![CDATA[
let findbar = this.findbar;
findbar._isIMEComposing = false;
if (findbar._findMode != findbar.FIND_NORMAL &&
!findbar.hidden)
if (findbar._findMode != findbar.FIND_NORMAL)
findbar._setFindCloseTimeout();
]]></handler>
@ -410,17 +409,18 @@
if (this._quickFindTimeout)
clearTimeout(this._quickFindTimeout);
// Don't close the find toolbar while IME is composing.
if (this._isIMEComposing) {
// Don't close the find toolbar while IME is composing OR when the
// findbar is already hidden.
if (this._isIMEComposing || this.hidden) {
this._quickFindTimeout = null;
return;
}
this._quickFindTimeout =
setTimeout(function(aSelf) {
if (aSelf._findMode != aSelf.FIND_NORMAL)
aSelf.close();
}, this._quickFindTimeoutLength, this);
this._quickFindTimeout = setTimeout(() => {
if (this._findMode != this.FIND_NORMAL)
this.close();
this._quickFindTimeout = null;
}, this._quickFindTimeoutLength);
]]></body>
</method>
@ -1127,7 +1127,7 @@
else
this._findFailedString = null;
if (this._findMode != this.FIND_NORMAL && !this.hidden)
if (this._findMode != this.FIND_NORMAL)
this._setFindCloseTimeout();
]]></body>
</method>

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

@ -11,6 +11,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/devtools/SourceMap.jsm");
Cu.import("resource://gre/modules/Task.jsm");
const promise = require("sdk/core/promise");
const events = require("sdk/event/core");
@ -94,18 +95,7 @@ let StyleSheetsActor = protocol.ActorClass({
let window = this.window;
var domReady = () => {
window.removeEventListener("DOMContentLoaded", domReady, true);
let documents = [this.document];
let actors = [];
for (let doc of documents) {
let sheets = this._addStyleSheets(doc.styleSheets);
actors = actors.concat(sheets);
// Recursively handle style sheets of the documents in iframes.
for (let iframe of doc.getElementsByTagName("iframe")) {
documents.push(iframe.contentDocument);
}
}
deferred.resolve(actors);
this._addAllStyleSheets().then(deferred.resolve, Cu.reportError);
};
if (window.document.readyState === "loading") {
@ -120,29 +110,55 @@ let StyleSheetsActor = protocol.ActorClass({
response: { styleSheets: RetVal("array:stylesheet") }
}),
/**
* Add all the stylesheets in this document and its subframes.
* Assumes the document is loaded.
*
* @return {Promise}
* Promise that resolves with an array of StyleSheetActors
*/
_addAllStyleSheets: function() {
return Task.spawn(function() {
let documents = [this.document];
let actors = [];
for (let doc of documents) {
let sheets = yield this._addStyleSheets(doc.styleSheets);
actors = actors.concat(sheets);
// Recursively handle style sheets of the documents in iframes.
for (let iframe of doc.getElementsByTagName("iframe")) {
documents.push(iframe.contentDocument);
}
}
throw new Task.Result(actors);
}.bind(this));
},
/**
* Add all the stylesheets to the map and create an actor for each one
* if not already created. Send event that there are new stylesheets.
* if not already created.
*
* @param {[DOMStyleSheet]} styleSheets
* Stylesheets to add
* @return {[object]}
* Array of actors for each StyleSheetActor created
*
* @return {Promise}
* Promise that resolves to an array of StyleSheetActors
*/
_addStyleSheets: function(styleSheets)
{
let sheets = [];
for (let i = 0; i < styleSheets.length; i++) {
let styleSheet = styleSheets[i];
sheets.push(styleSheet);
return Task.spawn(function() {
let actors = [];
for (let i = 0; i < styleSheets.length; i++) {
let actor = this._createStyleSheetActor(styleSheets[i]);
actors.push(actor);
// Get all sheets, including imported ones
let imports = this._getImported(styleSheet);
sheets = sheets.concat(imports);
}
let actors = sheets.map(this._createStyleSheetActor.bind(this));
return actors;
// Get all sheets, including imported ones
let imports = yield this._getImported(actor);
actors = actors.concat(imports);
}
throw new Task.Result(actors);
}.bind(this));
},
/**
@ -150,31 +166,37 @@ let StyleSheetsActor = protocol.ActorClass({
*
* @param {DOMStyleSheet} styleSheet
* Style sheet to search
* @return {array}
* All the imported stylesheets
* @return {Promise}
* A promise that resolves with an array of StyleSheetActors
*/
_getImported: function(styleSheet) {
let imported = [];
return Task.spawn(function() {
let rules = yield styleSheet.getCSSRules();
let imported = [];
for (let i = 0; i < styleSheet.cssRules.length; i++) {
let rule = styleSheet.cssRules[i];
if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
// Associated styleSheet may be null if it has already been seen due to
// duplicate @imports for the same URL.
if (!rule.styleSheet) {
continue;
for (let i = 0; i < rules.length; i++) {
let rule = rules[i];
if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
// Associated styleSheet may be null if it has already been seen due
// to duplicate @imports for the same URL.
if (!rule.styleSheet) {
continue;
}
let actor = this._createStyleSheetActor(rule.styleSheet);
imported.push(actor);
// recurse imports in this stylesheet as well
let children = yield this._getImported(actor);
imported = imported.concat(children);
}
imported.push(rule.styleSheet);
else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {
// @import rules must precede all others except @charset
break;
}
}
// recurse imports in this stylesheet as well
imported = imported.concat(this._getImported(rule.styleSheet));
}
else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {
// @import rules must precede all others except @charset
break;
}
}
return imported;
throw new Task.Result(imported);
}.bind(this));
},
/**
@ -319,17 +341,50 @@ let StyleSheetActor = protocol.ActorClass({
this._styleSheetIndex = -1;
this._transitionRefCount = 0;
},
// if this sheet has an @import, then it's rules are loaded async
let ownerNode = this.rawSheet.ownerNode;
if (ownerNode) {
let onSheetLoaded = function(event) {
ownerNode.removeEventListener("load", onSheetLoaded, false);
this._notifyPropertyChanged("ruleCount");
}.bind(this);
ownerNode.addEventListener("load", onSheetLoaded, false);
/**
* Get the raw stylesheet's cssRules once the sheet has been loaded.
*
* @return {Promise}
* Promise that resolves with a CSSRuleList
*/
getCSSRules: function() {
let rules;
try {
rules = this.rawSheet.cssRules;
}
catch (e) {
// sheet isn't loaded yet
}
if (rules) {
return promise.resolve(rules);
}
let ownerNode = this.rawSheet.ownerNode;
if (!ownerNode) {
return promise.resolve([]);
}
if (this._cssRules) {
return this._cssRules;
}
let deferred = promise.defer();
let onSheetLoaded = function(event) {
ownerNode.removeEventListener("load", onSheetLoaded, false);
deferred.resolve(this.rawSheet.cssRules);
}.bind(this);
ownerNode.addEventListener("load", onSheetLoaded, false);
// cache so we don't add many listeners if this is called multiple times.
this._cssRules = deferred.promise;
return this._cssRules;
},
/**
@ -369,6 +424,9 @@ let StyleSheetActor = protocol.ActorClass({
}
catch(e) {
// stylesheet had an @import rule that wasn't loaded yet
this.getCSSRules().then(() => {
this._notifyPropertyChanged("ruleCount");
});
}
return form;
},

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

@ -111,11 +111,12 @@ function safeGetState(state) {
if (!state) {
return "(none)";
}
let data, string;
try {
// Evaluate state(), normalize the result into something that we can
// safely stringify or upload.
let string = JSON.stringify(state());
let data = JSON.parse(string);
string = JSON.stringify(state());
data = JSON.parse(string);
// Simplify the rest of the code by ensuring that we can simply
// concatenate the result to a message.
if (data && typeof data == "object") {
@ -125,6 +126,9 @@ function safeGetState(state) {
}
return data;
} catch (ex) {
if (string) {
return string;
}
try {
return "Error getting state: " + ex + " at " + ex.stack;
} catch (ex2) {

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

@ -36,8 +36,9 @@ Finder.prototype = {
this._listeners = this._listeners.filter(l => l != aListener);
},
_notify: function (aSearchString, aResult, aFindBackwards, aDrawOutline) {
this._searchString = aSearchString;
_notify: function (aSearchString, aResult, aFindBackwards, aDrawOutline, aStoreResult = true) {
if (aStoreResult)
this._searchString = aSearchString;
this._outlineLink(aDrawOutline);
let foundLink = this._fastFind.foundLink;
@ -62,6 +63,7 @@ Finder.prototype = {
linkURL: linkURL,
rect: this._getResultRect(),
searchString: this._searchString,
storeResult: aStoreResult
};
for (let l of this._listeners) {
@ -110,7 +112,7 @@ Finder.prototype = {
if (aHighlight) {
let result = found ? Ci.nsITypeAheadFind.FIND_FOUND
: Ci.nsITypeAheadFind.FIND_NOTFOUND;
this._notify(aWord, result, false, false);
this._notify(aWord, result, false, false, false);
}
},

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

@ -5,213 +5,213 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GonkMemoryPressureMonitoring.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Monitor.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsMemoryPressure.h"
#include "nsXULAppAPI.h"
#include "base/message_loop.h"
#include "nsThreadUtils.h"
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <android/log.h>
#define LOG(args...) \
__android_log_print(ANDROID_LOG_INFO, "GonkMemoryPressure" , ## args)
#ifdef MOZ_NUWA_PROCESS
#include "ipc/Nuwa.h"
#endif
using namespace mozilla;
namespace {
//
// MemoryPressureWatcher watches on the I/O thread for changes to the
// lowmemkiller's sysfs interface. If the system runs low on memory,
// MemoryPressureWatcher sends a MemoryPressureEvent, removes itself
// from the I/O loop, and schedules a PollTask to re-start polling
// after a timeout has been reached.
//
// The PollTask is allocated by MemoryPressureWatcher and handed over
// to the I/O loop, which then owns the object and deletes it after it
// ran. We cannot allocate the object dynamically, because the system
// is already low on memory. Instead we overload the new and delete
// operators for PollTask to hand-out statically allocated memory.
// There can only be at most one instance of PollTask at a time, so
// we can re-use the same memory on each allocation.
//
// There is a separate observer for shutdown events. When the system
// shuts down, it sends a task to the I/O thread for removing the
// watcher. If a PollTask is pending, it gets canceled. We cannot
// delete it at this point, because it's owned by the I/O loop.
//
class MemoryPressureWatcher : public MessageLoopForIO::Watcher
/**
* MemoryPressureWatcher watches sysfs from its own thread to notice when the
* system is under memory pressure. When we observe memory pressure, we use
* MemoryPressureRunnable to notify observers that they should release memory.
*
* When the system is under memory pressure, we don't want to constantly fire
* memory-pressure events. So instead, we try to detect when sysfs indicates
* that we're no longer under memory pressure, and only then start firing events
* again.
*
* (This is a bit problematic because we can't poll() to detect when we're no
* longer under memory pressure; instead we have to periodically read the sysfs
* node. If we remain under memory pressure for a long time, this means we'll
* continue waking up to read from the node for a long time, potentially wasting
* battery life. Hopefully we don't hit this case in practice! We write to
* logcat each time we go around this loop so it's at least noticable.)
*
* Shutting down safely is a bit of a chore. XPCOM won't shut down until all
* threads exit, so we need to exit the Run() method below on shutdown. But our
* thread might be blocked in one of two situations: We might be poll()'ing the
* sysfs node waiting for memory pressure to occur, or we might be asleep
* waiting to read() the sysfs node to see if we're no longer under memory
* pressure.
*
* To let us wake up from the poll(), we poll() not just the sysfs node but also
* a pipe, which we write to on shutdown. To let us wake up from sleeping
* between read()s, we sleep by Wait()'ing on a monitor, which we notify on
* shutdown.
*/
class MemoryPressureWatcher
: public nsIRunnable
, public nsIObserver
{
public:
class PollTask : public CancelableTask
MemoryPressureWatcher()
: mMonitor("MemoryPressureWatcher")
, mShuttingDown(false)
{
public:
PollTask(MemoryPressureWatcher* aWatcher)
: mWatcher(aWatcher)
{
MOZ_ASSERT(mWatcher);
}
static void* operator new(size_t aSize);
static void operator delete(void* aMem, size_t aSize);
void Run() MOZ_OVERRIDE
{
MOZ_ASSERT(MessageLoopForIO::current());
if (mWatcher) {
MOZ_ASSERT(MessageLoopForIO::current() == mWatcher->GetIOLoop());
mWatcher->StartWatching();
}
}
void Cancel() MOZ_OVERRIDE
{
mWatcher = nullptr;
}
private:
MemoryPressureWatcher* mWatcher;
};
template <size_t Size> class PollTaskAllocator
{
public:
void* Alloc()
{
MOZ_ASSERT(!sAllocated);
sAllocated = true;
return mMem;
}
void Release(void* aMem)
{
MOZ_ASSERT(mMem == aMem);
MOZ_ASSERT(sAllocated);
sAllocated = false;
}
private:
static bool sAllocated;
unsigned char mMem[Size];
};
MemoryPressureWatcher(MessageLoop* aIOLoop, uint32_t aPollMS)
: mFd(-1)
, mIOLoop(aIOLoop)
, mPollTask(nullptr)
, mPollMS(aPollMS)
, mMemoryPressure(false)
{
MOZ_ASSERT(mIOLoop);
}
virtual ~MemoryPressureWatcher()
NS_DECL_THREADSAFE_ISUPPORTS
nsresult Init()
{
MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop);
MOZ_ASSERT(mFd == -1);
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
NS_ENSURE_STATE(os);
// The observer service holds us alive.
os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, /* holdsWeak */ false);
// While we're under memory pressure, we periodically read()
// notify_trigger_active to try and see when we're no longer under memory
// pressure. mPollMS indicates how many milliseconds we wait between those
// read()s.
mPollMS = Preferences::GetUint("gonk.systemMemoryPressureRecoveryPollMS",
/* default */ 5000);
int pipes[2];
NS_ENSURE_STATE(!pipe(pipes));
mShutdownPipeRead = pipes[0];
mShutdownPipeWrite = pipes[1];
return NS_OK;
}
MessageLoop* GetIOLoop () const
NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
return mIOLoop;
}
MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
LOG("Observed XPCOM shutdown.");
nsresult Open()
{
MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop);
int fd;
MonitorAutoLock lock(mMonitor);
mShuttingDown = true;
mMonitor.Notify();
int rv;
do {
fd = open("/sys/kernel/mm/lowmemkiller/notify_trigger_active",
O_RDONLY | O_CLOEXEC);
} while (fd == -1 && errno == EINTR);
if (NS_WARN_IF(fd == -1)) {
return NS_ERROR_NOT_AVAILABLE;
}
mFd = fd;
// Write something to the pipe; doesn't matter what.
uint32_t dummy = 0;
rv = write(mShutdownPipeWrite, &dummy, sizeof(dummy));
} while(rv == -1 && errno == EINTR);
return NS_OK;
}
void Close()
NS_IMETHOD Run()
{
MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop);
MOZ_ASSERT(!NS_IsMainThread());
if (NS_WARN_IF(mFd == -1)) {
return;
#ifdef MOZ_NUWA_PROCESS
if (IsNuwaProcess()) {
NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
"NuwaMarkCurrentThread is undefined!");
NuwaMarkCurrentThread(nullptr, nullptr);
}
#endif
int res;
int lowMemFd = open("/sys/kernel/mm/lowmemkiller/notify_trigger_active",
O_RDONLY | O_CLOEXEC);
NS_ENSURE_STATE(lowMemFd != -1);
ScopedClose autoClose(lowMemFd);
do {
res = close(mFd);
} while (res == -1 && errno == EINTR);
nsresult rv = CheckForMemoryPressure(lowMemFd, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
NS_WARN_IF(res == -1);
mFd = -1;
}
while (true) {
// Wait for a notification on lowMemFd or for data to be written to
// mShutdownPipeWrite. (poll(lowMemFd, POLLPRI) blocks until we're under
// memory pressure.)
struct pollfd pollfds[2];
pollfds[0].fd = lowMemFd;
pollfds[0].events = POLLPRI;
pollfds[1].fd = mShutdownPipeRead;
pollfds[1].events = POLLIN;
void StartWatching()
{
MessageLoopForIO* ioLoop = MessageLoopForIO::current();
MOZ_ASSERT(ioLoop == mIOLoop);
ioLoop->WatchFileDescriptor(mFd, true, MessageLoopForIO::WATCH_READ,
&mReadWatcher, this);
mPollTask = nullptr;
}
int pollRv;
do {
pollRv = poll(pollfds, ArrayLength(pollfds), /* timeout */ -1);
} while (pollRv == -1 && errno == EINTR);
void StopWatching()
{
if (mPollTask) {
mPollTask->Cancel();
}
mReadWatcher.StopWatchingFileDescriptor();
}
virtual void OnFileCanWriteWithoutBlocking(int aFd) MOZ_OVERRIDE
{
MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop);
NS_WARNING("Must not write to memory monitor");
}
virtual void OnFileCanReadWithoutBlocking(int aFd) MOZ_OVERRIDE
{
MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop);
bool memoryPressure;
nsresult rv = CheckForMemoryPressure(memoryPressure);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (memoryPressure) {
LOG("Memory pressure detected.");
StopWatching();
if (mMemoryPressure) {
rv = NS_DispatchMemoryPressure(MemPressure_Ongoing);
} else {
rv = NS_DispatchMemoryPressure(MemPressure_New);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
if (pollfds[1].revents) {
// Something was written to our shutdown pipe; we're outta here.
LOG("shutting down (1)");
return NS_OK;
}
// While we're under memory pressure, we periodically read()
// notify_trigger_active to try and see when we're no longer under
// memory pressure. mPollMS indicates how many milliseconds we wait
// between those read()s.
mPollTask = new PollTask(this);
mIOLoop->PostDelayedTask(FROM_HERE, mPollTask, mPollMS);
} else if (mMemoryPressure) {
// If pollfds[1] isn't happening, pollfds[0] ought to be!
if (!(pollfds[0].revents & POLLPRI)) {
LOG("Unexpected revents value after poll(): %d. "
"Shutting down GonkMemoryPressureMonitoring.", pollfds[0].revents);
return NS_ERROR_FAILURE;
}
// POLLPRI on lowMemFd indicates that we're in a low-memory situation. We
// could read lowMemFd to double-check, but we've observed that the read
// sometimes completes after the memory-pressure event is over, so let's
// just believe the result of poll().
// We use low-memory-no-forward because each process has its own watcher
// and thus there is no need for the main process to forward this event.
rv = NS_DispatchMemoryPressure(MemPressure_New);
NS_ENSURE_SUCCESS(rv, rv);
// Manually check lowMemFd until we observe that memory pressure is over.
// We won't fire any more low-memory events until we observe that
// we're no longer under pressure. Instead, we fire low-memory-ongoing
// events, which cause processes to keep flushing caches but will not
// trigger expensive GCs and other attempts to save memory that are
// likely futile at this point.
bool memoryPressure;
do {
{
MonitorAutoLock lock(mMonitor);
// We need to check mShuttingDown before we wait here, in order to
// catch a shutdown signal sent after we poll()'ed mShutdownPipeRead
// above but before we started waiting on the monitor. But we don't
// need to check after we wait, because we'll either do another
// iteration of this inner loop, in which case we'll check
// mShuttingDown, or we'll exit this loop and do another iteration
// of the outer loop, in which case we'll check the shutdown pipe.
if (mShuttingDown) {
LOG("shutting down (2)");
return NS_OK;
}
mMonitor.Wait(PR_MillisecondsToInterval(mPollMS));
}
LOG("Checking to see if memory pressure is over.");
rv = CheckForMemoryPressure(lowMemFd, &memoryPressure);
NS_ENSURE_SUCCESS(rv, rv);
if (memoryPressure) {
rv = NS_DispatchMemoryPressure(MemPressure_Ongoing);
NS_ENSURE_SUCCESS(rv, rv);
continue;
}
} while (false);
LOG("Memory pressure is over.");
}
mMemoryPressure = memoryPressure;
return NS_OK;
}
private:
@ -222,138 +222,38 @@ private:
*
* We don't expect this method to block.
*/
nsresult CheckForMemoryPressure(bool& aOut)
nsresult CheckForMemoryPressure(int aLowMemFd, bool* aOut)
{
aOut = false;
off_t off = lseek(mFd, 0, SEEK_SET);
if (NS_WARN_IF(off)) {
return NS_ERROR_UNEXPECTED;
if (aOut) {
*aOut = false;
}
lseek(aLowMemFd, 0, SEEK_SET);
char buf[2];
int nread;
do {
nread = read(mFd, buf, sizeof(buf));
nread = read(aLowMemFd, buf, sizeof(buf));
} while(nread == -1 && errno == EINTR);
if (NS_WARN_IF(nread != 2)) {
return NS_ERROR_UNEXPECTED;
}
NS_ENSURE_STATE(nread == 2);
// The notify_trigger_active sysfs node should contain either "0\n" or
// "1\n". The latter indicates memory pressure.
aOut = buf[0] == '1' && buf[1] == '\n';
return NS_OK;
}
int mFd;
MessageLoop* mIOLoop;
MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
PollTask* mPollTask;
uint32_t mPollMS;
bool mMemoryPressure;
};
static
MemoryPressureWatcher::PollTaskAllocator<sizeof(MemoryPressureWatcher::PollTask)>
sPollTaskAllocator;
template<>
bool
MemoryPressureWatcher::PollTaskAllocator<sizeof(MemoryPressureWatcher::PollTask)>::sAllocated(false);
void*
MemoryPressureWatcher::PollTask::operator new(size_t aSize)
{
return sPollTaskAllocator.Alloc();
}
void
MemoryPressureWatcher::PollTask::operator delete(void* aMem, size_t aSize)
{
sPollTaskAllocator.Release(aMem);
}
// Initializes MemoryPressureWatcher on I/O thread
//
class InitMemoryPressureWatcherTask : public Task
{
public:
InitMemoryPressureWatcherTask(MemoryPressureWatcher* aWatcher)
: mWatcher(aWatcher)
{
MOZ_ASSERT(mWatcher);
}
void Run() MOZ_OVERRIDE
{
MOZ_ASSERT(MessageLoopForIO::current() == mWatcher->GetIOLoop());
nsresult rv = mWatcher->Open();
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
if (aOut) {
*aOut = buf[0] == '1' && buf[1] == '\n';
}
mWatcher->StartWatching();
}
private:
MemoryPressureWatcher* mWatcher;
};
// Releases MemoryPressureWatcher on I/O thread
//
class ShutdownMemoryPressureWatcherTask : public Task
{
public:
ShutdownMemoryPressureWatcherTask(MemoryPressureWatcher* aWatcher)
: mWatcher(aWatcher)
{
MOZ_ASSERT(mWatcher);
}
void Run() MOZ_OVERRIDE
{
MOZ_ASSERT(MessageLoopForIO::current() == mWatcher->GetIOLoop());
mWatcher->StopWatching();
mWatcher->Close();
}
private:
nsAutoPtr<MemoryPressureWatcher> mWatcher;
};
// Closes MemoryPressureWatcher on shutdown
//
class ShutdownObserver : public nsIObserver
{
public:
ShutdownObserver(MemoryPressureWatcher* aWatcher)
: mWatcher(aWatcher)
{
MOZ_ASSERT(mWatcher);
}
NS_DECL_THREADSAFE_ISUPPORTS
NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
LOG("Observed XPCOM shutdown.");
Task* task = new ShutdownMemoryPressureWatcherTask(mWatcher);
mWatcher->GetIOLoop()->PostTask(FROM_HERE, task);
return NS_OK;
}
private:
MemoryPressureWatcher* mWatcher;
Monitor mMonitor;
uint32_t mPollMS;
bool mShuttingDown;
ScopedClose mShutdownPipeRead;
ScopedClose mShutdownPipeWrite;
};
NS_IMPL_ISUPPORTS1(ShutdownObserver, nsIObserver);
NS_IMPL_ISUPPORTS2(MemoryPressureWatcher, nsIRunnable, nsIObserver);
} // anonymous namespace
@ -362,22 +262,13 @@ namespace mozilla {
void
InitGonkMemoryPressureMonitoring()
{
MessageLoop* ioLoop = XRE_GetIOMessageLoop();
uint32_t pollMS =
Preferences::GetUint("gonk.systemMemoryPressureRecoveryPollMS", 5000);
MemoryPressureWatcher* watcher = new MemoryPressureWatcher(ioLoop, pollMS);
// memoryPressureWatcher is held alive by the observer service.
nsRefPtr<MemoryPressureWatcher> memoryPressureWatcher =
new MemoryPressureWatcher();
NS_ENSURE_SUCCESS_VOID(memoryPressureWatcher->Init());
// Start watcher on I/O thread
Task* task = new InitMemoryPressureWatcherTask(watcher);
watcher->GetIOLoop()->PostTask(FROM_HERE, task);
// Install shutdown observer
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (NS_WARN_IF(!os)) {
return;
}
nsRefPtr<ShutdownObserver> observer = new ShutdownObserver(watcher);
os->AddObserver(observer, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
nsCOMPtr<nsIThread> thread;
NS_NewThread(getter_AddRefs(thread), memoryPressureWatcher);
}
} // namespace mozilla