зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
6ed80db29e
|
@ -12,13 +12,13 @@
|
|||
<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="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
</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="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
|
||||
<!-- Stock Android things -->
|
||||
|
|
|
@ -12,13 +12,13 @@
|
|||
<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="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"revision": "621040ee199cdde3bdd2aeaf2a016ff7806875bb",
|
||||
"revision": "d369bbb3b7c415097c7c1fa303843cf5683cd843",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
<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="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
|
||||
|
|
|
@ -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="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
<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="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
<project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
<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="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
</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="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
|
||||
<!-- Stock Android things -->
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
<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="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
|
|
@ -230,6 +230,8 @@ function init() {
|
|||
if (user) {
|
||||
show("stage");
|
||||
show("manage");
|
||||
let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
|
||||
document.title = sb.GetStringFromName("manage.pageTitle");
|
||||
} else {
|
||||
show("stage");
|
||||
show("intro");
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&syncBrand.fxa-plural.label;</title>
|
||||
<title>&aboutAccounts.pageTitle;</title>
|
||||
<meta name="viewport" content="width=device-width"/>
|
||||
|
||||
|
||||
|
@ -55,9 +55,7 @@
|
|||
|
||||
<div id="intro">
|
||||
<header>
|
||||
<h1>&syncBrand.fxa-plural.label;</h1>
|
||||
|
||||
<h2>&syncBrand.shortName.label;</h2>
|
||||
<h1>&aboutAccounts.pageTitle;</h1>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
|
|
|
@ -11,52 +11,11 @@
|
|||
#endif
|
||||
oncommand="MultiplexHandler(event)"
|
||||
#ifdef OMIT_ACCESSKEYS
|
||||
#expand onpopupshowing="CharsetMenu.build(event, '__ID_PREFIX__', false);"
|
||||
#expand onpopupshowing="CharsetMenu.build(event.target, '__ID_PREFIX__', false);"
|
||||
#else
|
||||
#expand onpopupshowing="CharsetMenu.build(event, '__ID_PREFIX__');"
|
||||
#expand onpopupshowing="CharsetMenu.build(event.target, '__ID_PREFIX__');"
|
||||
#endif
|
||||
onpopupshown="UpdateMenus(event);">
|
||||
<menupopup>
|
||||
<menu label="&charsetMenuAutodet.label;"
|
||||
#ifndef OMIT_ACCESSKEYS
|
||||
accesskey="&charsetMenuAutodet.accesskey;"
|
||||
#endif
|
||||
>
|
||||
<menupopup>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
#expand id="__ID_PREFIX__chardet.off"
|
||||
label="&charsetMenuAutodet.off.label;"
|
||||
#ifndef OMIT_ACCESSKEYS
|
||||
accesskey="&charsetMenuAutodet.off.accesskey;"
|
||||
#endif
|
||||
/>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
#expand id="__ID_PREFIX__chardet.ja_parallel_state_machine"
|
||||
label="&charsetMenuAutodet.ja.label;"
|
||||
#ifndef OMIT_ACCESSKEYS
|
||||
accesskey="&charsetMenuAutodet.ja.accesskey;"
|
||||
#endif
|
||||
/>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
#expand id="__ID_PREFIX__chardet.ruprob"
|
||||
label="&charsetMenuAutodet.ru.label;"
|
||||
#ifndef OMIT_ACCESSKEYS
|
||||
accesskey="&charsetMenuAutodet.ru.accesskey;"
|
||||
#endif
|
||||
/>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
#expand id="__ID_PREFIX__chardet.ukprob"
|
||||
label="&charsetMenuAutodet.uk.label;"
|
||||
#ifndef OMIT_ACCESSKEYS
|
||||
accesskey="&charsetMenuAutodet.uk.accesskey;"
|
||||
#endif
|
||||
/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
|
|
|
@ -6,6 +6,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
|||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
|
||||
"resource://gre/modules/FxAccounts.jsm");
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
// Ensure we don't pollute prefs for next tests.
|
||||
|
@ -47,7 +49,7 @@ let gTests = [
|
|||
ok(false, "Failed to get all commands");
|
||||
deferred.reject();
|
||||
}
|
||||
return deferred.promise;
|
||||
return deferred.promise.then(() => fxAccounts.signOut());
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -74,18 +76,19 @@ function test()
|
|||
});
|
||||
}
|
||||
|
||||
function promiseNewTabLoadEvent(aUrl, aEventType="load")
|
||||
function promiseNewTabLoadEvent(aUrl)
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(aUrl);
|
||||
tab.linkedBrowser.addEventListener(aEventType, function load(event) {
|
||||
tab.linkedBrowser.removeEventListener(aEventType, load, true);
|
||||
let iframe = tab.linkedBrowser.contentDocument.getElementById("remote");
|
||||
iframe.addEventListener("load", function frameLoad(e) {
|
||||
iframe.removeEventListener("load", frameLoad, false);
|
||||
deferred.resolve();
|
||||
}, false);
|
||||
}, true);
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
browser.addEventListener("load", function onLoad(event) {
|
||||
let iframe = browser.contentDocument.getElementById("remote");
|
||||
if (iframe && event.target == iframe.contentDocument) {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
deferred.resolve();
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,4 +35,4 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
|
|||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
|
||||
DEFINES['MENUBAR_CAN_AUTOHIDE'] = 1
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
|
|
@ -18,7 +18,14 @@
|
|||
</hbox>
|
||||
<vbox id="customization-palette" class="customization-palette" flex="1"/>
|
||||
<spacer id="customization-spacer" flex="1"/>
|
||||
<hbox>
|
||||
<hbox id="customization-footer">
|
||||
#ifdef CAN_DRAW_IN_TITLEBAR
|
||||
<button id="customization-titlebar-visibility-button" class="customizationmode-button"
|
||||
label="&customizeMode.titlebar;" type="checkbox"
|
||||
#NB: because oncommand fires after click, by the time we've fired, the checkbox binding
|
||||
# will already have switched the button's state, so this is correct:
|
||||
oncommand="gCustomizeMode.toggleTitlebar(this.hasAttribute('checked'))"/>
|
||||
#endif
|
||||
<button id="customization-toolbar-visibility-button" label="&customizeMode.toolbars;" class="customizationmode-button" type="menu">
|
||||
<menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>
|
||||
</button>
|
||||
|
|
|
@ -150,7 +150,7 @@
|
|||
<vbox id="PanelUI-characterEncodingView-customlist"
|
||||
class="PanelUI-characterEncodingView-list"/>
|
||||
<vbox>
|
||||
<label value="&charsetMenuAutodet.label;"/>
|
||||
<label id="PanelUI-characterEncodingView-autodetect-label"/>
|
||||
<vbox id="PanelUI-characterEncodingView-autodetect"
|
||||
class="PanelUI-characterEncodingView-list"/>
|
||||
</vbox>
|
||||
|
|
|
@ -16,9 +16,16 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
|
|||
"resource:///modules/PlacesUIUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RecentlyClosedTabsAndWindowsMenuUtils",
|
||||
"resource:///modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
|
||||
"resource://gre/modules/ShortcutUtils.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "CharsetManager",
|
||||
"@mozilla.org/charset-converter-manager;1",
|
||||
"nsICharsetConverterManager");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "CharsetBundle", function() {
|
||||
const kCharsetBundle = "chrome://global/locale/charsetMenu.properties";
|
||||
return Services.strings.createBundle(kCharsetBundle);
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(this, "BrandBundle", function() {
|
||||
const kBrandBundle = "chrome://branding/locale/brand.properties";
|
||||
return Services.strings.createBundle(kBrandBundle);
|
||||
|
@ -32,13 +39,26 @@ let gModuleName = "[CustomizableWidgets]";
|
|||
#include logging.js
|
||||
|
||||
function setAttributes(aNode, aAttrs) {
|
||||
let doc = aNode.ownerDocument;
|
||||
for (let [name, value] of Iterator(aAttrs)) {
|
||||
if (!value) {
|
||||
if (aNode.hasAttribute(name))
|
||||
aNode.removeAttribute(name);
|
||||
} else {
|
||||
if (name == "label" || name == "tooltiptext")
|
||||
value = CustomizableUI.getLocalizedProperty({id: aAttrs.id}, name);
|
||||
if (name == "shortcutId") {
|
||||
continue;
|
||||
}
|
||||
if (name == "label" || name == "tooltiptext") {
|
||||
let stringId = (typeof value == "string") ? value : name;
|
||||
let additionalArgs = [];
|
||||
if (aAttrs.shortcutId) {
|
||||
let shortcut = doc.getElementById(aAttrs.shortcutId);
|
||||
if (doc) {
|
||||
additionalArgs.push(ShortcutUtils.prettifyShortcut(shortcut));
|
||||
}
|
||||
}
|
||||
value = CustomizableUI.getLocalizedProperty({id: aAttrs.id}, stringId, additionalArgs);
|
||||
}
|
||||
aNode.setAttribute(name, value);
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +83,7 @@ const CustomizableWidgets = [{
|
|||
type: "view",
|
||||
viewId: "PanelUI-history",
|
||||
shortcutId: "key_gotoHistory",
|
||||
tooltiptext: "history-panelmenu.tooltiptext2",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onViewShowing: function(aEvent) {
|
||||
// Populate our list of history
|
||||
|
@ -190,6 +211,7 @@ const CustomizableWidgets = [{
|
|||
}, {
|
||||
id: "save-page-button",
|
||||
shortcutId: "key_savePage",
|
||||
tooltiptext: "save-page-button.tooltiptext2",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onCommand: function(aEvent) {
|
||||
let win = aEvent.target &&
|
||||
|
@ -202,6 +224,7 @@ const CustomizableWidgets = [{
|
|||
}, {
|
||||
id: "find-button",
|
||||
shortcutId: "key_find",
|
||||
tooltiptext: "find-button.tooltiptext2",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onCommand: function(aEvent) {
|
||||
let win = aEvent.target &&
|
||||
|
@ -214,6 +237,7 @@ const CustomizableWidgets = [{
|
|||
}, {
|
||||
id: "open-file-button",
|
||||
shortcutId: "openFileKb",
|
||||
tooltiptext: "open-file-button.tooltiptext2",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onCommand: function(aEvent) {
|
||||
let win = aEvent.target
|
||||
|
@ -289,6 +313,7 @@ const CustomizableWidgets = [{
|
|||
}, {
|
||||
id: "add-ons-button",
|
||||
shortcutId: "key_openAddons",
|
||||
tooltiptext: "add-ons-button.tooltiptext2",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onCommand: function(aEvent) {
|
||||
let win = aEvent.target &&
|
||||
|
@ -303,7 +328,14 @@ const CustomizableWidgets = [{
|
|||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
#ifdef XP_WIN
|
||||
label: "preferences-button.labelWin",
|
||||
tooltiptext: "preferences-button.tooltipWin",
|
||||
tooltiptext: "preferences-button.tooltipWin2",
|
||||
#else
|
||||
#ifdef XP_MACOSX
|
||||
tooltiptext: "preferences-button.tooltiptext.withshortcut",
|
||||
shortcutId: "key_preferencesCmdMac",
|
||||
#else
|
||||
tooltiptext: "preferences-button.tooltiptext",
|
||||
#endif
|
||||
#endif
|
||||
onCommand: function(aEvent) {
|
||||
let win = aEvent.target &&
|
||||
|
@ -334,20 +366,23 @@ const CustomizableWidgets = [{
|
|||
command: "cmd_fullZoomReduce",
|
||||
class: cls,
|
||||
label: true,
|
||||
tooltiptext: true
|
||||
tooltiptext: "tooltiptext2",
|
||||
shortcutId: "key_fullZoomReduce",
|
||||
}, {
|
||||
id: "zoom-reset-button",
|
||||
closemenu: closeMenu,
|
||||
command: "cmd_fullZoomReset",
|
||||
class: cls,
|
||||
tooltiptext: true
|
||||
tooltiptext: "tooltiptext2",
|
||||
shortcutId: "key_fullZoomReset",
|
||||
}, {
|
||||
id: "zoom-in-button",
|
||||
closemenu: closeMenu,
|
||||
command: "cmd_fullZoomEnlarge",
|
||||
class: cls,
|
||||
label: true,
|
||||
tooltiptext: true
|
||||
tooltiptext: "tooltiptext2",
|
||||
shortcutId: "key_fullZoomEnlarge",
|
||||
}];
|
||||
|
||||
let node = aDocument.createElementNS(kNSXUL, "toolbaritem");
|
||||
|
@ -490,19 +525,22 @@ const CustomizableWidgets = [{
|
|||
command: "cmd_cut",
|
||||
class: cls,
|
||||
label: true,
|
||||
tooltiptext: true
|
||||
tooltiptext: "tooltiptext2",
|
||||
shortcutId: "key_cut",
|
||||
}, {
|
||||
id: "copy-button",
|
||||
command: "cmd_copy",
|
||||
class: cls,
|
||||
label: true,
|
||||
tooltiptext: true
|
||||
tooltiptext: "tooltiptext2",
|
||||
shortcutId: "key_copy",
|
||||
}, {
|
||||
id: "paste-button",
|
||||
command: "cmd_paste",
|
||||
class: cls,
|
||||
label: true,
|
||||
tooltiptext: true
|
||||
tooltiptext: "tooltiptext2",
|
||||
shortcutId: "key_paste",
|
||||
}];
|
||||
|
||||
let node = aDocument.createElementNS(kNSXUL, "toolbaritem");
|
||||
|
@ -610,6 +648,7 @@ const CustomizableWidgets = [{
|
|||
id: "characterencoding-button",
|
||||
type: "view",
|
||||
viewId: "PanelUI-characterEncodingView",
|
||||
tooltiptext: "characterencoding-button.tooltiptext2",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
maybeDisableMenu: function(aDocument) {
|
||||
let window = aDocument.defaultView;
|
||||
|
@ -731,6 +770,11 @@ const CustomizableWidgets = [{
|
|||
onViewShowing: function(aEvent) {
|
||||
let document = aEvent.target.ownerDocument;
|
||||
|
||||
let autoDetectLabelId = "PanelUI-characterEncodingView-autodetect-label";
|
||||
let autoDetectLabel = document.getElementById(autoDetectLabelId);
|
||||
let label = CharsetBundle.GetStringFromName("charsetMenuAutodet");
|
||||
autoDetectLabel.setAttribute("value", label);
|
||||
|
||||
this.populateList(document,
|
||||
"PanelUI-characterEncodingView-customlist",
|
||||
"browser");
|
||||
|
@ -817,6 +861,7 @@ const CustomizableWidgets = [{
|
|||
}
|
||||
}, {
|
||||
id: "email-link-button",
|
||||
tooltiptext: "email-link-button.tooltiptext2",
|
||||
onCommand: function(aEvent) {
|
||||
let win = aEvent.view;
|
||||
win.MailIntegration.sendLinkForWindow(win.content);
|
||||
|
|
|
@ -16,6 +16,7 @@ const kDragDataTypePrefix = "text/toolbarwrapper-id/";
|
|||
const kPlaceholderClass = "panel-customization-placeholder";
|
||||
const kSkipSourceNodePref = "browser.uiCustomization.skipSourceNodeCheck";
|
||||
const kToolbarVisibilityBtn = "customization-toolbar-visibility-button";
|
||||
const kDrawInTitlebarPref = "browser.tabs.drawInTitlebar";
|
||||
const kMaxTransitionDurationMs = 2000;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
@ -50,6 +51,11 @@ function CustomizeMode(aWindow) {
|
|||
this.visiblePalette = this.document.getElementById(kPaletteId);
|
||||
this.paletteEmptyNotice = this.document.getElementById("customization-empty");
|
||||
this.paletteSpacer = this.document.getElementById("customization-spacer");
|
||||
#ifdef CAN_DRAW_IN_TITLEBAR
|
||||
this._updateTitlebarButton();
|
||||
Services.prefs.addObserver(kDrawInTitlebarPref, this, false);
|
||||
this.window.addEventListener("unload", this);
|
||||
#endif
|
||||
};
|
||||
|
||||
CustomizeMode.prototype = {
|
||||
|
@ -80,6 +86,12 @@ CustomizeMode.prototype = {
|
|||
return this.window.CustomizationHandler;
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
#ifdef CAN_DRAW_IN_TITLEBAR
|
||||
Services.prefs.removeObserver(kDrawInTitlebarPref, this);
|
||||
#endif
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this._handler.isEnteringCustomizeMode || this._handler.isExitingCustomizeMode) {
|
||||
this._wantToBeInCustomizeMode = !this._wantToBeInCustomizeMode;
|
||||
|
@ -967,9 +979,43 @@ CustomizeMode.prototype = {
|
|||
this.exit();
|
||||
}
|
||||
break;
|
||||
#ifdef CAN_DRAW_IN_TITLEBAR
|
||||
case "unload":
|
||||
this.uninit();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
},
|
||||
|
||||
#ifdef CAN_DRAW_IN_TITLEBAR
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "nsPref:changed":
|
||||
this._updateTitlebarButton();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_updateTitlebarButton: function() {
|
||||
let drawInTitlebar = true;
|
||||
try {
|
||||
drawInTitlebar = Services.prefs.getBoolPref(kDrawInTitlebarPref);
|
||||
} catch (ex) { }
|
||||
let button = this.document.getElementById("customization-titlebar-visibility-button");
|
||||
// Drawing in the titlebar means 'hiding' the titlebar:
|
||||
if (drawInTitlebar) {
|
||||
button.removeAttribute("checked");
|
||||
} else {
|
||||
button.setAttribute("checked", "true");
|
||||
}
|
||||
},
|
||||
|
||||
toggleTitlebar: function(aShouldShowTitlebar) {
|
||||
// Drawing in the titlebar means not showing the titlebar, hence the negation:
|
||||
Services.prefs.setBoolPref(kDrawInTitlebarPref, !aShouldShowTitlebar);
|
||||
},
|
||||
#endif
|
||||
|
||||
_onDragStart: function(aEvent) {
|
||||
__dumpDragData(aEvent);
|
||||
let item = aEvent.target;
|
||||
|
|
|
@ -9,6 +9,9 @@ EXTRA_JS_MODULES += [
|
|||
'ScrollbarSampler.jsm',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
|
||||
DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'CustomizableUI.jsm',
|
||||
'CustomizableWidgets.jsm',
|
||||
|
|
|
@ -40,22 +40,6 @@ const DOWNLOAD_VIEW_SUPPORTED_COMMANDS =
|
|||
|
||||
const NOT_AVAILABLE = Number.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* Download a URL.
|
||||
*
|
||||
* @param aURL
|
||||
* the url to download (nsIURI object)
|
||||
* @param [optional] aFileName
|
||||
* the destination file name
|
||||
*/
|
||||
function DownloadURL(aURL, aFileName) {
|
||||
// For private browsing, try to get document out of the most recent browser
|
||||
// window, or provide our own if there's no browser window.
|
||||
let browserWin = RecentWindow.getMostRecentBrowserWindow();
|
||||
let initiatingDoc = browserWin ? browserWin.document : document;
|
||||
saveURL(aURL, aFileName, null, true, true, undefined, initiatingDoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* A download element shell is responsible for handling the commands and the
|
||||
* displayed data for a single download view element. The download element
|
||||
|
@ -645,7 +629,10 @@ DownloadElementShell.prototype = {
|
|||
// In future we may try to download into the same original target uri, when
|
||||
// we have it. Though that requires verifying the path is still valid and
|
||||
// may surprise the user if he wants to be requested every time.
|
||||
DownloadURL(this.downloadURI, this.getDownloadMetaData().fileName);
|
||||
let browserWin = RecentWindow.getMostRecentBrowserWindow();
|
||||
let initiatingDoc = browserWin ? browserWin.document : document;
|
||||
DownloadURL(this.downloadURI, this.getDownloadMetaData().fileName,
|
||||
initiatingDoc);
|
||||
},
|
||||
|
||||
/* nsIController */
|
||||
|
@ -1433,7 +1420,9 @@ DownloadsPlacesView.prototype = {
|
|||
|
||||
_downloadURLFromClipboard: function DPV__downloadURLFromClipboard() {
|
||||
let [url, name] = this._getURLFromClipboardData();
|
||||
DownloadURL(url, name);
|
||||
let browserWin = RecentWindow.getMostRecentBrowserWindow();
|
||||
let initiatingDoc = browserWin ? browserWin.document : document;
|
||||
DownloadURL(url, name, initiatingDoc);
|
||||
},
|
||||
|
||||
doCommand: function DPV_doCommand(aCommand) {
|
||||
|
@ -1487,6 +1476,11 @@ DownloadsPlacesView.prototype = {
|
|||
else
|
||||
contextMenu.removeAttribute("state");
|
||||
|
||||
if (state == nsIDM.DOWNLOAD_DOWNLOADING) {
|
||||
// The resumable property of a download may change at any time, so
|
||||
// ensure we update the related command now.
|
||||
goUpdateCommand("downloadsCmd_pauseResume");
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
|
@ -1579,8 +1573,11 @@ DownloadsPlacesView.prototype = {
|
|||
|
||||
let name = { };
|
||||
let url = Services.droppedLinkHandler.dropLink(aEvent, name);
|
||||
if (url)
|
||||
DownloadURL(url, name.value);
|
||||
if (url) {
|
||||
let browserWin = RecentWindow.getMostRecentBrowserWindow();
|
||||
let initiatingDoc = browserWin ? browserWin.document : document;
|
||||
DownloadURL(url, name.value, initiatingDoc);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -505,8 +505,7 @@ const DownloadsPanel = {
|
|||
|
||||
let uri = NetUtil.newURI(url);
|
||||
DownloadsCommon.log("Pasted URL seems valid. Starting download.");
|
||||
saveURL(uri.spec, name || uri.spec, null, true, true,
|
||||
undefined, document);
|
||||
DownloadURL(uri.spec, name, document);
|
||||
} catch (ex) {}
|
||||
},
|
||||
|
||||
|
|
|
@ -220,6 +220,11 @@ let SessionHistoryListener = {
|
|||
addEventListener("load", this, true);
|
||||
addEventListener("hashchange", this, true);
|
||||
Services.obs.addObserver(this, "browser:purge-session-history", false);
|
||||
|
||||
// Collect data if we start with a non-empty shistory.
|
||||
if (!SessionHistory.isEmpty(docShell)) {
|
||||
this.collect();
|
||||
}
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
|
|
|
@ -24,6 +24,10 @@ function debug(msg) {
|
|||
* The external API exported by this module.
|
||||
*/
|
||||
this.SessionHistory = Object.freeze({
|
||||
isEmpty: function (docShell) {
|
||||
return SessionHistoryInternal.isEmpty(docShell);
|
||||
},
|
||||
|
||||
collect: function (docShell) {
|
||||
return SessionHistoryInternal.collect(docShell);
|
||||
},
|
||||
|
@ -37,6 +41,19 @@ this.SessionHistory = Object.freeze({
|
|||
* The internal API for the SessionHistory module.
|
||||
*/
|
||||
let SessionHistoryInternal = {
|
||||
/**
|
||||
* Returns whether the given docShell's session history is empty.
|
||||
*
|
||||
* @param docShell
|
||||
* The docShell that owns the session history.
|
||||
*/
|
||||
isEmpty: function (docShell) {
|
||||
let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let history = webNavigation.sessionHistory;
|
||||
let uri = webNavigation.currentURI.spec;
|
||||
return uri == "about:blank" && history.count == 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Collects session history data for a given docShell.
|
||||
*
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
- 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/. -->
|
||||
|
||||
<!ENTITY aboutAccountsSetup.description "Sign in to backup and sync your tabs, bookmarks, and more.">
|
||||
<!ENTITY aboutAccountsSetup.startButton.label "Get Started">
|
||||
<!ENTITY aboutAccountsSetup.useOldSync.label "Using Sync on an older version of &brandShortName;?">
|
||||
<!ENTITY aboutAccounts.pageTitle "Welcome to Sync">
|
||||
|
||||
<!ENTITY aboutAccountsSetup.description "Sign in to sync your tabs, bookmarks, passwords & more.">
|
||||
<!ENTITY aboutAccountsSetup.startButton.label "Get started">
|
||||
<!ENTITY aboutAccountsSetup.useOldSync.label "Using an older version of Sync?">
|
||||
|
|
|
@ -693,6 +693,7 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
|||
<!ENTITY customizeMode.menuAndToolbars.emptyLink "Choose from thousands of add-ons">
|
||||
<!ENTITY customizeMode.restoreDefaults "Restore Defaults">
|
||||
<!ENTITY customizeMode.toolbars "Show / Hide Toolbars">
|
||||
<!ENTITY customizeMode.titlebar "Title Bar">
|
||||
|
||||
<!ENTITY social.chatBar.commandkey "c">
|
||||
<!ENTITY social.chatBar.label "Focus chats">
|
||||
|
|
|
@ -3,73 +3,80 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
history-panelmenu.label = History
|
||||
# LOCALIZATION NOTE (history-panelmenu.tooltiptext): Use the unicode ellipsis char,
|
||||
# \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
history-panelmenu.tooltiptext = History… (%S)
|
||||
# LOCALIZATION NOTE(history-panelmenu.tooltiptext2): %S is the keyboard shortcut
|
||||
history-panelmenu.tooltiptext2 = Show your history (%S)
|
||||
|
||||
privatebrowsing-button.label = New Private Window
|
||||
# LOCALIZATION NOTE(privatebrowsing-button.tooltiptext): %S is the keyboard shortcut
|
||||
privatebrowsing-button.tooltiptext = Open a new Private Browsing window (%S)
|
||||
|
||||
save-page-button.label = Save Page
|
||||
# LOCALIZATION NOTE(save-page-button.tooltiptext): %S is the keyboard shortcut
|
||||
save-page-button.tooltiptext = Save this page (%S)
|
||||
# LOCALIZATION NOTE(save-page-button.tooltiptext2): %S is the keyboard shortcut
|
||||
# Use the unicode ellipsis char, \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
save-page-button.tooltiptext2 = Save this page… (%S)
|
||||
|
||||
find-button.label = Find
|
||||
# LOCALIZATION NOTE(find-button.tooltiptext): %S is the keyboard shortcut
|
||||
find-button.tooltiptext = Find in this page (%S)
|
||||
# LOCALIZATION NOTE(find-button.tooltiptext2): %S is the keyboard shortcut.
|
||||
# Use the unicode ellipsis char, \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
find-button.tooltiptext2 = Find in this page… (%S)
|
||||
|
||||
open-file-button.label = Open File
|
||||
# LOCALIZATION NOTE(open-file-button.tooltiptext): %S is the keyboard shortcut
|
||||
open-file-button.tooltiptext = Open file (%S)
|
||||
# LOCALIZATION NOTE (open-file-button.tooltiptext2): %S is the keyboard shortcut.
|
||||
# Use the unicode ellipsis char, \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
open-file-button.tooltiptext2 = Open file… (%S)
|
||||
|
||||
developer-button.label = Developer
|
||||
# LOCALIZATION NOTE(developer-button.tooltiptext): %S is the keyboard shortcut
|
||||
developer-button.tooltiptext = Web Developer Tools (%S)
|
||||
|
||||
add-ons-button.label = Add-ons
|
||||
# LOCALIZATION NOTE(add-ons-button.tooltiptext): %S is the keyboard shortcut
|
||||
add-ons-button.tooltiptext = Add-ons Manager (%S)
|
||||
# LOCALIZATION NOTE(add-ons-button.tooltiptext2): %S is the keyboard shortcut
|
||||
add-ons-button.tooltiptext2 = Open Add-ons Manager (%S)
|
||||
|
||||
switch-to-metro-button2.label = Windows 8 Touch
|
||||
# LOCALIZATION NOTE(switch-to-metro-button2.tooltiptext): %S is the brand short name
|
||||
switch-to-metro-button2.tooltiptext = Relaunch in %S for Windows 8 Touch
|
||||
|
||||
preferences-button.label = Preferences
|
||||
# LOCALIZATION NOTE (preferences-button.tooltiptext): Use the unicode ellipsis char,
|
||||
# \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
preferences-button.tooltiptext = Preferences…
|
||||
preferences-button.tooltiptext2 = Open Preferences
|
||||
preferences-button.tooltiptext.withshortcut = Open Preferences (%S)
|
||||
# LOCALIZATION NOTE (preferences-button.labelWin): Windows-only label for Options
|
||||
preferences-button.labelWin = Options
|
||||
# LOCALIZATION NOTE (preferences-button.tooltipWin): Windows-only tooltip for Options
|
||||
preferences-button.tooltipWin = Options
|
||||
preferences-button.tooltipWin2 = Open Options
|
||||
|
||||
zoom-controls.label = Zoom Controls
|
||||
zoom-controls.tooltiptext = Zoom Controls
|
||||
|
||||
zoom-out-button.label = Zoom out
|
||||
zoom-out-button.tooltiptext = Zoom out
|
||||
# LOCALIZATION NOTE(zoom-out-button.tooltiptext2): %S is the keyboard shortcut.
|
||||
zoom-out-button.tooltiptext2 = Zoom out (%S)
|
||||
|
||||
# LOCALIZATION NOTE(zoom-reset-button.label): %S is the current zoom level,
|
||||
# %% will be displayed as a single % character (% is commonly used to define
|
||||
# format specifiers, so it needs to be escaped).
|
||||
zoom-reset-button.label = %S%%
|
||||
zoom-reset-button.tooltiptext = Reset zoom level
|
||||
# LOCALIZATION NOTE(zoom-reset-button.tooltiptext2): %S is the keyboard shortcut.
|
||||
zoom-reset-button.tooltiptext2 = Reset zoom level (%S)
|
||||
|
||||
zoom-in-button.label = Zoom in
|
||||
zoom-in-button.tooltiptext = Zoom in
|
||||
# LOCALIZATION NOTE(zoom-in-button.tooltiptext2): %S is the keyboard shortcut.
|
||||
zoom-in-button.tooltiptext2 = Zoom in (%S)
|
||||
|
||||
edit-controls.label = Edit Controls
|
||||
edit-controls.tooltiptext = Edit Controls
|
||||
|
||||
cut-button.label = Cut
|
||||
cut-button.tooltiptext = Cut
|
||||
# LOCALIZATION NOTE(cut-button.tooltiptext2): %S is the keyboard shortcut.
|
||||
cut-button.tooltiptext2 = Cut (%S)
|
||||
|
||||
copy-button.label = Copy
|
||||
copy-button.tooltiptext = Copy
|
||||
# LOCALIZATION NOTE(copy-button.tooltiptext2): %S is the keyboard shortcut.
|
||||
copy-button.tooltiptext2 = Copy (%S)
|
||||
|
||||
paste-button.label = Paste
|
||||
paste-button.tooltiptext = Paste
|
||||
# LOCALIZATION NOTE(paste-button.tooltiptext2): %S is the keyboard shortcut.
|
||||
paste-button.tooltiptext2 = Paste (%S)
|
||||
|
||||
# LOCALIZATION NOTE (feed-button.tooltiptext): Use the unicode ellipsis char,
|
||||
# \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
|
@ -77,10 +84,12 @@ feed-button.label = Subscribe
|
|||
feed-button.tooltiptext = Subscribe to this page…
|
||||
|
||||
characterencoding-button.label = Character Encoding
|
||||
characterencoding-button.tooltiptext = Character encoding
|
||||
characterencoding-button.tooltiptext2 = Show Character Encoding options
|
||||
|
||||
email-link-button.label = Email Link
|
||||
email-link-button.tooltiptext = Email Link
|
||||
# LOCALIZATION NOTE (email-link-button.tooltiptext2): Use the unicode ellipsis char,
|
||||
# \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
|
||||
email-link-button.tooltiptext2 = Email Link…
|
||||
|
||||
# LOCALIZATION NOTE(quit-button.tooltiptext.linux): %S is the keyboard shortcut
|
||||
quit-button.tooltiptext.linux = Quit (%S)
|
||||
|
|
|
@ -60,3 +60,6 @@ relink.verify.title = Merge Warning
|
|||
relink.verify.heading = Are you sure you want to sign in to Sync?
|
||||
# LOCALIZATION NOTE (relink.verify.description): Email address of a user previously signed into sync.
|
||||
relink.verify.description = A different user was previously signed in to Sync on this device. Signing in will merge this browser's bookmarks, passwords and other settings with %S
|
||||
|
||||
|
||||
manage.pageTitle = Manage Sync
|
||||
|
|
|
@ -25,6 +25,7 @@ var Appbar = {
|
|||
|
||||
// tilegroup selection events for all modules get bubbled up
|
||||
window.addEventListener("selectionchange", this, false);
|
||||
Services.obs.addObserver(this, "metro_on_async_tile_created", false);
|
||||
|
||||
// gather appbar telemetry data
|
||||
try {
|
||||
|
@ -35,6 +36,14 @@ var Appbar = {
|
|||
}
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "metro_on_async_tile_created":
|
||||
this._updatePinButton();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function Appbar_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case 'URLChanged':
|
||||
|
|
|
@ -166,8 +166,8 @@ gTests.push({
|
|||
"Displays the low, high, and average FTR frame intervals when flinging a page of text.");
|
||||
PerfTest.declareNumericalResults([
|
||||
{ value: low, desc: "low" },
|
||||
{ value: high, desc: "high" },
|
||||
{ value: ave, desc: "average" }
|
||||
{ value: high, desc: "high", shareAxis: 0 },
|
||||
{ value: ave, desc: "average", shareAxis: 0 }
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
@ -213,8 +213,8 @@ gTests.push({
|
|||
"Displays the low, high, and average FTR frame intervals for about:start.");
|
||||
PerfTest.declareNumericalResults([
|
||||
{ value: results.low, desc: "low" },
|
||||
{ value: results.high, desc: "high" },
|
||||
{ value: results.ave, desc: "average" }
|
||||
{ value: results.high, desc: "high", shareAxis: 0 },
|
||||
{ value: results.ave, desc: "average", shareAxis: 0 }
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -194,6 +194,12 @@ documenttab[selected] .documenttab-selection {
|
|||
background: transparent -moz-image-rect(url("chrome://browser/skin/images/tab-overlay.png"), 50%, 100%, 100%, 0%) 50% 50% no-repeat;
|
||||
}
|
||||
|
||||
.documenttab-crop:-moz-locale-dir(rtl),
|
||||
.documenttab-selection:-moz-locale-dir(rtl),
|
||||
documenttab[selected] .documenttab-selection:-moz-locale-dir(rtl) {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.documenttab-close {
|
||||
background: none !important;
|
||||
padding: @metro_spacing_small@ !important;
|
||||
|
@ -389,8 +395,7 @@ documenttab[selected] .documenttab-selection {
|
|||
background-position: right 6px center;
|
||||
}
|
||||
|
||||
#overlay-plus:-moz-locale-dir(ltr),
|
||||
#overlay-back:-moz-locale-dir(rtl) {
|
||||
#overlay-plus:-moz-locale-dir(ltr) {
|
||||
right: -70px;
|
||||
background-position: left 6px center;
|
||||
}
|
||||
|
@ -443,12 +448,21 @@ documenttab[selected] .documenttab-selection {
|
|||
transform: translateX(40px) scale(1.2);
|
||||
}
|
||||
|
||||
#overlay-plus:-moz-locale-dir(ltr):hover,
|
||||
#overlay-back:-moz-locale-dir(rtl):hover {
|
||||
#overlay-plus:-moz-locale-dir(ltr):hover {
|
||||
background-position: left 12px center;
|
||||
transform: translateX(-40px) scale(1.2);
|
||||
}
|
||||
|
||||
#overlay-back:-moz-locale-dir(rtl):hover {
|
||||
transform: translateX(-40px) scale(1.2) scaleX(-1);
|
||||
}
|
||||
|
||||
#overlay-back:-moz-locale-dir(rtl) {
|
||||
transform: scaleX(-1);
|
||||
right: -70px;
|
||||
background-position: right 9px center;
|
||||
}
|
||||
|
||||
#overlay-back[mousedrag],
|
||||
#overlay-plus[mousedrag] {
|
||||
transition-property: left, right, transform, background-position,
|
||||
|
@ -535,6 +549,11 @@ documenttab[selected] .documenttab-selection {
|
|||
transition: opacity @forward_transition_length@ ease-out;
|
||||
}
|
||||
|
||||
#back-button:-moz-locale-dir(rtl),
|
||||
#forward-button:-moz-locale-dir(rtl) {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
#back-button[disabled] {
|
||||
visibility: visible;
|
||||
opacity: 0.5;
|
||||
|
@ -858,6 +877,10 @@ documenttab[selected] .documenttab-selection {
|
|||
-moz-box-pack: center;
|
||||
}
|
||||
|
||||
#panel-close-button:-moz-locale-dir(rtl) {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
@media (min-resolution: @min_res_140pc@) {
|
||||
#panel-close-button {
|
||||
list-style-image: url(chrome://browser/skin/images/navbar-back@1.4x.png);
|
||||
|
|
|
@ -79,6 +79,10 @@ flyoutpanel[visible] {
|
|||
-moz-image-region: rect(0 32px 32px 0);
|
||||
}
|
||||
|
||||
.flyout-close-button:-moz-locale-dir(rtl) {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.flyout-close-button:hover {
|
||||
-moz-image-region: rect(0 64px 32px 32px);
|
||||
}
|
||||
|
|
|
@ -4056,6 +4056,21 @@ window > chatbox {
|
|||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
#customization-titlebar-visibility-button {
|
||||
list-style-image: url("chrome://browser/skin/customizableui/customize-titleBar-toggle@2x.png");
|
||||
-moz-image-region: rect(0, 48px, 48px, 0);
|
||||
}
|
||||
|
||||
#customization-titlebar-visibility-button[checked] {
|
||||
-moz-image-region: rect(0, 96px, 48px, 48px);
|
||||
}
|
||||
|
||||
#customization-titlebar-visibility-button > .button-box > .button-icon {
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
/* End customization mode */
|
||||
|
||||
#main-window[privatebrowsingmode=temporary] {
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 317 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 538 B |
|
@ -24,10 +24,12 @@ browser.jar:
|
|||
* skin/classic/browser/browser-lightweightTheme.css
|
||||
skin/classic/browser/click-to-play-warning-stripes.png
|
||||
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
|
||||
skin/classic/browser/customizableui/customize-titleBar-toggle.png (customizableui/customize-titleBar-toggle.png)
|
||||
skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png (customizableui/customize-titleBar-toggle@2x.png)
|
||||
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)
|
||||
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
* skin/classic/browser/engineManager.css (engineManager.css)
|
||||
skin/classic/browser/fullscreen-darknoise.png
|
||||
skin/classic/browser/Geolocation-16.png
|
||||
|
|
|
@ -36,8 +36,9 @@
|
|||
background-color: rgb(247,247,247);
|
||||
}
|
||||
|
||||
#customization-palette-container {
|
||||
padding: 25px;
|
||||
#customization-palette,
|
||||
#customization-empty {
|
||||
padding: 0 25px 25px;
|
||||
}
|
||||
|
||||
#customization-header {
|
||||
|
@ -46,6 +47,7 @@
|
|||
color: rgb(64,100,128);
|
||||
font-weight: lighter;
|
||||
margin-bottom: 1em;
|
||||
padding: 25px 25px 0;
|
||||
}
|
||||
|
||||
#customization-panel-container {
|
||||
|
@ -53,6 +55,46 @@
|
|||
background-image: linear-gradient(to bottom, #3e86ce, #3878ba);
|
||||
}
|
||||
|
||||
#customization-footer {
|
||||
background-color: rgb(236,236,236);
|
||||
border-top: 1px solid rgb(221,221,221);
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.customizationmode-button {
|
||||
border: 1px solid rgb(192,192,192);
|
||||
border-radius: 3px;
|
||||
margin: 0;
|
||||
padding: 2px 12px;
|
||||
background-color: rgb(251,251,251);
|
||||
color: rgb(71,71,71);
|
||||
box-shadow: 0 1px rgba(255, 255, 255, 0.5),
|
||||
inset 0 1px rgba(255, 255, 255, 0.5);
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
#customization-titlebar-visibility-button {
|
||||
list-style-image: url("chrome://browser/skin/customizableui/customize-titleBar-toggle.png");
|
||||
-moz-image-region: rect(0, 24px, 24px, 0);
|
||||
padding: 2px 7px;
|
||||
-moz-margin-end: 10px;
|
||||
}
|
||||
|
||||
#customization-titlebar-visibility-button > .button-box > .button-text {
|
||||
/* Sadly, button.css thinks its margins are perfect for everyone. */
|
||||
-moz-margin-start: 6px !important;
|
||||
}
|
||||
|
||||
#customization-titlebar-visibility-button[checked] {
|
||||
-moz-image-region: rect(0, 48px, 24px, 24px);
|
||||
background-color: rgb(218, 218, 218);
|
||||
border-color: rgb(168, 168, 168);
|
||||
text-shadow: 0 1px rgb(236, 236, 236);
|
||||
box-shadow: 0 1px rgba(255, 255, 255, 0.5),
|
||||
inset 0 1px rgb(196, 196, 196);
|
||||
}
|
||||
|
||||
|
||||
#main-window[customize-entered] #customization-panel-container {
|
||||
background-image: url("chrome://browser/skin/customizableui/customizeMode-separatorHorizontal.png"),
|
||||
url("chrome://browser/skin/customizableui/customizeMode-separatorVertical.png"),
|
||||
|
|
|
@ -391,7 +391,6 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
panelview .toolbarbutton-1,
|
||||
.subviewbutton,
|
||||
.widget-overflow-list .toolbarbutton-1,
|
||||
.customizationmode-button,
|
||||
.panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button,
|
||||
#edit-controls@inAnyPanel@ > toolbarbutton,
|
||||
#zoom-controls@inAnyPanel@ > toolbarbutton {
|
||||
|
@ -450,7 +449,6 @@ panelview .toolbarbutton-1,
|
|||
panelview .toolbarbutton-1@buttonStateHover@,
|
||||
.subviewbutton@buttonStateHover@,
|
||||
.widget-overflow-list .toolbarbutton-1@buttonStateHover@,
|
||||
.customizationmode-button,
|
||||
#edit-controls@inAnyPanel@ > toolbarbutton@buttonStateHover@,
|
||||
#zoom-controls@inAnyPanel@ > toolbarbutton@buttonStateHover@ {
|
||||
background-color: hsla(210,4%,10%,.08);
|
||||
|
@ -464,7 +462,6 @@ panelview .toolbarbutton-1@buttonStateHover@,
|
|||
|
||||
panelview .toolbarbutton-1@buttonStateActive@,
|
||||
.subviewbutton@buttonStateActive@,
|
||||
.customizationmode-button@buttonStateActive@,
|
||||
.widget-overflow-list .toolbarbutton-1@buttonStateActive@,
|
||||
#edit-controls@inAnyPanel@ > toolbarbutton@buttonStateActive@,
|
||||
#zoom-controls@inAnyPanel@ > toolbarbutton@buttonStateActive@ {
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 550 B |
|
@ -26,10 +26,11 @@ browser.jar:
|
|||
* skin/classic/browser/browser-lightweightTheme.css
|
||||
skin/classic/browser/click-to-play-warning-stripes.png
|
||||
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
|
||||
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
skin/classic/browser/customizableui/customize-titleBar-toggle.png (customizableui/customize-titleBar-toggle.png)
|
||||
skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)
|
||||
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
* skin/classic/browser/engineManager.css
|
||||
skin/classic/browser/fullscreen-darknoise.png
|
||||
skin/classic/browser/Geolocation-16.png
|
||||
|
@ -352,10 +353,11 @@ browser.jar:
|
|||
* skin/classic/aero/browser/browser-lightweightTheme.css
|
||||
skin/classic/aero/browser/click-to-play-warning-stripes.png
|
||||
skin/classic/aero/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
|
||||
skin/classic/aero/browser/customizableui/customize-titleBar-toggle.png (customizableui/customize-titleBar-toggle.png)
|
||||
skin/classic/aero/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
skin/classic/aero/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
|
||||
skin/classic/aero/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
|
||||
skin/classic/aero/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)
|
||||
skin/classic/aero/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
* skin/classic/aero/browser/engineManager.css
|
||||
skin/classic/aero/browser/fullscreen-darknoise.png
|
||||
skin/classic/aero/browser/Geolocation-16.png
|
||||
|
|
|
@ -62,7 +62,8 @@ public:
|
|||
SendFileBatch(const nsAString& aDeviceAddress, BlobParent* aActor)
|
||||
: mDeviceAddress(aDeviceAddress)
|
||||
{
|
||||
mBlobs.AppendElement(aActor->GetBlob().get());
|
||||
nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
|
||||
mBlobs.AppendElement(blob);
|
||||
}
|
||||
|
||||
nsString mDeviceAddress;
|
||||
|
@ -374,7 +375,8 @@ BluetoothOppManager::AppendBlobToSend(const nsAString& aDeviceAddress,
|
|||
SendFileBatch batch(aDeviceAddress, aActor);
|
||||
mBatches.AppendElement(batch);
|
||||
} else {
|
||||
mBatches[indexTail].mBlobs.AppendElement(aActor->GetBlob().get());
|
||||
nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
|
||||
mBatches[indexTail].mBlobs.AppendElement(blob);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,8 @@ public:
|
|||
SendFileBatch(const nsAString& aDeviceAddress, BlobParent* aActor)
|
||||
: mDeviceAddress(aDeviceAddress)
|
||||
{
|
||||
mBlobs.AppendElement(aActor->GetBlob().get());
|
||||
nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
|
||||
mBlobs.AppendElement(blob);
|
||||
}
|
||||
|
||||
nsString mDeviceAddress;
|
||||
|
@ -390,7 +391,8 @@ BluetoothOppManager::AppendBlobToSend(const nsAString& aDeviceAddress,
|
|||
SendFileBatch batch(aDeviceAddress, aActor);
|
||||
mBatches.AppendElement(batch);
|
||||
} else {
|
||||
mBatches[indexTail].mBlobs.AppendElement(aActor->GetBlob().get());
|
||||
nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
|
||||
mBatches[indexTail].mBlobs.AppendElement(blob);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -189,12 +189,16 @@ GetAppPrincipal(uint32_t aAppId)
|
|||
{
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
|
||||
nsString manifestURL;
|
||||
nsresult rv = appsService->GetManifestURLByLocalId(aAppId, manifestURL);
|
||||
nsCOMPtr<mozIApplication> app;
|
||||
nsresult rv = appsService->GetAppByLocalId(aAppId, getter_AddRefs(app));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
nsString origin;
|
||||
rv = app->GetOrigin(origin);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
NS_NewURI(getter_AddRefs(uri), manifestURL);
|
||||
NS_NewURI(getter_AddRefs(uri), origin);
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> secMan =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
|
|
|
@ -164,25 +164,27 @@ mozNfc.prototype = {
|
|||
init: function init(aWindow) {
|
||||
debug("mozNfc init called");
|
||||
this._window = aWindow;
|
||||
let origin = this._window.document.nodePrincipal.origin;
|
||||
// Only System Process should listen on 'nfc-p2p-user-accept' event
|
||||
if (origin !== 'app://system.gaiamobile.org') {
|
||||
return;
|
||||
}
|
||||
let self = this;
|
||||
this._window.addEventListener("nfc-p2p-user-accept", function (event) {
|
||||
let appID = appsService.getAppLocalIdByManifestURL(event.detail.manifestUrl);
|
||||
// Notify Chrome process of User's acknowledgement
|
||||
self._nfcContentHelper.notifyUserAcceptedP2P(self._window, appID);
|
||||
});
|
||||
},
|
||||
|
||||
// Only System Process can call the following interfaces
|
||||
// 'checkP2PRegistration' , 'notifyUserAcceptedP2P' , 'notifySendFileStatus'
|
||||
checkP2PRegistration: function checkP2PRegistration(manifestUrl) {
|
||||
// Get the AppID and pass it to ContentHelper
|
||||
let appID = appsService.getAppLocalIdByManifestURL(manifestUrl);
|
||||
return this._nfcContentHelper.checkP2PRegistration(this._window, appID);
|
||||
},
|
||||
|
||||
notifyUserAcceptedP2P: function notifyUserAcceptedP2P(manifestUrl) {
|
||||
let appID = appsService.getAppLocalIdByManifestURL(manifestUrl);
|
||||
// Notify chrome process of user's acknowledgement
|
||||
this._nfcContentHelper.notifyUserAcceptedP2P(this._window, appID);
|
||||
},
|
||||
|
||||
notifySendFileStatus: function notifySendFileStatus(status, requestId) {
|
||||
this._nfcContentHelper.notifySendFileStatus(this._window,
|
||||
status, requestId);
|
||||
},
|
||||
|
||||
getNFCTag: function getNFCTag(sessionToken) {
|
||||
let obj = new MozNFCTag();
|
||||
let nfcTag = this._window.MozNFCTag._create(this._window, obj);
|
||||
|
|
|
@ -59,7 +59,8 @@ const NFC_IPC_PEER_MSG_NAMES = [
|
|||
"NFC:RegisterPeerTarget",
|
||||
"NFC:UnregisterPeerTarget",
|
||||
"NFC:CheckP2PRegistration",
|
||||
"NFC:NotifyUserAcceptedP2P"
|
||||
"NFC:NotifyUserAcceptedP2P",
|
||||
"NFC:NotifySendFileStatus"
|
||||
];
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
|
@ -309,10 +310,12 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
|||
return null;
|
||||
}
|
||||
|
||||
// Add extra permission check for below IPC Peer events:
|
||||
// 'NFC:CheckP2PRegistration' , 'NFC:NotifyUserAcceptedP2P'
|
||||
// Add extra permission check for below events:
|
||||
// 'NFC:CheckP2PRegistration' , 'NFC:NotifyUserAcceptedP2P',
|
||||
// 'NFC:NotifySendFileStatus'
|
||||
if ((msg.name == "NFC:CheckP2PRegistration") ||
|
||||
(msg.name == "NFC:NotifyUserAcceptedP2P")) {
|
||||
(msg.name == "NFC:NotifyUserAcceptedP2P") ||
|
||||
(msg.name == "NFC:NotifySendFileStatus")) {
|
||||
// ONLY privileged Content can send these events
|
||||
if (!msg.target.assertPermission("nfc-manager")) {
|
||||
debug("NFC message " + message.name +
|
||||
|
@ -356,6 +359,11 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
|||
// Notify the 'NFC_PEER_EVENT_READY' since user has acknowledged
|
||||
this.notifyPeerEvent(msg.json.appId, NFC.NFC_PEER_EVENT_READY);
|
||||
break;
|
||||
case "NFC:NotifySendFileStatus":
|
||||
// Upon receiving the status of sendFile operation, send the response
|
||||
// to appropriate content process.
|
||||
this.sendNfcResponseMessage(msg.name + "Response", msg.json);
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
|
|
@ -52,7 +52,9 @@ const NFC_IPC_MSG_NAMES = [
|
|||
"NFC:ConnectResponse",
|
||||
"NFC:CloseResponse",
|
||||
"NFC:CheckP2PRegistrationResponse",
|
||||
"NFC:PeerEvent"
|
||||
"NFC:PeerEvent",
|
||||
"NFC:NotifySendFileStatusResponse",
|
||||
"NFC:SendFileResponse"
|
||||
];
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
|
@ -235,6 +237,19 @@ NfcContentHelper.prototype = {
|
|||
return request;
|
||||
},
|
||||
|
||||
notifySendFileStatus: function notifySendFileStatus(window, status,
|
||||
requestId) {
|
||||
if (window == null) {
|
||||
throw Components.Exception("Can't get window object",
|
||||
Cr.NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
cpmm.sendAsyncMessage("NFC:NotifySendFileStatus", {
|
||||
status: status,
|
||||
requestId: requestId
|
||||
});
|
||||
},
|
||||
|
||||
registerTargetForPeerEvent: function registerTargetForPeerEvent(window,
|
||||
appId, event, callback) {
|
||||
if (window == null) {
|
||||
|
@ -341,6 +356,7 @@ NfcContentHelper.prototype = {
|
|||
case "NFC:MakeReadOnlyNDEFResponse":
|
||||
case "NFC:GetDetailsNDEFResponse":
|
||||
case "NFC:CheckP2PRegistrationResponse":
|
||||
case "NFC:NotifySendFileStatusResponse":
|
||||
this.handleResponse(message.json);
|
||||
break;
|
||||
case "NFC:PeerEvent":
|
||||
|
|
|
@ -24,7 +24,7 @@ interface nsINfcPeerCallback : nsISupports
|
|||
in DOMString sessionToken);
|
||||
};
|
||||
|
||||
[scriptable, uuid(91c2760a-f41c-4174-ad68-614840d4e201)]
|
||||
[scriptable, uuid(70cac000-7e3c-11e3-baa7-0800200c9a66)]
|
||||
interface nsINfcContentHelper : nsISupports
|
||||
{
|
||||
const long NFC_EVENT_PEER_READY = 0x01;
|
||||
|
@ -118,4 +118,21 @@ interface nsINfcContentHelper : nsISupports
|
|||
* Application ID that is capable of handling NFC_EVENT_PEER_READY event
|
||||
*/
|
||||
void notifyUserAcceptedP2P(in nsIDOMWindow window, in unsigned long appId);
|
||||
|
||||
/**
|
||||
* Notify the status of sendFile operation to Chrome process
|
||||
*
|
||||
* @param window
|
||||
* Current window
|
||||
*
|
||||
* @param status
|
||||
* Status of sendFile operation
|
||||
* (GECKO_NFC_ERROR_SUCCESS, GECKO_NFC_ERROR_GENERIC_FAILURE)
|
||||
*
|
||||
* @param requestId
|
||||
* Request ID of SendFile DOM Request
|
||||
*/
|
||||
void notifySendFileStatus(in nsIDOMWindow window,
|
||||
in octet status,
|
||||
in DOMString requestId);
|
||||
};
|
||||
|
|
|
@ -218,8 +218,7 @@ Telephony::HasDialingCall()
|
|||
{
|
||||
for (uint32_t i = 0; i < mCalls.Length(); i++) {
|
||||
const nsRefPtr<TelephonyCall>& call = mCalls[i];
|
||||
if (call->IsOutgoing() &&
|
||||
call->CallState() > nsITelephonyProvider::CALL_STATE_UNKNOWN &&
|
||||
if (call->CallState() > nsITelephonyProvider::CALL_STATE_UNKNOWN &&
|
||||
call->CallState() < nsITelephonyProvider::CALL_STATE_CONNECTED) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -46,8 +46,7 @@ TelephonyCall::TelephonyCall(nsPIDOMWindow* aOwner)
|
|||
: nsDOMEventTargetHelper(aOwner),
|
||||
mCallIndex(kOutgoingPlaceholderCallIndex),
|
||||
mCallState(nsITelephonyProvider::CALL_STATE_UNKNOWN),
|
||||
mLive(false),
|
||||
mOutgoing(false)
|
||||
mLive(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -105,10 +104,6 @@ TelephonyCall::ChangeStateInternal(uint16_t aCallState, bool aFireEvents)
|
|||
mState = stateString;
|
||||
mCallState = aCallState;
|
||||
|
||||
if (aCallState == nsITelephonyProvider::CALL_STATE_DIALING) {
|
||||
mOutgoing = true;
|
||||
}
|
||||
|
||||
if (aCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) {
|
||||
NS_ASSERTION(mLive, "Should be live!");
|
||||
if (mGroup) {
|
||||
|
|
|
@ -31,7 +31,6 @@ class TelephonyCall MOZ_FINAL : public nsDOMEventTargetHelper
|
|||
uint32_t mCallIndex;
|
||||
uint16_t mCallState;
|
||||
bool mLive;
|
||||
bool mOutgoing;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
@ -157,12 +156,6 @@ public:
|
|||
mSecondNumber = aNumber;
|
||||
}
|
||||
|
||||
bool
|
||||
IsOutgoing() const
|
||||
{
|
||||
return mOutgoing;
|
||||
}
|
||||
|
||||
void
|
||||
NotifyError(const nsAString& aError);
|
||||
|
||||
|
|
|
@ -13,11 +13,18 @@ interface MozNfcManager {
|
|||
*
|
||||
* Returns success if given manifestUrl is registered for 'onpeerready',
|
||||
* otherwise error
|
||||
*
|
||||
* Users of this API should have valid permissions 'nfc-manager'
|
||||
* and 'nfc-write'
|
||||
*/
|
||||
DOMRequest checkP2PRegistration(DOMString manifestUrl);
|
||||
|
||||
/**
|
||||
* Notify that user has accepted to share nfc message on P2P UI
|
||||
*/
|
||||
void notifyUserAcceptedP2P(DOMString manifestUrl);
|
||||
|
||||
/**
|
||||
* Notify the status of sendFile operation
|
||||
*/
|
||||
void notifySendFileStatus(octet status, DOMString requestId);
|
||||
};
|
||||
|
||||
[JSImplementation="@mozilla.org/navigatorNfc;1",
|
||||
|
|
|
@ -759,10 +759,38 @@ GetTimezoneOffset()
|
|||
return -(offset / 60);
|
||||
}
|
||||
|
||||
static int32_t sKernelTimezoneOffset = 0;
|
||||
|
||||
static void
|
||||
UpdateKernelTimezone(int32_t timezoneOffset)
|
||||
{
|
||||
if (sKernelTimezoneOffset == timezoneOffset) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell the kernel about the new time zone as well, so that FAT filesystems
|
||||
// will get local timestamps rather than UTC timestamps.
|
||||
//
|
||||
// We assume that /init.rc has a sysclktz entry so that settimeofday has
|
||||
// already been called once before we call it (there is a side-effect in
|
||||
// the kernel the very first time settimeofday is called where it does some
|
||||
// special processing if you only set the timezone).
|
||||
struct timezone tz;
|
||||
memset(&tz, 0, sizeof(tz));
|
||||
tz.tz_minuteswest = timezoneOffset;
|
||||
settimeofday(nullptr, &tz);
|
||||
sKernelTimezoneOffset = timezoneOffset;
|
||||
}
|
||||
|
||||
void
|
||||
SetTimezone(const nsCString& aTimezoneSpec)
|
||||
{
|
||||
if (aTimezoneSpec.Equals(GetTimezone())) {
|
||||
// Even though the timezone hasn't changed, we still need to tell the
|
||||
// kernel what the current timezone is. The timezone is persisted in
|
||||
// a property and doesn't change across reboots, but the kernel still
|
||||
// needs to be updated on every boot.
|
||||
UpdateKernelTimezone(GetTimezoneOffset());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -772,6 +800,7 @@ SetTimezone(const nsCString& aTimezoneSpec)
|
|||
// functions that depend on the timezone. To be safe, we call it manually.
|
||||
tzset();
|
||||
int32_t newTimezoneOffsetMinutes = GetTimezoneOffset();
|
||||
UpdateKernelTimezone(newTimezoneOffsetMinutes);
|
||||
hal::NotifySystemTimezoneChange(
|
||||
hal::SystemTimezoneChangeInformation(
|
||||
oldTimezoneOffsetMinutes, newTimezoneOffsetMinutes));
|
||||
|
|
|
@ -472,6 +472,8 @@ pref("app.creditsURL", "http://www.mozilla.org/credits/");
|
|||
pref("app.channelURL", "http://www.mozilla.org/%LOCALE%/firefox/channel/");
|
||||
#if MOZ_UPDATE_CHANNEL == aurora
|
||||
pref("app.releaseNotesURL", "http://www.mozilla.com/%LOCALE%/mobile/%VERSION%/auroranotes/");
|
||||
#elif MOZ_UPDATE_CHANNEL == beta
|
||||
pref("app.releaseNotesURL", "http://www.mozilla.com/%LOCALE%/mobile/%VERSION%beta/releasenotes/");
|
||||
#else
|
||||
pref("app.releaseNotesURL", "http://www.mozilla.com/%LOCALE%/mobile/%VERSION%/releasenotes/");
|
||||
#endif
|
||||
|
|
|
@ -49,8 +49,8 @@ this.BrowserIDManager = function BrowserIDManager() {
|
|||
// will be a promise that resolves when we are ready to authenticate
|
||||
this.whenReadyToAuthenticate = null;
|
||||
this._log = Log.repository.getLogger("Sync.BrowserIDManager");
|
||||
this._log.Level = Log.Level[Svc.Prefs.get("log.logger.identity")];
|
||||
|
||||
this._log.addAppender(new Log.DumpAppender());
|
||||
this._log.Level = Log.Level[Svc.Prefs.get("log.logger.identity")] || Log.Level.Error;
|
||||
};
|
||||
|
||||
this.BrowserIDManager.prototype = {
|
||||
|
|
|
@ -72,7 +72,7 @@ pref("services.sync.log.logger.engine.apps", "Debug");
|
|||
pref("services.sync.log.logger.userapi", "Debug");
|
||||
pref("services.sync.log.cryptoDebug", false);
|
||||
|
||||
pref("services.sync.tokenServerURI", "http://auth.oldsync.dev.lcip.org/1.0/sync/1.1");
|
||||
pref("services.sync.tokenServerURI", "https://token.services.mozilla.com/1.0/sync/1.5");
|
||||
|
||||
pref("services.sync.fxa.termsURL", "https://accounts.firefox.com/legal/terms");
|
||||
pref("services.sync.fxa.privacyURL", "https://accounts.firefox.com/legal/privacy");
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
|
||||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["TelemetryFile"];
|
||||
|
@ -7,20 +12,13 @@ const Ci = Components.interfaces;
|
|||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
let imports = {};
|
||||
Cu.import("resource://gre/modules/Services.jsm", imports);
|
||||
Cu.import("resource://gre/modules/Deprecated.jsm", imports);
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm", imports);
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/Deprecated.jsm", this);
|
||||
Cu.import("resource://gre/modules/osfile.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
|
||||
let {Services, Deprecated, NetUtil} = imports;
|
||||
|
||||
// Constants from prio.h for nsIFileOutputStream.init
|
||||
const PR_WRONLY = 0x2;
|
||||
const PR_CREATE_FILE = 0x8;
|
||||
const PR_TRUNCATE = 0x20;
|
||||
const PR_EXCL = 0x80;
|
||||
const RW_OWNER = parseInt("0600", 8);
|
||||
const RWX_OWNER = parseInt("0700", 8);
|
||||
const Telemetry = Services.telemetry;
|
||||
|
||||
// Files that have been lying around for longer than MAX_PING_FILE_AGE are
|
||||
// deleted without being loaded.
|
||||
|
@ -34,9 +32,6 @@ const OVERDUE_PING_FILE_AGE = 7 * 24 * 60 * 60 * 1000; // 1 week
|
|||
// requests for.
|
||||
let pingsLoaded = 0;
|
||||
|
||||
// The number of those requests that have actually completed.
|
||||
let pingLoadsCompleted = 0;
|
||||
|
||||
// The number of pings that we have destroyed due to being older
|
||||
// than MAX_PING_FILE_AGE.
|
||||
let pingsDiscarded = 0;
|
||||
|
@ -45,13 +40,11 @@ let pingsDiscarded = 0;
|
|||
// but younger than MAX_PING_FILE_AGE.
|
||||
let pingsOverdue = 0;
|
||||
|
||||
// If |true|, send notifications "telemetry-test-save-complete"
|
||||
// and "telemetry-test-load-complete" once save/load is complete.
|
||||
let shouldNotifyUponSave = false;
|
||||
|
||||
// Data that has neither been saved nor sent by ping
|
||||
let pendingPings = [];
|
||||
|
||||
let isPingDirectoryCreated = false;
|
||||
|
||||
this.TelemetryFile = {
|
||||
|
||||
get MAX_PING_FILE_AGE() {
|
||||
|
@ -62,94 +55,64 @@ this.TelemetryFile = {
|
|||
return OVERDUE_PING_FILE_AGE;
|
||||
},
|
||||
|
||||
get pingDirectoryPath() {
|
||||
return OS.Path.join(OS.Constants.Path.profileDir, "saved-telemetry-pings");
|
||||
},
|
||||
|
||||
/**
|
||||
* Save a single ping to a file.
|
||||
*
|
||||
* @param {object} ping The content of the ping to save.
|
||||
* @param {nsIFile} file The destination file.
|
||||
* @param {bool} sync If |true|, write synchronously. Deprecated.
|
||||
* This argument should be |false|.
|
||||
* @param {string} file The destination file.
|
||||
* @param {bool} overwrite If |true|, the file will be overwritten
|
||||
* if it exists.
|
||||
* @returns {promise}
|
||||
*/
|
||||
savePingToFile: function(ping, file, sync, overwrite) {
|
||||
savePingToFile: function(ping, file, overwrite) {
|
||||
let pingString = JSON.stringify(ping);
|
||||
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
let ostream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
let initFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
|
||||
if (!overwrite) {
|
||||
initFlags |= PR_EXCL;
|
||||
}
|
||||
try {
|
||||
ostream.init(file, initFlags, RW_OWNER, 0);
|
||||
} catch (e) {
|
||||
// Probably due to PR_EXCL.
|
||||
return;
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
let utf8String = converter.ConvertFromUnicode(pingString);
|
||||
utf8String += converter.Finish();
|
||||
let success = false;
|
||||
try {
|
||||
let amount = ostream.write(utf8String, utf8String.length);
|
||||
success = amount == utf8String.length;
|
||||
} catch (e) {
|
||||
}
|
||||
finishTelemetrySave(success, ostream);
|
||||
} else {
|
||||
let istream = converter.convertToInputStream(pingString);
|
||||
let self = this;
|
||||
NetUtil.asyncCopy(istream, ostream,
|
||||
function(result) {
|
||||
finishTelemetrySave(Components.isSuccessCode(result),
|
||||
ostream);
|
||||
});
|
||||
}
|
||||
return OS.File.writeAtomic(file, pingString, {tmpPath: file + ".tmp",
|
||||
noOverwrite: !overwrite});
|
||||
},
|
||||
|
||||
/**
|
||||
* Save a ping to its file, synchronously.
|
||||
* Save a ping to its file.
|
||||
*
|
||||
* @param {object} ping The content of the ping to save.
|
||||
* @param {bool} overwrite If |true|, the file will be overwritten
|
||||
* if it exists.
|
||||
* @returns {promise}
|
||||
*/
|
||||
savePing: function(ping, overwrite) {
|
||||
this.savePingToFile(ping,
|
||||
getSaveFileForPing(ping), true, overwrite);
|
||||
return Task.spawn(function*() {
|
||||
yield getPingDirectory();
|
||||
let file = pingFilePath(ping);
|
||||
return this.savePingToFile(ping, file, overwrite);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Save all pending pings, synchronously.
|
||||
* Save all pending pings.
|
||||
*
|
||||
* @param {object} sessionPing The additional session ping.
|
||||
* @returns {promise}
|
||||
*/
|
||||
savePendingPings: function(sessionPing) {
|
||||
this.savePing(sessionPing, true);
|
||||
pendingPings.forEach(function sppcb(e, i, a) {
|
||||
this.savePing(e, false);
|
||||
}, this);
|
||||
let p = pendingPings.reduce((p, ping) => {
|
||||
p.push(this.savePing(ping, false));
|
||||
return p;}, [this.savePing(sessionPing, true)]);
|
||||
|
||||
pendingPings = [];
|
||||
return Promise.all(p);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the file for a ping
|
||||
*
|
||||
* @param {object} ping The ping.
|
||||
* @returns {promise}
|
||||
*/
|
||||
cleanupPingFile: function(ping) {
|
||||
// FIXME: We shouldn't create the directory just to remove the file.
|
||||
let file = getSaveFileForPing(ping);
|
||||
try {
|
||||
file.remove(true); // FIXME: Should be |false|, isn't it?
|
||||
} catch(e) {
|
||||
}
|
||||
return OS.File.remove(pingFilePath(ping));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -158,24 +121,26 @@ this.TelemetryFile = {
|
|||
* Once loaded, the saved pings can be accessed (destructively only)
|
||||
* through |popPendingPings|.
|
||||
*
|
||||
* @param {bool} sync If |true|, loading takes place synchronously.
|
||||
* @param {function*} onLoad A function called upon loading of each
|
||||
* ping. It is passed |true| in case of success, |false| in case of
|
||||
* format error.
|
||||
* @returns {promise}
|
||||
*/
|
||||
loadSavedPings: function(sync, onLoad = null, onDone = null) {
|
||||
let directory = ensurePingDirectory();
|
||||
let entries = directory.directoryEntries
|
||||
.QueryInterface(Ci.nsIDirectoryEnumerator);
|
||||
pingsLoaded = 0;
|
||||
pingLoadsCompleted = 0;
|
||||
try {
|
||||
while (entries.hasMoreElements()) {
|
||||
this.loadHistograms(entries.nextFile, sync, onLoad, onDone);
|
||||
loadSavedPings: function() {
|
||||
return Task.spawn(function*() {
|
||||
let directory = TelemetryFile.pingDirectoryPath;
|
||||
let iter = new OS.File.DirectoryIterator(directory);
|
||||
let exists = yield iter.exists();
|
||||
|
||||
if (exists) {
|
||||
let entries = yield iter.nextBatch();
|
||||
yield iter.close();
|
||||
|
||||
let p = [e for (e of entries) if (!e.isDir)].
|
||||
map((e) => this.loadHistograms(e.path));
|
||||
|
||||
yield Promise.all(p);
|
||||
}
|
||||
} finally {
|
||||
entries.close();
|
||||
}
|
||||
|
||||
yield iter.close();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -184,43 +149,26 @@ this.TelemetryFile = {
|
|||
* Once loaded, the saved pings can be accessed (destructively only)
|
||||
* through |popPendingPings|.
|
||||
*
|
||||
* @param {nsIFile} file The file to load.
|
||||
* @param {bool} sync If |true|, loading takes place synchronously.
|
||||
* @param {function*} onLoad A function called upon loading of the
|
||||
* ping. It is passed |true| in case of success, |false| in case of
|
||||
* format error.
|
||||
* @param {string} file The file to load.
|
||||
* @returns {promise}
|
||||
*/
|
||||
loadHistograms: function loadHistograms(file, sync, onLoad = null, onDone = null) {
|
||||
let now = Date.now();
|
||||
if (now - file.lastModifiedTime > MAX_PING_FILE_AGE) {
|
||||
// We haven't had much luck in sending this file; delete it.
|
||||
file.remove(true);
|
||||
pingsDiscarded++;
|
||||
return;
|
||||
}
|
||||
loadHistograms: function loadHistograms(file) {
|
||||
return OS.File.stat(file).then(function(info){
|
||||
let now = Date.now();
|
||||
if (now - info.lastModificationDate > MAX_PING_FILE_AGE) {
|
||||
// We haven't had much luck in sending this file; delete it.
|
||||
pingsDiscarded++;
|
||||
return OS.File.remove(file);
|
||||
}
|
||||
|
||||
// This file is a bit stale, and overdue for sending.
|
||||
if (now - file.lastModifiedTime > OVERDUE_PING_FILE_AGE) {
|
||||
pingsOverdue++;
|
||||
}
|
||||
// This file is a bit stale, and overdue for sending.
|
||||
if (now - info.lastModificationDate > OVERDUE_PING_FILE_AGE) {
|
||||
pingsOverdue++;
|
||||
}
|
||||
|
||||
pingsLoaded++;
|
||||
if (sync) {
|
||||
let stream = Cc["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Ci.nsIFileInputStream);
|
||||
stream.init(file, -1, -1, 0);
|
||||
addToPendingPings(file, stream, onLoad, onDone);
|
||||
} else {
|
||||
let channel = NetUtil.newChannel(file);
|
||||
channel.contentType = "application/json";
|
||||
|
||||
NetUtil.asyncFetch(channel, (function(stream, result) {
|
||||
if (!Components.isSuccessCode(result)) {
|
||||
return;
|
||||
}
|
||||
addToPendingPings(file, stream, onLoad, onDone);
|
||||
}).bind(this));
|
||||
}
|
||||
pingsLoaded++;
|
||||
return addToPendingPings(file);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -251,7 +199,7 @@ this.TelemetryFile = {
|
|||
*
|
||||
* @return {iterator}
|
||||
*/
|
||||
popPendingPings: function(reason) {
|
||||
popPendingPings: function*(reason) {
|
||||
while (pendingPings.length > 0) {
|
||||
let data = pendingPings.pop();
|
||||
// Send persisted pings to the test URL too.
|
||||
|
@ -262,74 +210,53 @@ this.TelemetryFile = {
|
|||
}
|
||||
},
|
||||
|
||||
set shouldNotifyUponSave(value) {
|
||||
shouldNotifyUponSave = value;
|
||||
},
|
||||
|
||||
testLoadHistograms: function(file, sync, onLoad) {
|
||||
testLoadHistograms: function(file) {
|
||||
pingsLoaded = 0;
|
||||
pingLoadsCompleted = 0;
|
||||
this.loadHistograms(file, sync, onLoad);
|
||||
return this.loadHistograms(file.path);
|
||||
}
|
||||
};
|
||||
|
||||
///// Utility functions
|
||||
function pingFilePath(ping) {
|
||||
return OS.Path.join(TelemetryFile.pingDirectoryPath, ping.slug);
|
||||
}
|
||||
|
||||
function getSaveFileForPing(ping) {
|
||||
let file = ensurePingDirectory();
|
||||
file.append(ping.slug);
|
||||
return file;
|
||||
};
|
||||
function getPingDirectory() {
|
||||
return Task.spawn(function*() {
|
||||
let directory = TelemetryFile.pingDirectoryPath;
|
||||
|
||||
function ensurePingDirectory() {
|
||||
let directory = Services.dirsvc.get("ProfD", Ci.nsILocalFile).clone();
|
||||
directory.append("saved-telemetry-pings");
|
||||
try {
|
||||
directory.create(Ci.nsIFile.DIRECTORY_TYPE, RWX_OWNER);
|
||||
} catch (e) {
|
||||
// Already exists, just ignore this.
|
||||
}
|
||||
return directory;
|
||||
};
|
||||
|
||||
function addToPendingPings(file, stream, onLoad, onDone) {
|
||||
let success = false;
|
||||
|
||||
try {
|
||||
let string = NetUtil.readInputStreamToString(stream, stream.available(),
|
||||
{ charset: "UTF-8" });
|
||||
stream.close();
|
||||
let ping = JSON.parse(string);
|
||||
// The ping's payload used to be stringified JSON. Deal with that.
|
||||
if (typeof(ping.payload) == "string") {
|
||||
ping.payload = JSON.parse(ping.payload);
|
||||
if (!isPingDirectoryCreated) {
|
||||
yield OS.File.makeDir(directory, { unixMode: OS.Constants.S_IRWXU });
|
||||
isPingDirectoryCreated = true;
|
||||
}
|
||||
pingLoadsCompleted++;
|
||||
pendingPings.push(ping);
|
||||
success = true;
|
||||
} catch (e) {
|
||||
// An error reading the file, or an error parsing the contents.
|
||||
stream.close(); // close is idempotent.
|
||||
file.remove(true); // FIXME: Should be false, isn't it?
|
||||
|
||||
return directory;
|
||||
});
|
||||
}
|
||||
|
||||
function addToPendingPings(file) {
|
||||
function onLoad(success) {
|
||||
let success_histogram = Telemetry.getHistogramById("READ_SAVED_PING_SUCCESS");
|
||||
success_histogram.add(success);
|
||||
}
|
||||
|
||||
if (onLoad) {
|
||||
onLoad(success);
|
||||
}
|
||||
return Task.spawn(function*() {
|
||||
try {
|
||||
let array = yield OS.File.read(file);
|
||||
let decoder = new TextDecoder();
|
||||
let string = decoder.decode(array);
|
||||
|
||||
if (pingLoadsCompleted == pingsLoaded) {
|
||||
if (onDone) {
|
||||
onDone();
|
||||
let ping = JSON.parse(string);
|
||||
// The ping's payload used to be stringified JSON. Deal with that.
|
||||
if (typeof(ping.payload) == "string") {
|
||||
ping.payload = JSON.parse(ping.payload);
|
||||
}
|
||||
|
||||
pendingPings.push(ping);
|
||||
onLoad(true);
|
||||
} catch (e) {
|
||||
onLoad(false);
|
||||
yield OS.File.remove(file);
|
||||
}
|
||||
if (shouldNotifyUponSave) {
|
||||
Services.obs.notifyObservers(null, "telemetry-test-load-complete", null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function finishTelemetrySave(ok, stream) {
|
||||
stream.close();
|
||||
if (shouldNotifyUponSave && ok) {
|
||||
Services.obs.notifyObservers(null, "telemetry-test-save-complete", null);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,13 +10,15 @@ const Ci = Components.interfaces;
|
|||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/debug.js");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/debug.js", this);
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
Cu.import("resource://gre/modules/LightweightThemeManager.jsm", this);
|
||||
#endif
|
||||
Cu.import("resource://gre/modules/ThirdPartyCookieProbe.jsm");
|
||||
Cu.import("resource://gre/modules/ThirdPartyCookieProbe.jsm", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
|
||||
// When modifying the payload in incompatible ways, please bump this version number
|
||||
const PAYLOAD_VERSION = 1;
|
||||
|
@ -127,30 +129,57 @@ let processInfo = {
|
|||
this.EXPORTED_SYMBOLS = ["TelemetryPing"];
|
||||
|
||||
this.TelemetryPing = Object.freeze({
|
||||
/**
|
||||
* Returns the current telemetry payload.
|
||||
* @returns Object
|
||||
*/
|
||||
getPayload: function() {
|
||||
return Impl.getPayload();
|
||||
},
|
||||
saveHistograms: function(aFile, aSync) {
|
||||
return Impl.saveHistograms(aFile, aSync);
|
||||
/**
|
||||
* Save histograms to a file.
|
||||
* Used only for testing purposes.
|
||||
*
|
||||
* @param {nsIFile} aFile The file to load from.
|
||||
*/
|
||||
testSaveHistograms: function(aFile) {
|
||||
return Impl.testSaveHistograms(aFile);
|
||||
},
|
||||
/**
|
||||
* Collect and store information about startup.
|
||||
*/
|
||||
gatherStartup: function() {
|
||||
return Impl.gatherStartup();
|
||||
},
|
||||
enableLoadSaveNotifications: function() {
|
||||
return Impl.enableLoadSaveNotifications();
|
||||
},
|
||||
cacheProfileDirectory: function() {
|
||||
return Impl.cacheProfileDirectory();
|
||||
},
|
||||
/**
|
||||
* Inform the ping which AddOns are installed.
|
||||
*
|
||||
* @param aAddOns - The AddOns.
|
||||
*/
|
||||
setAddOns: function(aAddOns) {
|
||||
return Impl.setAddOns(aAddOns);
|
||||
},
|
||||
/**
|
||||
* Send a ping to a test server. Used only for testing.
|
||||
*
|
||||
* @param aServer - The server.
|
||||
*/
|
||||
testPing: function(aServer) {
|
||||
return Impl.testPing(aServer);
|
||||
},
|
||||
testLoadHistograms: function(aFile, aSync) {
|
||||
return Impl.testLoadHistograms(aFile, aSync);
|
||||
/**
|
||||
* Load histograms from a file.
|
||||
* Used only for testing purposes.
|
||||
*
|
||||
* @param aFile - File to load from.
|
||||
*/
|
||||
testLoadHistograms: function(aFile) {
|
||||
return Impl.testLoadHistograms(aFile);
|
||||
},
|
||||
/**
|
||||
* Returns the path component of the current submission URL.
|
||||
* @returns String
|
||||
*/
|
||||
submissionPath: function() {
|
||||
return Impl.submissionPath();
|
||||
},
|
||||
|
@ -164,13 +193,13 @@ this.TelemetryPing = Object.freeze({
|
|||
*/
|
||||
reset: function() {
|
||||
this.uninstall();
|
||||
this.setup();
|
||||
return this.setup();
|
||||
},
|
||||
/**
|
||||
* Used only for testing purposes.
|
||||
*/
|
||||
setup: function() {
|
||||
Impl.setup(true);
|
||||
return Impl.setup(true);
|
||||
},
|
||||
/**
|
||||
* Used only for testing purposes.
|
||||
|
@ -697,46 +726,15 @@ let Impl = {
|
|||
send: function send(reason, server) {
|
||||
// populate histograms one last time
|
||||
this.gatherMemory();
|
||||
this.sendPingsFromIterator(server, reason,
|
||||
return this.sendPingsFromIterator(server, reason,
|
||||
Iterator(this.popPayloads(reason)));
|
||||
},
|
||||
|
||||
/**
|
||||
* What we want to do is the following:
|
||||
*
|
||||
* for data in getPayloads(reason):
|
||||
* if sending ping data to server failed:
|
||||
* break;
|
||||
*
|
||||
* but we can't do that, since XMLHttpRequest is async. What we do
|
||||
* instead is let this function control the essential looping logic
|
||||
* and provide callbacks for XMLHttpRequest when a request has
|
||||
* finished.
|
||||
*/
|
||||
sendPingsFromIterator: function sendPingsFromIterator(server, reason, i) {
|
||||
function finishPings(reason) {
|
||||
if (reason == "test-ping") {
|
||||
Services.obs.notifyObservers(null, "telemetry-test-xhr-complete", null);
|
||||
}
|
||||
}
|
||||
let p = [data for (data in i)].map((data) =>
|
||||
this.doPing(server, data).then(null, () => TelemetryFile.savePing(data, true)));
|
||||
|
||||
let data = null;
|
||||
try {
|
||||
data = i.next();
|
||||
} catch (e if e instanceof StopIteration) {
|
||||
finishPings(reason);
|
||||
return;
|
||||
}
|
||||
function onSuccess() {
|
||||
this.sendPingsFromIterator(server, reason, i);
|
||||
}
|
||||
function onError() {
|
||||
TelemetryFile.savePing(data, true);
|
||||
// Notify that testing is complete, even if we didn't send everything.
|
||||
finishPings(reason);
|
||||
}
|
||||
this.doPing(server, data,
|
||||
onSuccess.bind(this), onError.bind(this));
|
||||
return Promise.all(p);
|
||||
},
|
||||
|
||||
finishPingRequest: function finishPingRequest(success, startTime, ping) {
|
||||
|
@ -747,7 +745,9 @@ let Impl = {
|
|||
hping.add(new Date() - startTime);
|
||||
|
||||
if (success) {
|
||||
TelemetryFile.cleanupPingFile(ping);
|
||||
return TelemetryFile.cleanupPingFile(ping);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -765,7 +765,8 @@ let Impl = {
|
|||
return "/submit/telemetry/" + slug;
|
||||
},
|
||||
|
||||
doPing: function doPing(server, ping, onSuccess, onError) {
|
||||
doPing: function doPing(server, ping) {
|
||||
let deferred = Promise.defer();
|
||||
let url = server + this.submissionPath(ping);
|
||||
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
|
@ -776,14 +777,19 @@ let Impl = {
|
|||
|
||||
let startTime = new Date();
|
||||
|
||||
function handler(success, callback) {
|
||||
function handler(success) {
|
||||
return function(event) {
|
||||
this.finishPingRequest(success, startTime, ping);
|
||||
callback();
|
||||
|
||||
if (success) {
|
||||
deferred.resolve();
|
||||
} else {
|
||||
deferred.reject(event);
|
||||
}
|
||||
};
|
||||
}
|
||||
request.addEventListener("error", handler(false, onError).bind(this), false);
|
||||
request.addEventListener("load", handler(true, onSuccess).bind(this), false);
|
||||
request.addEventListener("error", handler(false).bind(this), false);
|
||||
request.addEventListener("load", handler(true).bind(this), false);
|
||||
|
||||
request.setRequestHeader("Content-Encoding", "gzip");
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
|
@ -795,6 +801,7 @@ let Impl = {
|
|||
.createInstance(Ci.nsIStringInputStream);
|
||||
payloadStream.data = this.gzipCompressString(utf8Payload);
|
||||
request.send(payloadStream);
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
gzipCompressString: function gzipCompressString(string) {
|
||||
|
@ -899,41 +906,39 @@ let Impl = {
|
|||
// run various late initializers. Otherwise our gathered memory
|
||||
// footprint and other numbers would be too optimistic.
|
||||
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
function timerCallback() {
|
||||
this._initialized = true;
|
||||
TelemetryFile.loadSavedPings(false, (success =>
|
||||
{
|
||||
let success_histogram = Telemetry.getHistogramById("READ_SAVED_PING_SUCCESS");
|
||||
success_histogram.add(success);
|
||||
}), () =>
|
||||
{
|
||||
// If we have any TelemetryPings lying around, we'll be aggressive
|
||||
// and try to send them all off ASAP.
|
||||
if (TelemetryFile.pingsOverdue > 0) {
|
||||
// It doesn't really matter what we pass to this.send as a reason,
|
||||
// since it's never sent to the server. All that this.send does with
|
||||
// the reason is check to make sure it's not a test-ping.
|
||||
this.send("overdue-flush", this._server);
|
||||
}
|
||||
});
|
||||
this.attachObservers();
|
||||
this.gatherMemory();
|
||||
let deferred = Promise.defer();
|
||||
|
||||
Telemetry.asyncFetchTelemetryData(function () {
|
||||
});
|
||||
delete this._timer;
|
||||
function timerCallback() {
|
||||
Task.spawn(function*(){
|
||||
this._initialized = true;
|
||||
|
||||
yield TelemetryFile.loadSavedPings();
|
||||
// If we have any TelemetryPings lying around, we'll be aggressive
|
||||
// and try to send them all off ASAP.
|
||||
if (TelemetryFile.pingsOverdue > 0) {
|
||||
// It doesn't really matter what we pass to this.send as a reason,
|
||||
// since it's never sent to the server. All that this.send does with
|
||||
// the reason is check to make sure it's not a test-ping.
|
||||
yield this.send("overdue-flush", this._server);
|
||||
}
|
||||
|
||||
this.attachObservers();
|
||||
this.gatherMemory();
|
||||
|
||||
Telemetry.asyncFetchTelemetryData(function () {});
|
||||
delete this._timer;
|
||||
deferred.resolve();
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
this._timer.initWithCallback(timerCallback.bind(this),
|
||||
aTesting ? TELEMETRY_TEST_DELAY : TELEMETRY_DELAY,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
testLoadHistograms: function testLoadHistograms(file, sync) {
|
||||
TelemetryFile.testLoadHistograms(file, sync, (success =>
|
||||
{
|
||||
let success_histogram = Telemetry.getHistogramById("READ_SAVED_PING_SUCCESS");
|
||||
success_histogram.add(success);
|
||||
}));
|
||||
testLoadHistograms: function testLoadHistograms(file) {
|
||||
return TelemetryFile.testLoadHistograms(file);
|
||||
},
|
||||
|
||||
getFlashVersion: function getFlashVersion() {
|
||||
|
@ -950,13 +955,12 @@ let Impl = {
|
|||
|
||||
savePendingPings: function savePendingPings() {
|
||||
let sessionPing = this.getSessionPayloadAndSlug("saved-session");
|
||||
TelemetryFile.savePendingPings(sessionPing);
|
||||
return TelemetryFile.savePendingPings(sessionPing);
|
||||
},
|
||||
|
||||
saveHistograms: function saveHistograms(file, sync) {
|
||||
TelemetryFile.savePingToFile(
|
||||
this.getSessionPayloadAndSlug("saved-session"),
|
||||
file, sync, true);
|
||||
testSaveHistograms: function testSaveHistograms(file) {
|
||||
return TelemetryFile.savePingToFile(this.getSessionPayloadAndSlug("saved-session"),
|
||||
file.path, true);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1000,10 +1004,6 @@ let Impl = {
|
|||
this._slowSQLStartup = Telemetry.slowSQL;
|
||||
},
|
||||
|
||||
enableLoadSaveNotifications: function enableLoadSaveNotifications() {
|
||||
TelemetryFile.shouldNotifyUponSave = true;
|
||||
},
|
||||
|
||||
setAddOns: function setAddOns(aAddOns) {
|
||||
this._addons = aAddOns;
|
||||
},
|
||||
|
@ -1014,19 +1014,14 @@ let Impl = {
|
|||
this._isIdleObserver = false;
|
||||
}
|
||||
if (aTest) {
|
||||
this.send("test-ping", aServer);
|
||||
return this.send("test-ping", aServer);
|
||||
} else if (Telemetry.canSend) {
|
||||
this.send("idle-daily", aServer);
|
||||
return this.send("idle-daily", aServer);
|
||||
}
|
||||
},
|
||||
|
||||
testPing: function testPing(server) {
|
||||
this.sendIdlePing(true, server);
|
||||
},
|
||||
|
||||
cacheProfileDirectory: function cacheProfileDirectory() {
|
||||
// This method doesn't do anything anymore
|
||||
return;
|
||||
return this.sendIdlePing(true, server);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1035,8 +1030,7 @@ let Impl = {
|
|||
observe: function (aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "profile-after-change":
|
||||
this.setup();
|
||||
break;
|
||||
return this.setup();
|
||||
case "cycle-collector-begin":
|
||||
let now = new Date();
|
||||
if (!gLastMemoryPoll
|
||||
|
@ -1079,9 +1073,10 @@ let Impl = {
|
|||
case "profile-before-change2":
|
||||
this.uninstall();
|
||||
if (Telemetry.canSend) {
|
||||
this.savePendingPings();
|
||||
return this.savePendingPings();
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// On Android, we can get killed without warning once we are in the background,
|
||||
|
@ -1107,5 +1102,5 @@ let Impl = {
|
|||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -13,11 +13,13 @@ const Ci = Components.interfaces;
|
|||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://testing-common/httpd.js", this);
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryPing.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/Task.jsm", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
|
||||
const IGNORE_HISTOGRAM = "test::ignore_me";
|
||||
const IGNORE_HISTOGRAM_TO_CLONE = "MEMORY_HEAP_ALLOCATED";
|
||||
|
@ -36,45 +38,27 @@ const PR_TRUNCATE = 0x20;
|
|||
const RW_OWNER = 0600;
|
||||
|
||||
const NUMBER_OF_THREADS_TO_LAUNCH = 30;
|
||||
var gNumberOfThreadsLaunched = 0;
|
||||
let gNumberOfThreadsLaunched = 0;
|
||||
|
||||
const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
|
||||
|
||||
var httpserver = new HttpServer();
|
||||
var serverStarted = false;
|
||||
var gFinished = false;
|
||||
let gHttpServer = new HttpServer();
|
||||
let gServerStarted = false;
|
||||
let gRequestIterator = null;
|
||||
|
||||
function test_expired_histogram() {
|
||||
var histogram_id = "FOOBAR";
|
||||
var dummy = Telemetry.newHistogram(histogram_id, "30", 1, 2, 3, Telemetry.HISTOGRAM_EXPONENTIAL);
|
||||
|
||||
dummy.add(1);
|
||||
|
||||
do_check_eq(TelemetryPing.getPayload()["histograms"][histogram_id], undefined);
|
||||
do_check_eq(TelemetryPing.getPayload()["histograms"]["TELEMETRY_TEST_EXPIRED"], undefined);
|
||||
}
|
||||
|
||||
function telemetry_ping () {
|
||||
function sendPing () {
|
||||
TelemetryPing.gatherStartup();
|
||||
TelemetryPing.enableLoadSaveNotifications();
|
||||
TelemetryPing.cacheProfileDirectory();
|
||||
if (serverStarted) {
|
||||
TelemetryPing.testPing("http://localhost:" + httpserver.identity.primaryPort);
|
||||
if (gServerStarted) {
|
||||
return TelemetryPing.testPing("http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
} else {
|
||||
TelemetryPing.testPing("http://doesnotexist");
|
||||
return TelemetryPing.testPing("http://doesnotexist");
|
||||
}
|
||||
}
|
||||
|
||||
// Mostly useful so that you can dump payloads from decodeRequestPayload.
|
||||
function dummyHandler(request, response) {
|
||||
let p = decodeRequestPayload(request);
|
||||
return p;
|
||||
}
|
||||
|
||||
function wrapWithExceptionHandler(f) {
|
||||
function wrapper() {
|
||||
function wrapper(...args) {
|
||||
try {
|
||||
f.apply(null, arguments);
|
||||
f(...args);
|
||||
} catch (ex if typeof(ex) == 'object') {
|
||||
dump("Caught exception: " + ex.message + "\n");
|
||||
dump(ex.stack);
|
||||
|
@ -84,29 +68,11 @@ function wrapWithExceptionHandler(f) {
|
|||
return wrapper;
|
||||
}
|
||||
|
||||
function addWrappedObserver(f, topic) {
|
||||
let wrappedObserver = wrapWithExceptionHandler(f);
|
||||
Services.obs.addObserver(function g(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(g, aTopic);
|
||||
wrappedObserver(aSubject, aTopic, aData);
|
||||
}, topic, false);
|
||||
}
|
||||
|
||||
function registerPingHandler(handler) {
|
||||
httpserver.registerPrefixHandler("/submit/telemetry/",
|
||||
gHttpServer.registerPrefixHandler("/submit/telemetry/",
|
||||
wrapWithExceptionHandler(handler));
|
||||
}
|
||||
|
||||
function nonexistentServerObserver(aSubject, aTopic, aData) {
|
||||
httpserver.start(-1);
|
||||
serverStarted = true;
|
||||
|
||||
// Provide a dummy function so it returns 200 instead of 404 to telemetry.
|
||||
registerPingHandler(dummyHandler);
|
||||
addWrappedObserver(telemetryObserver, "telemetry-test-xhr-complete");
|
||||
telemetry_ping();
|
||||
}
|
||||
|
||||
function setupTestData() {
|
||||
Telemetry.newHistogram(IGNORE_HISTOGRAM, "never", 1, 2, 3, Telemetry.HISTOGRAM_BOOLEAN);
|
||||
Telemetry.histogramFrom(IGNORE_CLONED_HISTOGRAM, IGNORE_HISTOGRAM_TO_CLONE);
|
||||
|
@ -133,16 +99,6 @@ function getSavedHistogramsFile(basename) {
|
|||
return histogramsFile;
|
||||
}
|
||||
|
||||
function telemetryObserver(aSubject, aTopic, aData) {
|
||||
registerPingHandler(checkHistogramsSync);
|
||||
let histogramsFile = getSavedHistogramsFile("saved-histograms.dat");
|
||||
setupTestData();
|
||||
|
||||
TelemetryPing.saveHistograms(histogramsFile, true);
|
||||
TelemetryPing.testLoadHistograms(histogramsFile, true);
|
||||
telemetry_ping();
|
||||
}
|
||||
|
||||
function decodeRequestPayload(request) {
|
||||
let s = request.bodyInputStream;
|
||||
let payload = null;
|
||||
|
@ -204,9 +160,9 @@ function checkPayloadInfo(payload, reason) {
|
|||
try {
|
||||
// If we've not got nsIGfxInfoDebug, then this will throw and stop us doing
|
||||
// this test.
|
||||
var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
|
||||
var isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes);
|
||||
var isOSX = ("nsILocalFileMac" in Components.interfaces);
|
||||
let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
|
||||
let isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes);
|
||||
let isOSX = ("nsILocalFileMac" in Components.interfaces);
|
||||
|
||||
if (isWindows || isOSX) {
|
||||
do_check_true("adapterVendorID" in payload.info);
|
||||
|
@ -240,7 +196,7 @@ function checkPayload(request, reason, successfulPings) {
|
|||
do_check_true(!failedProfileLocksFile.exists());
|
||||
|
||||
|
||||
var isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes);
|
||||
let isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes);
|
||||
if (isWindows) {
|
||||
do_check_true(payload.simpleMeasurements.startupSessionRestoreReadBytes > 0);
|
||||
do_check_true(payload.simpleMeasurements.startupSessionRestoreWriteBytes > 0);
|
||||
|
@ -312,72 +268,6 @@ function checkPayload(request, reason, successfulPings) {
|
|||
("otherThreads" in payload.slowSQL));
|
||||
}
|
||||
|
||||
function checkPersistedHistogramsSync(request, response) {
|
||||
// Even though we have had two successful pings when this handler is
|
||||
// run, we only had one successful ping when the histograms were
|
||||
// saved.
|
||||
checkPayload(request, "saved-session", 1);
|
||||
|
||||
addWrappedObserver(runAsyncTestObserver, "telemetry-test-xhr-complete");
|
||||
}
|
||||
|
||||
function checkHistogramsSync(request, response) {
|
||||
registerPingHandler(checkPersistedHistogramsSync);
|
||||
checkPayload(request, "test-ping", 1);
|
||||
}
|
||||
|
||||
function runAsyncTestObserver(aSubject, aTopic, aData) {
|
||||
registerPingHandler(checkHistogramsAsync);
|
||||
let histogramsFile = getSavedHistogramsFile("saved-histograms2.dat");
|
||||
|
||||
addWrappedObserver(function(aSubject, aTopic, aData) {
|
||||
addWrappedObserver(function(aSubject, aTopic, aData) {
|
||||
telemetry_ping();
|
||||
}, "telemetry-test-load-complete");
|
||||
|
||||
TelemetryPing.testLoadHistograms(histogramsFile, false);
|
||||
}, "telemetry-test-save-complete");
|
||||
TelemetryPing.saveHistograms(histogramsFile, false);
|
||||
}
|
||||
|
||||
function checkPersistedHistogramsAsync(request, response) {
|
||||
// do not need the http server anymore
|
||||
httpserver.stop(do_test_finished);
|
||||
// Even though we have had four successful pings when this handler is
|
||||
// run, we only had three successful pings when the histograms were
|
||||
// saved.
|
||||
checkPayload(request, "saved-session", 3);
|
||||
|
||||
runOldPingFileTest();
|
||||
|
||||
gFinished = true;
|
||||
}
|
||||
|
||||
function checkHistogramsAsync(request, response) {
|
||||
registerPingHandler(checkPersistedHistogramsAsync);
|
||||
checkPayload(request, "test-ping", 3);
|
||||
}
|
||||
|
||||
function runInvalidJSONTest() {
|
||||
let histogramsFile = getSavedHistogramsFile("invalid-histograms.dat");
|
||||
writeStringToFile(histogramsFile, "this.is.invalid.JSON");
|
||||
do_check_true(histogramsFile.exists());
|
||||
|
||||
TelemetryPing.testLoadHistograms(histogramsFile, true);
|
||||
do_check_false(histogramsFile.exists());
|
||||
}
|
||||
|
||||
function runOldPingFileTest() {
|
||||
let histogramsFile = getSavedHistogramsFile("old-histograms.dat");
|
||||
TelemetryPing.saveHistograms(histogramsFile, true);
|
||||
do_check_true(histogramsFile.exists());
|
||||
|
||||
let mtime = histogramsFile.lastModifiedTime;
|
||||
histogramsFile.lastModifiedTime = mtime - (14 * 24 * 60 * 60 * 1000 + 60000); // 14 days, 1m
|
||||
TelemetryPing.testLoadHistograms(histogramsFile, true);
|
||||
do_check_false(histogramsFile.exists());
|
||||
}
|
||||
|
||||
function dummyTheme(id) {
|
||||
return {
|
||||
id: id,
|
||||
|
@ -390,7 +280,7 @@ function dummyTheme(id) {
|
|||
}
|
||||
|
||||
// A fake plugin host for testing flash version telemetry
|
||||
var PluginHost = {
|
||||
let PluginHost = {
|
||||
getPluginTags: function(countRef) {
|
||||
let plugins = [{name: "Shockwave Flash", version: FLASH_VERSION}];
|
||||
countRef.value = plugins.length;
|
||||
|
@ -406,7 +296,7 @@ var PluginHost = {
|
|||
}
|
||||
}
|
||||
|
||||
var PluginHostFactory = {
|
||||
let PluginHostFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null)
|
||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
|
@ -418,7 +308,7 @@ const PLUGINHOST_CONTRACTID = "@mozilla.org/plugin/host;1";
|
|||
const PLUGINHOST_CID = Components.ID("{2329e6ea-1f15-4cbe-9ded-6e98e842de0e}");
|
||||
|
||||
function registerFakePluginHost() {
|
||||
var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(PLUGINHOST_CID, "Fake Plugin Host",
|
||||
PLUGINHOST_CONTRACTID, PluginHostFactory);
|
||||
}
|
||||
|
@ -452,7 +342,7 @@ function write_fake_failedprofilelocks_file() {
|
|||
function run_test() {
|
||||
do_test_pending();
|
||||
try {
|
||||
var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
|
||||
let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
|
||||
gfxInfo.spoofVendorID("0xabcd");
|
||||
gfxInfo.spoofDeviceID("0x1234");
|
||||
} catch (x) {
|
||||
|
@ -495,8 +385,6 @@ function run_test() {
|
|||
}
|
||||
|
||||
function actualTest() {
|
||||
// ensure that test runs to completion
|
||||
do_register_cleanup(function () do_check_true(gFinished));
|
||||
// try to make LightweightThemeManager do stuff
|
||||
let gInternalManager = Cc["@mozilla.org/addons/integration;1"]
|
||||
.getService(Ci.nsIObserver)
|
||||
|
@ -508,12 +396,99 @@ function actualTest() {
|
|||
// fake plugin host for consistent flash version data
|
||||
registerFakePluginHost();
|
||||
|
||||
runInvalidJSONTest();
|
||||
test_expired_histogram();
|
||||
|
||||
addWrappedObserver(nonexistentServerObserver, "telemetry-test-xhr-complete");
|
||||
telemetry_ping();
|
||||
// spin the event loop
|
||||
do_test_pending();
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Ensures that expired histograms are not part of the payload.
|
||||
add_task(function* test_expiredHistogram() {
|
||||
let histogram_id = "FOOBAR";
|
||||
let dummy = Telemetry.newHistogram(histogram_id, "30", 1, 2, 3, Telemetry.HISTOGRAM_EXPONENTIAL);
|
||||
|
||||
dummy.add(1);
|
||||
|
||||
do_check_eq(TelemetryPing.getPayload()["histograms"][histogram_id], undefined);
|
||||
do_check_eq(TelemetryPing.getPayload()["histograms"]["TELEMETRY_TEST_EXPIRED"], undefined);
|
||||
});
|
||||
|
||||
// Checks that an invalid histogram file is deleted if TelemetryFile fails to parse it.
|
||||
add_task(function* test_runInvalidJSON() {
|
||||
let histogramsFile = getSavedHistogramsFile("invalid-histograms.dat");
|
||||
|
||||
writeStringToFile(histogramsFile, "this.is.invalid.JSON");
|
||||
do_check_true(histogramsFile.exists());
|
||||
|
||||
yield TelemetryPing.testLoadHistograms(histogramsFile);
|
||||
do_check_false(histogramsFile.exists());
|
||||
});
|
||||
|
||||
// Sends a ping to a non existing server.
|
||||
add_task(function* test_noServerPing() {
|
||||
yield sendPing();
|
||||
});
|
||||
|
||||
// Checks that a sent ping is correctly received by a dummy http server.
|
||||
add_task(function* test_simplePing() {
|
||||
gHttpServer.start(-1);
|
||||
gServerStarted = true;
|
||||
gRequestIterator = Iterator(new Request());
|
||||
|
||||
yield sendPing();
|
||||
decodeRequestPayload(yield gRequestIterator.next());
|
||||
});
|
||||
|
||||
// Saves the current session histograms, reloads them, perfoms a ping
|
||||
// and checks that the dummy http server received both the previously
|
||||
// saved histograms and the new ones.
|
||||
add_task(function* test_saveLoadPing() {
|
||||
let histogramsFile = getSavedHistogramsFile("saved-histograms.dat");
|
||||
|
||||
setupTestData();
|
||||
yield TelemetryPing.testSaveHistograms(histogramsFile);
|
||||
yield TelemetryPing.testLoadHistograms(histogramsFile);
|
||||
yield sendPing();
|
||||
checkPayload((yield gRequestIterator.next()), "test-ping", 1);
|
||||
checkPayload((yield gRequestIterator.next()), "saved-session", 1);
|
||||
});
|
||||
|
||||
// Checks that an expired histogram file is deleted when loaded.
|
||||
add_task(function* test_runOldPingFile() {
|
||||
let histogramsFile = getSavedHistogramsFile("old-histograms.dat");
|
||||
|
||||
yield TelemetryPing.testSaveHistograms(histogramsFile);
|
||||
do_check_true(histogramsFile.exists());
|
||||
let mtime = histogramsFile.lastModifiedTime;
|
||||
histogramsFile.lastModifiedTime = mtime - (14 * 24 * 60 * 60 * 1000 + 60000); // 14 days, 1m
|
||||
|
||||
yield TelemetryPing.testLoadHistograms(histogramsFile);
|
||||
do_check_false(histogramsFile.exists());
|
||||
});
|
||||
|
||||
add_task(function* stopServer(){
|
||||
gHttpServer.stop(do_test_finished);
|
||||
});
|
||||
|
||||
// An iterable sequence of http requests
|
||||
function Request() {
|
||||
let defers = [];
|
||||
let current = 0;
|
||||
|
||||
function RequestIterator() {}
|
||||
|
||||
// Returns a promise that resolves to the next http request
|
||||
RequestIterator.prototype.next = function() {
|
||||
let deferred = defers[current++];
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
this.__iterator__ = function(){
|
||||
return new RequestIterator();
|
||||
}
|
||||
|
||||
registerPingHandler((request, response) => {
|
||||
let deferred = defers[defers.length - 1];
|
||||
defers.push(Promise.defer());
|
||||
deferred.resolve(request);
|
||||
});
|
||||
|
||||
defers.push(Promise.defer());
|
||||
}
|
||||
|
|
|
@ -13,57 +13,57 @@
|
|||
* -> previousBuildID in telemetry, new value set in prefs.
|
||||
*/
|
||||
|
||||
"use strict"
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm", this);
|
||||
|
||||
// Force the Telemetry enabled preference so that TelemetryPing.reset() doesn't exit early.
|
||||
Services.prefs.setBoolPref(TelemetryPing.Constants.PREF_ENABLED, true);
|
||||
|
||||
// Set up our dummy AppInfo object so we can control the appBuildID.
|
||||
Cu.import("resource://testing-common/AppInfo.jsm");
|
||||
Cu.import("resource://testing-common/AppInfo.jsm", this);
|
||||
updateAppInfo();
|
||||
|
||||
// Check that when run with no previous build ID stored, we update the pref but do not
|
||||
// put anything into the metadata.
|
||||
function testFirstRun() {
|
||||
TelemetryPing.reset();
|
||||
add_task(function* test_firstRun() {
|
||||
yield TelemetryPing.reset();
|
||||
let metadata = TelemetryPing.getMetadata();
|
||||
do_check_false("previousBuildID" in metadata);
|
||||
let appBuildID = getAppInfo().appBuildID;
|
||||
let buildIDPref = Services.prefs.getCharPref(TelemetryPing.Constants.PREF_PREVIOUS_BUILDID);
|
||||
do_check_eq(appBuildID, buildIDPref);
|
||||
}
|
||||
});
|
||||
|
||||
// Check that a subsequent run with the same build ID does not put prev build ID in
|
||||
// metadata. Assumes testFirstRun() has already been called to set the previousBuildID pref.
|
||||
function testSecondRun() {
|
||||
TelemetryPing.reset();
|
||||
add_task(function* test_secondRun() {
|
||||
yield TelemetryPing.reset();
|
||||
let metadata = TelemetryPing.getMetadata();
|
||||
do_check_false("previousBuildID" in metadata);
|
||||
}
|
||||
});
|
||||
|
||||
// Set up telemetry with a different app build ID and check that the old build ID
|
||||
// is returned in the metadata and the pref is updated to the new build ID.
|
||||
// Assumes testFirstRun() has been called to set the previousBuildID pref.
|
||||
const NEW_BUILD_ID = "20130314";
|
||||
function testNewBuild() {
|
||||
add_task(function* test_newBuild() {
|
||||
let info = getAppInfo();
|
||||
let oldBuildID = info.appBuildID;
|
||||
info.appBuildID = NEW_BUILD_ID;
|
||||
TelemetryPing.reset();
|
||||
yield TelemetryPing.reset();
|
||||
let metadata = TelemetryPing.getMetadata();
|
||||
do_check_eq(metadata.previousBuildID, oldBuildID);
|
||||
let buildIDPref = Services.prefs.getCharPref(TelemetryPing.Constants.PREF_PREVIOUS_BUILDID);
|
||||
do_check_eq(NEW_BUILD_ID, buildIDPref);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function run_test() {
|
||||
// Make sure we have a profile directory.
|
||||
do_get_profile();
|
||||
testFirstRun();
|
||||
testSecondRun();
|
||||
testNewBuild();
|
||||
run_next_test();
|
||||
}
|
||||
|
|
|
@ -10,16 +10,20 @@
|
|||
* overdue and recent pings.
|
||||
*/
|
||||
|
||||
"use strict"
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryFile.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://testing-common/httpd.js", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryFile.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
Cu.import("resource://gre/modules/osfile.jsm", this);
|
||||
|
||||
// We increment TelemetryFile's MAX_PING_FILE_AGE and
|
||||
// OVERDUE_PING_FILE_AGE by 1ms so that our test pings exceed
|
||||
|
@ -51,25 +55,30 @@ let gSeenPings = 0;
|
|||
* @returns an Array with the created pings.
|
||||
*/
|
||||
function createSavedPings(aNum, aAge) {
|
||||
// Create a TelemetryPing service that we can generate payloads from.
|
||||
// Luckily, the TelemetryPing constructor does nothing that we need to
|
||||
// clean up.
|
||||
let pings = [];
|
||||
let age = Date.now() - aAge;
|
||||
for (let i = 0; i < aNum; ++i) {
|
||||
let payload = TelemetryPing.getPayload();
|
||||
let ping = { slug: "test-ping-" + gCreatedPings, reason: "test", payload: payload };
|
||||
TelemetryFile.savePing(ping);
|
||||
if (aAge) {
|
||||
// savePing writes to the file synchronously, so we're good to
|
||||
// modify the lastModifedTime now.
|
||||
let file = getSaveFileForPing(ping);
|
||||
file.lastModifiedTime = age;
|
||||
return Task.spawn(function*(){
|
||||
// Create a TelemetryPing service that we can generate payloads from.
|
||||
// Luckily, the TelemetryPing constructor does nothing that we need to
|
||||
// clean up.
|
||||
let pings = [];
|
||||
let age = Date.now() - aAge;
|
||||
|
||||
for (let i = 0; i < aNum; ++i) {
|
||||
let payload = TelemetryPing.getPayload();
|
||||
let ping = { slug: "test-ping-" + gCreatedPings, reason: "test", payload: payload };
|
||||
|
||||
yield TelemetryFile.savePing(ping);
|
||||
|
||||
if (aAge) {
|
||||
// savePing writes to the file synchronously, so we're good to
|
||||
// modify the lastModifedTime now.
|
||||
let file = getSaveFileForPing(ping);
|
||||
file.lastModifiedTime = age;
|
||||
}
|
||||
gCreatedPings++;
|
||||
pings.push(ping);
|
||||
}
|
||||
gCreatedPings++;
|
||||
pings.push(ping);
|
||||
}
|
||||
return pings;
|
||||
return pings;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,45 +110,13 @@ function getSaveFileForPing(aPing) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Wait for PING_TIMEOUT_LENGTH ms, and make sure we didn't receive
|
||||
* TelemetryPings in that time.
|
||||
*
|
||||
* @returns Promise
|
||||
*/
|
||||
function assertReceivedNoPings() {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
do_timeout(PING_TIMEOUT_LENGTH, function() {
|
||||
if (gSeenPings > 0) {
|
||||
deferred.reject();
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise that rejects if the number of TelemetryPings
|
||||
* received by the HttpServer is not equal to aExpectedNum.
|
||||
* Check if the number of TelemetryPings received by the
|
||||
* HttpServer is not equal to aExpectedNum.
|
||||
*
|
||||
* @param aExpectedNum the number of pings we expect to receive.
|
||||
* @returns Promise
|
||||
*/
|
||||
function assertReceivedPings(aExpectedNum) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
do_timeout(PING_TIMEOUT_LENGTH, function() {
|
||||
if (gSeenPings == aExpectedNum) {
|
||||
deferred.resolve();
|
||||
} else {
|
||||
deferred.reject("Saw " + gSeenPings + " TelemetryPings, " +
|
||||
"but expected " + aExpectedNum);
|
||||
}
|
||||
})
|
||||
|
||||
return deferred.promise;
|
||||
do_check_eq(gSeenPings, aExpectedNum);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -202,7 +179,7 @@ function resetTelemetry() {
|
|||
* mode.
|
||||
*/
|
||||
function startTelemetry() {
|
||||
TelemetryPing.setup();
|
||||
return TelemetryPing.setup();
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
|
@ -220,21 +197,21 @@ function run_test() {
|
|||
* immediately and never sent.
|
||||
*/
|
||||
add_task(function test_expired_pings_are_deleted() {
|
||||
let expiredPings = createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
|
||||
startTelemetry();
|
||||
yield assertReceivedNoPings();
|
||||
let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
|
||||
yield startTelemetry();
|
||||
assertReceivedPings(0);
|
||||
assertNotSaved(expiredPings);
|
||||
resetTelemetry();
|
||||
yield resetTelemetry();
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that really recent pings are not sent on Telemetry initialization.
|
||||
*/
|
||||
add_task(function test_recent_pings_not_sent() {
|
||||
let recentPings = createSavedPings(RECENT_PINGS);
|
||||
startTelemetry();
|
||||
yield assertReceivedNoPings();
|
||||
resetTelemetry();
|
||||
let recentPings = yield createSavedPings(RECENT_PINGS);
|
||||
yield startTelemetry();
|
||||
assertReceivedPings(0);
|
||||
yield resetTelemetry();
|
||||
clearPings(recentPings);
|
||||
});
|
||||
|
||||
|
@ -244,17 +221,17 @@ add_task(function test_recent_pings_not_sent() {
|
|||
* should just be deleted.
|
||||
*/
|
||||
add_task(function test_overdue_pings_trigger_send() {
|
||||
let recentPings = createSavedPings(RECENT_PINGS);
|
||||
let expiredPings = createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
|
||||
let overduePings = createSavedPings(OVERDUE_PINGS, OVERDUE_PING_FILE_AGE);
|
||||
let recentPings = yield createSavedPings(RECENT_PINGS);
|
||||
let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
|
||||
let overduePings = yield createSavedPings(OVERDUE_PINGS, OVERDUE_PING_FILE_AGE);
|
||||
|
||||
startTelemetry();
|
||||
yield assertReceivedPings(TOTAL_EXPECTED_PINGS);
|
||||
yield startTelemetry();
|
||||
assertReceivedPings(TOTAL_EXPECTED_PINGS);
|
||||
|
||||
assertNotSaved(recentPings);
|
||||
assertNotSaved(expiredPings);
|
||||
assertNotSaved(overduePings);
|
||||
resetTelemetry();
|
||||
yield resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(function teardown() {
|
||||
|
|
|
@ -202,35 +202,9 @@
|
|||
label="&charsetMenu.label;"
|
||||
accesskey="&charsetMenu.accesskey;"
|
||||
oncommand="MultiplexHandler(event);"
|
||||
onpopupshowing="CharsetMenu.build(event);"
|
||||
onpopupshowing="CharsetMenu.build(event.target);"
|
||||
onpopupshown="UpdateMenus();">
|
||||
<menupopup>
|
||||
<menu label="&charsetMenuAutodet.label;"
|
||||
accesskey="&charsetMenuAutodet.accesskey;">
|
||||
<menupopup>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
id="chardet.off"
|
||||
label="&charsetMenuAutodet.off.label;"
|
||||
accesskey="&charsetMenuAutodet.off.accesskey;"/>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
id="chardet.ja_parallel_state_machine"
|
||||
label="&charsetMenuAutodet.ja.label;"
|
||||
accesskey="&charsetMenuAutodet.ja.accesskey;"/>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
id="chardet.ruprob"
|
||||
label="&charsetMenuAutodet.ru.label;"
|
||||
accesskey="&charsetMenuAutodet.ru.accesskey;"/>
|
||||
<menuitem type="radio"
|
||||
name="detectorGroup"
|
||||
id="chardet.ukprob"
|
||||
label="&charsetMenuAutodet.uk.label;"
|
||||
accesskey="&charsetMenuAutodet.uk.accesskey;"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
|
|
|
@ -679,6 +679,54 @@ function uniqueFile(aLocalFile)
|
|||
return aLocalFile;
|
||||
}
|
||||
|
||||
#ifdef MOZ_JSDOWNLOADS
|
||||
/**
|
||||
* Download a URL using the new jsdownloads API.
|
||||
*
|
||||
* @param aURL
|
||||
* the url to download
|
||||
* @param [optional] aFileName
|
||||
* the destination file name, if omitted will be obtained from the url.
|
||||
* @param aInitiatingDocument
|
||||
* The document from which the download was initiated.
|
||||
*/
|
||||
function DownloadURL(aURL, aFileName, aInitiatingDocument) {
|
||||
// For private browsing, try to get document out of the most recent browser
|
||||
// window, or provide our own if there's no browser window.
|
||||
let isPrivate = aInitiatingDocument.defaultView
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsILoadContext)
|
||||
.usePrivateBrowsing;
|
||||
|
||||
let fileInfo = new FileInfo(aFileName);
|
||||
initFileInfo(fileInfo, aURL, null, null, null, null);
|
||||
|
||||
let filepickerParams = {
|
||||
fileInfo: fileInfo,
|
||||
saveMode: SAVEMODE_FILEONLY
|
||||
};
|
||||
|
||||
Task.spawn(function* () {
|
||||
let accepted = yield promiseTargetFile(filepickerParams, true, fileInfo.uri);
|
||||
if (!accepted)
|
||||
return;
|
||||
|
||||
let file = filepickerParams.file;
|
||||
let download = yield Downloads.createDownload({
|
||||
source: { url: aURL, isPrivate: isPrivate },
|
||||
target: { path: file.path, partFilePath: file.path + ".part" }
|
||||
});
|
||||
download.tryToKeepPartialData = true;
|
||||
download.start();
|
||||
|
||||
// Add the download to the list, allowing it to be managed.
|
||||
let list = yield Downloads.getList(Downloads.ALL);
|
||||
list.add(download);
|
||||
}).then(null, Components.utils.reportError);
|
||||
}
|
||||
#endif
|
||||
|
||||
// We have no DOM, and can only save the URL as is.
|
||||
const SAVEMODE_FILEONLY = 0x00;
|
||||
// We have a DOM and can save as complete.
|
||||
|
|
|
@ -59,6 +59,9 @@
|
|||
<li><a href="about:license#lgpl">GNU Lesser General Public License 2.1</a>
|
||||
<br><br>
|
||||
</li>
|
||||
<li><a href="about:license#lgpl-3.0">GNU Lesser General Public License 3.0</a>
|
||||
<br><br>
|
||||
</li>
|
||||
<li><a href="about:license#ACE">ACE License</a></li>
|
||||
<li><a href="about:license#acorn">acorn License</a></li>
|
||||
<li><a href="about:license#android">Android Open Source License</a></li>
|
||||
|
@ -1143,6 +1146,192 @@ DAMAGES.
|
|||
|
||||
<hr>
|
||||
|
||||
<h1 id="lgpl-3.0">GNU Lesser General Public License 3.0</h1>
|
||||
|
||||
<p>Some versions of this product contains code from the following LGPLed libraries:</p>
|
||||
|
||||
<ul>
|
||||
<li><a
|
||||
href="https://addons.mozilla.org/en-US/firefox/addon/görans-hemmasnickrade-ordli/">Swedish dictionary</a>
|
||||
</ul>
|
||||
|
||||
<pre>Copyright © 2007 Free Software Foundation, Inc.
|
||||
<<a href="http://fsf.org/">http://fsf.org/</a>>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.</pre>
|
||||
|
||||
<p>This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.</p>
|
||||
|
||||
<h3><a id="section0">0. Additional Definitions</a></h3>
|
||||
|
||||
<p>As used herein, “this License” refers to version 3 of the GNU Lesser
|
||||
General Public License, and the “GNU GPL” refers to version 3 of the GNU
|
||||
General Public License.</p>
|
||||
|
||||
<p>“The Library” refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.</p>
|
||||
|
||||
<p>An “Application” is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.</p>
|
||||
|
||||
<p>A “Combined Work” is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the “Linked
|
||||
Version”.</p>
|
||||
|
||||
<p>The “Minimal Corresponding Source” for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.</p>
|
||||
|
||||
<p>The “Corresponding Application Code” for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.</p>
|
||||
|
||||
<h3><a id="section1">1. Exception to Section 3 of the GNU GPL.</a></h3>
|
||||
|
||||
<p>You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.</p>
|
||||
|
||||
<h3><a id="section2">2. Conveying Modified Versions.</a></h3>
|
||||
|
||||
<p>If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:</p>
|
||||
|
||||
<ul>
|
||||
<li>a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or</li>
|
||||
|
||||
<li>b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.</li>
|
||||
</ul>
|
||||
|
||||
<h3><a id="section3">3. Object Code Incorporating Material from Library Header Files.</a></h3>
|
||||
|
||||
<p>The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:</p>
|
||||
|
||||
<ul>
|
||||
<li>a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.</li>
|
||||
|
||||
<li>b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.</li>
|
||||
</ul>
|
||||
|
||||
<h3><a id="section4">4. Combined Works.</a></h3>
|
||||
|
||||
<p>You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:</p>
|
||||
|
||||
<ul>
|
||||
<li>a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.</li>
|
||||
|
||||
<li>b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.</li>
|
||||
|
||||
<li>c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.</li>
|
||||
|
||||
<li>d) Do one of the following:
|
||||
|
||||
<ul>
|
||||
<li>0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.</li>
|
||||
|
||||
<li>1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)</li>
|
||||
</ul>
|
||||
|
||||
<h3><a id="section5">5. Combined Libraries.</a></h3>
|
||||
|
||||
<p>You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:</p>
|
||||
|
||||
<ul>
|
||||
<li>a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.</li>
|
||||
|
||||
<li>b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.</li>
|
||||
</ul>
|
||||
|
||||
<h3><a id="section6">6. Revised Versions of the GNU Lesser General Public License.</a></h3>
|
||||
|
||||
<p>The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.</p>
|
||||
|
||||
<p>Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License “or any later version”
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.</p>
|
||||
|
||||
<p>If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.</p>
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
<h1><a id="ACE"></a>ACE License</h1>
|
||||
|
||||
<p>This license applies to the file
|
||||
|
|
|
@ -4,15 +4,3 @@
|
|||
|
||||
<!ENTITY charsetMenu.label "Character Encoding">
|
||||
<!ENTITY charsetMenu.accesskey "C">
|
||||
<!ENTITY charsetMenuAutodet.label "Auto-Detect">
|
||||
<!ENTITY charsetMenuAutodet.accesskey "D"><!-- A reserved for Arabic -->
|
||||
|
||||
<!ENTITY charsetMenuAutodet.off.label "(off)">
|
||||
<!ENTITY charsetMenuAutodet.off.accesskey "o">
|
||||
<!ENTITY charsetMenuAutodet.ja.label "Japanese">
|
||||
<!ENTITY charsetMenuAutodet.ja.accesskey "J">
|
||||
<!ENTITY charsetMenuAutodet.ru.label "Russian">
|
||||
<!ENTITY charsetMenuAutodet.ru.accesskey "R">
|
||||
<!ENTITY charsetMenuAutodet.uk.label "Ukrainian">
|
||||
<!ENTITY charsetMenuAutodet.uk.accesskey "U">
|
||||
|
||||
|
|
|
@ -24,6 +24,20 @@
|
|||
# (When this code was developed, all localizations appeared to use
|
||||
# U+0028 LEFT PARENTHESIS for this purpose.)
|
||||
|
||||
# Auto-Detect (sub)menu
|
||||
charsetMenuCharsets = Character Encoding
|
||||
charsetMenuAutodet = Auto-Detect
|
||||
# 'A' is reserved for Arabic:
|
||||
charsetMenuAutodet.key = D
|
||||
charsetMenuAutodet.off = (off)
|
||||
charsetMenuAutodet.off.key = o
|
||||
charsetMenuAutodet.ja = Japanese
|
||||
charsetMenuAutodet.ja.key = J
|
||||
charsetMenuAutodet.ru = Russian
|
||||
charsetMenuAutodet.ru.key = R
|
||||
charsetMenuAutodet.uk = Ukrainian
|
||||
charsetMenuAutodet.uk.key = U
|
||||
|
||||
# Globally-relevant
|
||||
UTF-8.key = U
|
||||
UTF-8 = Unicode
|
||||
|
|
|
@ -12,6 +12,14 @@ XPCOMUtils.defineLazyGetter(this, "gBundle", function() {
|
|||
const kUrl = "chrome://global/locale/charsetMenu.properties";
|
||||
return Services.strings.createBundle(kUrl);
|
||||
});
|
||||
|
||||
const kAutoDetectors = [
|
||||
["off", "off"],
|
||||
["ja", "ja_parallel_state_machine"],
|
||||
["ru", "ruprob"],
|
||||
["uk", "ukprob"]
|
||||
];
|
||||
|
||||
/**
|
||||
* This set contains encodings that are in the Encoding Standard, except:
|
||||
* - XSS-dangerous encodings (except ISO-2022-JP which is assumed to be
|
||||
|
@ -80,53 +88,94 @@ const kPinned = [
|
|||
"windows-1252"
|
||||
];
|
||||
|
||||
this.CharsetMenu = Object.freeze({
|
||||
build: function BuildCharsetMenu(event, idPrefix="", showAccessKeys=true) {
|
||||
let parent = event.target;
|
||||
if (parent.lastChild.localName != "menuseparator") {
|
||||
kPinned.forEach(x => kEncodings.delete(x));
|
||||
|
||||
|
||||
let gDetectorInfoCache, gCharsetInfoCache, gPinnedInfoCache;
|
||||
|
||||
let CharsetMenu = {
|
||||
build: function(parent, idPrefix="", showAccessKeys=true) {
|
||||
function createDOMNode(doc, nodeInfo) {
|
||||
let node = doc.createElement("menuitem");
|
||||
node.setAttribute("type", "radio");
|
||||
node.setAttribute("name", nodeInfo.name);
|
||||
node.setAttribute("label", nodeInfo.label);
|
||||
if (showAccessKeys && nodeInfo.accesskey) {
|
||||
node.setAttribute("accesskey", nodeInfo.accesskey);
|
||||
}
|
||||
if (idPrefix) {
|
||||
node.id = idPrefix + nodeInfo.id;
|
||||
} else {
|
||||
node.id = nodeInfo.id;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
if (parent.childElementCount > 0) {
|
||||
// Detector menu or charset menu already built
|
||||
return;
|
||||
}
|
||||
let doc = parent.ownerDocument;
|
||||
|
||||
function createItem(encoding) {
|
||||
let menuItem = doc.createElement("menuitem");
|
||||
menuItem.setAttribute("type", "radio");
|
||||
menuItem.setAttribute("name", "charsetGroup");
|
||||
try {
|
||||
menuItem.setAttribute("label", gBundle.GetStringFromName(encoding));
|
||||
} catch (e) {
|
||||
// Localization error but put *something* in the menu to recover.
|
||||
menuItem.setAttribute("label", encoding);
|
||||
}
|
||||
if (showAccessKeys) {
|
||||
try {
|
||||
menuItem.setAttribute("accesskey",
|
||||
gBundle.GetStringFromName(encoding + ".key"));
|
||||
} catch (e) {
|
||||
// Some items intentionally don't have an accesskey
|
||||
}
|
||||
}
|
||||
menuItem.setAttribute("id", idPrefix + "charset." + encoding);
|
||||
return menuItem;
|
||||
let menuNode = doc.createElement("menu");
|
||||
menuNode.setAttribute("label", gBundle.GetStringFromName("charsetMenuAutodet"));
|
||||
if (showAccessKeys) {
|
||||
menuNode.setAttribute("accesskey", gBundle.GetStringFromName("charsetMenuAutodet.key"));
|
||||
}
|
||||
parent.appendChild(menuNode);
|
||||
|
||||
// Clone the set in order to be able to remove the pinned encodings from
|
||||
// the cloned set.
|
||||
let encodings = new Set(kEncodings);
|
||||
for (let encoding of kPinned) {
|
||||
encodings.delete(encoding);
|
||||
parent.appendChild(createItem(encoding));
|
||||
}
|
||||
let menuPopupNode = doc.createElement("menupopup");
|
||||
menuNode.appendChild(menuPopupNode);
|
||||
|
||||
this._ensureDataReady();
|
||||
gDetectorInfoCache.forEach(detectorInfo => menuPopupNode.appendChild(createDOMNode(doc, detectorInfo)));
|
||||
parent.appendChild(doc.createElement("menuseparator"));
|
||||
let list = [];
|
||||
for (let encoding of encodings) {
|
||||
list.push(createItem(encoding));
|
||||
gPinnedInfoCache.forEach(charsetInfo => parent.appendChild(createDOMNode(doc, charsetInfo)));
|
||||
parent.appendChild(doc.createElement("menuseparator"));
|
||||
gCharsetInfoCache.forEach(charsetInfo => parent.appendChild(createDOMNode(doc, charsetInfo)));
|
||||
},
|
||||
|
||||
getData: function() {
|
||||
this._ensureDataReady();
|
||||
return {
|
||||
detectors: gDetectorInfoCache,
|
||||
pinnedCharsets: gPinnedInfoCache,
|
||||
otherCharsets: gCharsetInfoCache
|
||||
};
|
||||
},
|
||||
|
||||
_ensureDataReady: function() {
|
||||
if (!gDetectorInfoCache) {
|
||||
gDetectorInfoCache = this.getDetectorInfo();
|
||||
gPinnedInfoCache = this.getCharsetInfo(kPinned, false);
|
||||
gCharsetInfoCache = this.getCharsetInfo([...kEncodings]);
|
||||
}
|
||||
},
|
||||
|
||||
getDetectorInfo: function() {
|
||||
return kAutoDetectors.map(([detectorName, nodeId]) => ({
|
||||
id: "chardet." + nodeId,
|
||||
label: this._getDetectorLabel(detectorName),
|
||||
accesskey: this._getDetectorAccesskey(detectorName),
|
||||
name: "detectorGroup",
|
||||
}));
|
||||
},
|
||||
|
||||
getCharsetInfo: function(charsets, sort=true) {
|
||||
let list = charsets.map(charset => ({
|
||||
id: "charset." + charset,
|
||||
label: this._getCharsetLabel(charset),
|
||||
accesskey: this._getCharsetAccessKey(charset),
|
||||
name: "charsetGroup",
|
||||
}));
|
||||
|
||||
if (!sort) {
|
||||
return list;
|
||||
}
|
||||
|
||||
list.sort(function (a, b) {
|
||||
let titleA = a.getAttribute("label");
|
||||
let titleB = b.getAttribute("label");
|
||||
let titleA = a.label;
|
||||
let titleB = b.label;
|
||||
// Normal sorting sorts the part in parenthesis in an order that
|
||||
// happens to make the less frequently-used items first.
|
||||
let index;
|
||||
|
@ -143,19 +192,43 @@ this.CharsetMenu = Object.freeze({
|
|||
// secondarily reverse sort by encoding name to sort "windows" or
|
||||
// "shift_jis" first. This works regardless of localization, because
|
||||
// the ids aren't localized.
|
||||
let idA = a.getAttribute("id");
|
||||
let idB = b.getAttribute("id");
|
||||
if (idA < idB) {
|
||||
if (a.id < b.id) {
|
||||
return 1;
|
||||
}
|
||||
if (idB < idA) {
|
||||
if (b.id < a.id) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
for (let item of list) {
|
||||
parent.appendChild(item);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
});
|
||||
|
||||
_getDetectorLabel: function(detector) {
|
||||
try {
|
||||
return gBundle.GetStringFromName("charsetMenuAutodet." + detector);
|
||||
} catch (ex) {}
|
||||
return detector;
|
||||
},
|
||||
_getDetectorAccesskey: function(detector) {
|
||||
try {
|
||||
return gBundle.GetStringFromName("charsetMenuAutodet." + detector + ".key");
|
||||
} catch (ex) {}
|
||||
return "";
|
||||
},
|
||||
|
||||
_getCharsetLabel: function(charset) {
|
||||
try {
|
||||
return gBundle.GetStringFromName(charset);
|
||||
} catch (ex) {}
|
||||
return charset;
|
||||
},
|
||||
_getCharsetAccessKey: function(charset) {
|
||||
try {
|
||||
accesskey = gBundle.GetStringFromName(charset + ".key");
|
||||
} catch (ex) {}
|
||||
return "";
|
||||
},
|
||||
};
|
||||
|
||||
Object.freeze(CharsetMenu);
|
||||
|
||||
|
|
|
@ -878,6 +878,10 @@ nsAppShell::Init()
|
|||
android::FakeSurfaceComposer::instantiate();
|
||||
#endif
|
||||
GonkPermissionService::instantiate();
|
||||
|
||||
// Causes the kernel timezone to be set, which in turn causes the
|
||||
// timestamps on SD cards to have the local time rather than UTC time.
|
||||
hal::SetTimezone(hal::GetTimezone());
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsServ = GetObserverService();
|
||||
|
|
|
@ -158,6 +158,7 @@ MetroApp::OnAsyncTileCreated(ABI::Windows::Foundation::IAsyncOperation<bool>* aO
|
|||
AsyncStatus aStatus)
|
||||
{
|
||||
WinUtils::Log("Async operation status: %d", aStatus);
|
||||
MetroUtils::FireObserver("metro_on_async_tile_created");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче