зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound a=merge
This commit is contained in:
Коммит
ea0415b62f
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1036b544b7e102592bd9fab95cd9317329ac1293"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -133,7 +133,7 @@
|
|||
<project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
|
||||
<project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
|
||||
<project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
|
||||
<project name="kernel/common" path="kernel" revision="f365109310138f85bb91884b7dee60f6f0da042d"/>
|
||||
<project name="kernel/common" path="kernel" revision="250294fb70e018b0966402f744ff9705109f8635"/>
|
||||
<project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
|
||||
<project name="u-boot" path="u-boot" revision="982c1fd67b89d5573317c1796cf5b0143de44e8a"/>
|
||||
<project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="6974f8e771d4d8e910357a6739ab124768891e8f"/>
|
||||
|
|
|
@ -19,7 +19,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="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1036b544b7e102592bd9fab95cd9317329ac1293"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1036b544b7e102592bd9fab95cd9317329ac1293"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1036b544b7e102592bd9fab95cd9317329ac1293"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -19,7 +19,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="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1036b544b7e102592bd9fab95cd9317329ac1293"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1036b544b7e102592bd9fab95cd9317329ac1293"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1036b544b7e102592bd9fab95cd9317329ac1293"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "ce7bf759bbf4ecdfa46b64cdccc16ccf7531178d",
|
||||
"revision": "ab9466a85acc108164bc17b9064387142b82d4da",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,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="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1036b544b7e102592bd9fab95cd9317329ac1293"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -15,7 +15,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="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1036b544b7e102592bd9fab95cd9317329ac1293"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1036b544b7e102592bd9fab95cd9317329ac1293"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
|
||||
|
|
|
@ -17,7 +17,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="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1036b544b7e102592bd9fab95cd9317329ac1293"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -1292,6 +1292,9 @@ pref("services.sync.prefs.sync.spellchecker.dictionary", true);
|
|||
pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
|
||||
#endif
|
||||
|
||||
// Developer edition preferences
|
||||
pref("browser.devedition.theme.enabled", false);
|
||||
|
||||
// Disable the error console
|
||||
pref("devtools.errorconsole.enabled", false);
|
||||
|
||||
|
@ -1604,6 +1607,7 @@ pref("loop.soft_start_hostname", "soft-start.loop.services.mozilla.com");
|
|||
|
||||
pref("loop.server", "https://loop.services.mozilla.com");
|
||||
pref("loop.seenToS", "unseen");
|
||||
pref("loop.learnMoreUrl", "https://www.firefox.com/hello/");
|
||||
pref("loop.legal.ToS_url", "https://call.mozilla.com/legal/terms/");
|
||||
pref("loop.legal.privacy_url", "https://www.mozilla.org/privacy/");
|
||||
pref("loop.do_not_disturb", false);
|
||||
|
@ -1632,6 +1636,15 @@ pref("services.push.serverURL", "wss://push.services.mozilla.com/");
|
|||
|
||||
pref("social.sidebar.unload_timeout_ms", 10000);
|
||||
|
||||
// activation from inside of share panel is possible if activationPanelEnabled
|
||||
// is true. Pref'd off for release while usage testing is done through beta.
|
||||
#ifdef EARLY_BETA_OR_EARLIER
|
||||
pref("social.share.activationPanelEnabled", true);
|
||||
#else
|
||||
pref("social.share.activationPanelEnabled", false);
|
||||
#endif
|
||||
pref("social.shareDirectory", "https://activations.cdn.mozilla.net/sharePanel.html");
|
||||
|
||||
pref("dom.identity.enabled", false);
|
||||
|
||||
// Block insecure active content on https pages
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- 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/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % htmlDTD
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"DTD/xhtml1-strict.dtd">
|
||||
%htmlDTD;
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
|
||||
%brandDTD;
|
||||
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
|
||||
%browserDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&social.directory.label;</title>
|
||||
<link rel="stylesheet" type="text/css" media="all"
|
||||
href="chrome://browser/skin/aboutProviderDirectory.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="activation-link" hidden="true">
|
||||
<div id="message-box">
|
||||
<p>&social.directory.text;</p>
|
||||
</div>
|
||||
<div id="button-box">
|
||||
<button onclick="openDirectory()">&social.directory.button;</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="activation" hidden="true">
|
||||
<p>&social.directory.introText;</p>
|
||||
<div><iframe id="activation-frame"/></div>
|
||||
<p><a class="link" onclick="openDirectory()">&social.directory.viewmore.text;</a></p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script type="text/javascript;version=1.8"><![CDATA[
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function openDirectory() {
|
||||
let url = Services.prefs.getCharPref("social.directories").split(',')[0];
|
||||
window.open(url);
|
||||
window.close();
|
||||
}
|
||||
|
||||
if (Services.prefs.getBoolPref("social.share.activationPanelEnabled")) {
|
||||
let url = Services.prefs.getCharPref("social.shareDirectory");
|
||||
document.getElementById("activation-frame").setAttribute("src", url);
|
||||
document.getElementById("activation").removeAttribute("hidden");
|
||||
} else {
|
||||
document.getElementById("activation-link").removeAttribute("hidden");
|
||||
}
|
||||
]]></script>
|
||||
</html>
|
|
@ -42,13 +42,16 @@
|
|||
}
|
||||
|
||||
function parseQueryString() {
|
||||
let url = document.documentURI;
|
||||
let queryString = url.replace(/^about:socialerror\??/, "");
|
||||
|
||||
let modeMatch = queryString.match(/mode=([^&]+)/);
|
||||
let mode = modeMatch && modeMatch[1] ? modeMatch[1] : "";
|
||||
let originMatch = queryString.match(/origin=([^&]+)/);
|
||||
config.origin = originMatch && originMatch[1] ? decodeURIComponent(originMatch[1]) : "";
|
||||
let searchParams = new URLSearchParams(location.href.split("?")[1]);
|
||||
let mode = searchParams.get("mode");
|
||||
config.directory = searchParams.get("directory");
|
||||
config.origin = searchParams.get("origin");
|
||||
let encodedURL = searchParams.get("url");
|
||||
let url = decodeURIComponent(encodedURL);
|
||||
if (config.directory) {
|
||||
let URI = Services.io.newURI(url, null, null);
|
||||
config.origin = Services.scriptSecurityManager.getNoAppCodebasePrincipal(URI).origin;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case "compactInfo":
|
||||
|
@ -59,10 +62,6 @@
|
|||
document.getElementById("btnCloseSidebar").style.display = 'none';
|
||||
//intentional fall-through
|
||||
case "tryAgain":
|
||||
let urlMatch = queryString.match(/url=([^&]+)/);
|
||||
let encodedURL = urlMatch && urlMatch[1] ? urlMatch[1] : "";
|
||||
url = decodeURIComponent(encodedURL);
|
||||
|
||||
config.tryAgainCallback = loadQueryURL;
|
||||
config.queryURL = url;
|
||||
break;
|
||||
|
@ -80,7 +79,7 @@
|
|||
|
||||
let productName = brandBundle.GetStringFromName("brandShortName");
|
||||
let provider = Social._getProviderFromOrigin(config.origin);
|
||||
let providerName = provider && provider.name;
|
||||
let providerName = provider ? provider.name : config.origin;
|
||||
|
||||
// Sets up the error message
|
||||
let msg = browserBundle.formatStringFromName("social.error.message", [productName, providerName], 2);
|
||||
|
|
|
@ -72,8 +72,8 @@
|
|||
accesskey="&bookmarkThisLinkCmd.accesskey;"
|
||||
oncommand="gContextMenu.bookmarkLink();"/>
|
||||
<menuitem id="context-sharelink"
|
||||
label="&shareLinkCmd.label;"
|
||||
accesskey="&shareLinkCmd.accesskey;"
|
||||
label="&shareLink.label;"
|
||||
accesskey="&shareLink.accesskey;"
|
||||
oncommand="gContextMenu.shareLink();"/>
|
||||
<menuitem id="context-savelink"
|
||||
label="&saveLinkCmd.label;"
|
||||
|
@ -200,8 +200,8 @@
|
|||
accesskey="&saveImageCmd.accesskey;"
|
||||
oncommand="gContextMenu.saveMedia();"/>
|
||||
<menuitem id="context-shareimage"
|
||||
label="&shareImageCmd.label;"
|
||||
accesskey="&shareImageCmd.accesskey;"
|
||||
label="&shareImage.label;"
|
||||
accesskey="&shareImage.accesskey;"
|
||||
oncommand="gContextMenu.shareImage();"/>
|
||||
<menuitem id="context-sendimage"
|
||||
label="&emailImageCmd.label;"
|
||||
|
@ -225,8 +225,8 @@
|
|||
accesskey="&saveVideoCmd.accesskey;"
|
||||
oncommand="gContextMenu.saveMedia();"/>
|
||||
<menuitem id="context-sharevideo"
|
||||
label="&shareVideoCmd.label;"
|
||||
accesskey="&shareVideoCmd.accesskey;"
|
||||
label="&shareVideo.label;"
|
||||
accesskey="&shareVideo.accesskey;"
|
||||
oncommand="gContextMenu.shareVideo();"/>
|
||||
<menuitem id="context-saveaudio"
|
||||
label="&saveAudioCmd.label;"
|
||||
|
@ -305,8 +305,8 @@
|
|||
<menuitem id="context-searchselect"
|
||||
oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms);"/>
|
||||
<menuitem id="context-shareselect"
|
||||
label="&shareSelectCmd.label;"
|
||||
accesskey="&shareSelectCmd.accesskey;"
|
||||
label="&shareSelect.label;"
|
||||
accesskey="&shareSelect.accesskey;"
|
||||
oncommand="gContextMenu.shareSelect(getBrowserSelection());"/>
|
||||
<menuseparator id="frame-sep"/>
|
||||
<menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
# 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/.
|
||||
|
||||
/**
|
||||
* Listeners for the DevEdition theme. This adds an extra stylesheet
|
||||
* to browser.xul if a pref is set and no other themes are applied.
|
||||
*/
|
||||
let DevEdition = {
|
||||
_prefName: "browser.devedition.theme.enabled",
|
||||
_themePrefName: "general.skins.selectedSkin",
|
||||
_lwThemePrefName: "lightweightThemes.isThemeSelected",
|
||||
_devtoolsThemePrefName: "devtools.theme",
|
||||
|
||||
styleSheetLocation: "chrome://browser/skin/devedition.css",
|
||||
styleSheet: null,
|
||||
|
||||
init: function () {
|
||||
this._updateDevtoolsThemeAttribute();
|
||||
this._updateStyleSheet();
|
||||
|
||||
// Listen for changes to all prefs except for complete themes.
|
||||
// No need for this since changing a complete theme requires a
|
||||
// restart.
|
||||
Services.prefs.addObserver(this._lwThemePrefName, this, false);
|
||||
Services.prefs.addObserver(this._prefName, this, false);
|
||||
Services.prefs.addObserver(this._devtoolsThemePrefName, this, false);
|
||||
},
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
if (topic == "nsPref:changed") {
|
||||
if (data == this._devtoolsThemePrefName) {
|
||||
this._updateDevtoolsThemeAttribute();
|
||||
} else {
|
||||
this._updateStyleSheet();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_updateDevtoolsThemeAttribute: function() {
|
||||
// Set an attribute on root element to make it possible
|
||||
// to change colors based on the selected devtools theme.
|
||||
document.documentElement.setAttribute("devtoolstheme",
|
||||
Services.prefs.getCharPref(this._devtoolsThemePrefName));
|
||||
},
|
||||
|
||||
_updateStyleSheet: function() {
|
||||
// Only try to apply the dev edition theme if it is preffered
|
||||
// on and there are no other themes applied.
|
||||
let lightweightThemeSelected = false;
|
||||
try {
|
||||
lightweightThemeSelected = Services.prefs.getBoolPref(this._lwThemePrefName);
|
||||
} catch(e) {}
|
||||
|
||||
let defaultThemeSelected = false;
|
||||
try {
|
||||
defaultThemeSelected = Services.prefs.getCharPref(this._themePrefName) == "classic/1.0";
|
||||
} catch(e) {}
|
||||
|
||||
let deveditionThemeEnabled = Services.prefs.getBoolPref(this._prefName) &&
|
||||
!lightweightThemeSelected && defaultThemeSelected;
|
||||
|
||||
if (deveditionThemeEnabled && !this.styleSheet) {
|
||||
let styleSheetAttr = `href="${this.styleSheetLocation}" type="text/css"`;
|
||||
let styleSheet = this.styleSheet = document.createProcessingInstruction(
|
||||
'xml-stylesheet', styleSheetAttr);
|
||||
this.styleSheet.addEventListener("load", function onLoad() {
|
||||
styleSheet.removeEventListener("load", onLoad);
|
||||
ToolbarIconColor.inferFromText();
|
||||
});
|
||||
document.insertBefore(this.styleSheet, document.documentElement);
|
||||
} else if (!deveditionThemeEnabled && this.styleSheet) {
|
||||
this.styleSheet.remove();
|
||||
this.styleSheet = null;
|
||||
ToolbarIconColor.inferFromText();
|
||||
}
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
Services.prefs.removeObserver(this._lwThemePrefName, this);
|
||||
Services.prefs.removeObserver(this._prefName, this);
|
||||
Services.prefs.removeObserver(this._devtoolsThemePrefName, this);
|
||||
this.styleSheet = null;
|
||||
}
|
||||
};
|
|
@ -119,7 +119,7 @@
|
|||
#endif
|
||||
<command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
|
||||
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
|
||||
<command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
|
||||
<command id="Social:SharePage" oncommand="SocialShare.sharePage();"/>
|
||||
<command id="Social:ToggleSidebar" oncommand="SocialSidebar.toggleSidebar();" hidden="true"/>
|
||||
<command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
|
||||
<command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
|
||||
|
@ -134,6 +134,7 @@
|
|||
</commandset>
|
||||
|
||||
<broadcasterset id="mainBroadcasterSet">
|
||||
<broadcaster id="Social:PageShareOrMark" disabled="true"/>
|
||||
<broadcaster id="viewBookmarksSidebar" autoCheck="false" label="&bookmarksButton.label;"
|
||||
type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/bookmarks/bookmarksPanel.xul"
|
||||
oncommand="toggleSidebar('viewBookmarksSidebar');"/>
|
||||
|
|
|
@ -48,6 +48,12 @@ XPCOMUtils.defineLazyGetter(this, "CreateSocialMarkWidget", function() {
|
|||
return tmp.CreateSocialMarkWidget;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "hookWindowCloseForPanelClose", function() {
|
||||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/MozSocialAPI.jsm", tmp);
|
||||
return tmp.hookWindowCloseForPanelClose;
|
||||
});
|
||||
|
||||
SocialUI = {
|
||||
_initialized: false,
|
||||
|
||||
|
@ -68,7 +74,7 @@ SocialUI = {
|
|||
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
|
||||
|
||||
gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler.bind(this), true, true);
|
||||
PanelUI.panel.addEventListener("popupshown", SocialUI.updatePanelState, true);
|
||||
CustomizableUI.addListener(this);
|
||||
|
||||
// menupopups that list social providers. we only populate them when shown,
|
||||
// and if it has not been done already.
|
||||
|
@ -101,8 +107,8 @@ SocialUI = {
|
|||
Services.obs.removeObserver(this, "social:provider-disabled");
|
||||
|
||||
Services.prefs.removeObserver("social.toast-notifications.enabled", this);
|
||||
CustomizableUI.removeListener(this);
|
||||
|
||||
PanelUI.panel.removeEventListener("popupshown", SocialUI.updatePanelState, true);
|
||||
document.getElementById("viewSidebarMenu").removeEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
|
||||
document.getElementById("social-statusarea-popup").removeEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
|
||||
|
||||
|
@ -166,7 +172,6 @@ SocialUI = {
|
|||
SocialShare.populateProviderMenu();
|
||||
SocialStatus.populateToolbarPalette();
|
||||
SocialMarks.populateToolbarPalette();
|
||||
SocialShare.update();
|
||||
},
|
||||
|
||||
// This handles "ActivateSocialFeature" events fired against content documents
|
||||
|
@ -174,7 +179,7 @@ SocialUI = {
|
|||
// about:home or the share panel, we bypass the enable prompt. Any website
|
||||
// activation, such as from the activations directory or a providers website
|
||||
// will still get the prompt.
|
||||
_activationEventHandler: function SocialUI_activationHandler(e, aBypassUserEnable=false) {
|
||||
_activationEventHandler: function SocialUI_activationHandler(e, options={}) {
|
||||
let targetDoc;
|
||||
let node;
|
||||
if (e.target instanceof HTMLDocument) {
|
||||
|
@ -188,7 +193,9 @@ SocialUI = {
|
|||
if (!(targetDoc instanceof HTMLDocument))
|
||||
return;
|
||||
|
||||
if (!aBypassUserEnable && targetDoc.defaultView != content)
|
||||
// The share panel iframe will not match "content" so it passes a bypass
|
||||
// flag
|
||||
if (!options.bypassContentCheck && targetDoc.defaultView != content)
|
||||
return;
|
||||
|
||||
// If we are in PB mode, we silently do nothing (bug 829404 exists to
|
||||
|
@ -224,11 +231,36 @@ SocialUI = {
|
|||
if (provider.sidebarURL) {
|
||||
SocialSidebar.show(provider.origin);
|
||||
}
|
||||
if (provider.shareURL) {
|
||||
// Ensure that the share button is somewhere usable.
|
||||
// SocialShare.shareButton may return null if it is in the menu-panel
|
||||
// and has never been visible, so we check the widget directly. If
|
||||
// there is no area for the widget we move it into the toolbar.
|
||||
let widget = CustomizableUI.getWidget("social-share-button");
|
||||
if (!widget.areaType) {
|
||||
CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
|
||||
// ensure correct state
|
||||
SocialUI.onCustomizeEnd(window);
|
||||
}
|
||||
|
||||
// make this new provider the selected provider. If the panel hasn't
|
||||
// been opened, we need to make the frame first.
|
||||
SocialShare._createFrame();
|
||||
SocialShare.iframe.setAttribute('src', 'data:text/plain;charset=utf8,');
|
||||
SocialShare.iframe.setAttribute('origin', provider.origin);
|
||||
// get the right button selected
|
||||
SocialShare.populateProviderMenu();
|
||||
if (SocialShare.panel.state == "open") {
|
||||
SocialShare.sharePage(provider.origin);
|
||||
}
|
||||
}
|
||||
if (provider.postActivationURL) {
|
||||
openUILinkIn(provider.postActivationURL, "tab");
|
||||
// if activated from an open share panel, we load the landing page in
|
||||
// a background tab
|
||||
gBrowser.loadOneTab(provider.postActivationURL, {inBackground: SocialShare.panel.state == "open"});
|
||||
}
|
||||
});
|
||||
}, aBypassUserEnable);
|
||||
}, options);
|
||||
},
|
||||
|
||||
showLearnMore: function() {
|
||||
|
@ -278,21 +310,49 @@ SocialUI = {
|
|||
return Social.providers.length > 0;
|
||||
},
|
||||
|
||||
updatePanelState :function(event) {
|
||||
// we only want to update when the panel is initially opened, not during
|
||||
// multiview changes
|
||||
if (event.target != PanelUI.panel)
|
||||
canShareOrMarkPage: function(aURI) {
|
||||
// Bug 898706 we do not enable social in private sessions since frameworker
|
||||
// would be shared between private and non-private windows
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(window))
|
||||
return false;
|
||||
|
||||
return (aURI && (aURI.schemeIs('http') || aURI.schemeIs('https')));
|
||||
},
|
||||
|
||||
onCustomizeEnd: function(aWindow) {
|
||||
if (aWindow != window)
|
||||
return;
|
||||
SocialUI.updateState();
|
||||
// customization mode gets buttons out of sync with command updating, fix
|
||||
// the disabled state
|
||||
let canShare = this.canShareOrMarkPage(gBrowser.currentURI);
|
||||
let shareButton = SocialShare.shareButton;
|
||||
if (shareButton) {
|
||||
if (canShare) {
|
||||
shareButton.removeAttribute("disabled")
|
||||
} else {
|
||||
shareButton.setAttribute("disabled", "true")
|
||||
}
|
||||
}
|
||||
// update the disabled state of the button based on the command
|
||||
for (let node of SocialMarks.nodes) {
|
||||
if (canShare) {
|
||||
node.removeAttribute("disabled")
|
||||
} else {
|
||||
node.setAttribute("disabled", "true")
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// called on tab/urlbar/location changes and after customization. Update
|
||||
// anything that is tab specific.
|
||||
updateState: function() {
|
||||
if (location == "about:customizing")
|
||||
return;
|
||||
goSetCommandEnabled("Social:PageShareOrMark", this.canShareOrMarkPage(gBrowser.currentURI));
|
||||
if (!SocialUI.enabled)
|
||||
return;
|
||||
// larger update that may change button icons
|
||||
SocialMarks.update();
|
||||
SocialShare.update();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,6 +493,12 @@ SocialFlyout = {
|
|||
}
|
||||
|
||||
SocialShare = {
|
||||
get _dynamicResizer() {
|
||||
delete this._dynamicResizer;
|
||||
this._dynamicResizer = new DynamicResizeWatcher();
|
||||
return this._dynamicResizer;
|
||||
},
|
||||
|
||||
// Share panel may be attached to the overflow or menu button depending on
|
||||
// customization, we need to manage open state of the anchor.
|
||||
get anchor() {
|
||||
|
@ -451,15 +517,22 @@ SocialShare = {
|
|||
return this.panel.lastChild;
|
||||
},
|
||||
|
||||
_activationHandler: function(event) {
|
||||
if (!Services.prefs.getBoolPref("social.share.activationPanelEnabled"))
|
||||
return;
|
||||
SocialUI._activationEventHandler(event, { bypassContentCheck: true, bypassInstallPanel: true });
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
if (this.iframe) {
|
||||
this.iframe.removeEventListener("ActivateSocialFeature", this._activationHandler, true, true);
|
||||
this.iframe.remove();
|
||||
}
|
||||
},
|
||||
|
||||
_createFrame: function() {
|
||||
let panel = this.panel;
|
||||
if (!SocialUI.enabled || this.iframe)
|
||||
if (this.iframe)
|
||||
return;
|
||||
this.panel.hidden = false;
|
||||
// create and initialize the panel for this window
|
||||
|
@ -471,6 +544,7 @@ SocialShare = {
|
|||
iframe.setAttribute("disableglobalhistory", "true");
|
||||
iframe.setAttribute("flex", "1");
|
||||
panel.appendChild(iframe);
|
||||
this.iframe.addEventListener("ActivateSocialFeature", this._activationHandler, true, true);
|
||||
this.populateProviderMenu();
|
||||
},
|
||||
|
||||
|
@ -480,34 +554,27 @@ SocialShare = {
|
|||
if (lastProviderOrigin) {
|
||||
provider = Social._getProviderFromOrigin(lastProviderOrigin);
|
||||
}
|
||||
// if they have a provider selected in the sidebar use that for the initial
|
||||
// default in share
|
||||
if (!provider)
|
||||
provider = SocialSidebar.provider;
|
||||
// if our provider has no shareURL, select the first one that does
|
||||
if (!provider || !provider.shareURL) {
|
||||
let providers = [p for (p of Social.providers) if (p.shareURL)];
|
||||
provider = providers.length > 0 && providers[0];
|
||||
}
|
||||
return provider;
|
||||
},
|
||||
|
||||
createTooltip: function(event) {
|
||||
let tt = event.target;
|
||||
let provider = Social._getProviderFromOrigin(tt.triggerNode.getAttribute("origin"));
|
||||
tt.firstChild.setAttribute("value", provider.name);
|
||||
tt.lastChild.setAttribute("value", provider.origin);
|
||||
},
|
||||
|
||||
populateProviderMenu: function() {
|
||||
if (!this.iframe)
|
||||
return;
|
||||
let providers = [p for (p of Social.providers) if (p.shareURL)];
|
||||
let hbox = document.getElementById("social-share-provider-buttons");
|
||||
// selectable providers are inserted before the provider-menu seperator,
|
||||
// remove any menuitems in that area
|
||||
while (hbox.firstChild) {
|
||||
// remove everything before the add-share-provider button (which should also
|
||||
// be lastChild if any share providers were added)
|
||||
let addButton = document.getElementById("add-share-provider");
|
||||
while (hbox.firstChild != addButton) {
|
||||
hbox.removeChild(hbox.firstChild);
|
||||
}
|
||||
// reset our share toolbar
|
||||
// only show a selection if there is more than one
|
||||
if (!SocialUI.enabled || providers.length < 2) {
|
||||
this.panel.firstChild.hidden = true;
|
||||
return;
|
||||
}
|
||||
let selectedProvider = this.getSelectedProvider();
|
||||
for (let provider of providers) {
|
||||
let button = document.createElement("toolbarbutton");
|
||||
|
@ -515,19 +582,18 @@ SocialShare = {
|
|||
button.setAttribute("type", "radio");
|
||||
button.setAttribute("group", "share-providers");
|
||||
button.setAttribute("image", provider.iconURL);
|
||||
button.setAttribute("tooltiptext", provider.name);
|
||||
button.setAttribute("tooltip", "share-button-tooltip");
|
||||
button.setAttribute("origin", provider.origin);
|
||||
button.setAttribute("oncommand", "SocialShare.sharePage(this.getAttribute('origin')); this.checked=true;");
|
||||
button.setAttribute("oncommand", "SocialShare.sharePage(this.getAttribute('origin'));");
|
||||
if (provider == selectedProvider) {
|
||||
this.defaultButton = button;
|
||||
}
|
||||
hbox.appendChild(button);
|
||||
hbox.insertBefore(button, addButton);
|
||||
}
|
||||
if (!this.defaultButton) {
|
||||
this.defaultButton = hbox.firstChild
|
||||
this.defaultButton = addButton;
|
||||
}
|
||||
this.defaultButton.setAttribute("checked", "true");
|
||||
this.panel.firstChild.hidden = false;
|
||||
},
|
||||
|
||||
get shareButton() {
|
||||
|
@ -542,42 +608,6 @@ SocialShare = {
|
|||
return widget.forWindow(window).node;
|
||||
},
|
||||
|
||||
canSharePage: function(aURI) {
|
||||
// we do not enable sharing from private sessions
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(window))
|
||||
return false;
|
||||
|
||||
if (!aURI || !(aURI.schemeIs('http') || aURI.schemeIs('https')))
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
|
||||
update: function() {
|
||||
let widget = CustomizableUI.getWidget("social-share-button");
|
||||
if (!widget)
|
||||
return;
|
||||
let shareButton = widget.forWindow(window).node;
|
||||
// hidden state is based on available share providers and location of
|
||||
// button. It's always visible and disabled in the customization palette.
|
||||
shareButton.hidden = !SocialUI.enabled || (widget.areaType &&
|
||||
[p for (p of Social.providers) if (p.shareURL)].length == 0);
|
||||
let disabled = !widget.areaType || shareButton.hidden || !this.canSharePage(gBrowser.currentURI);
|
||||
|
||||
// 1. update the relevent command's disabled state so the keyboard
|
||||
// shortcut only works when available.
|
||||
// 2. If the button has been relocated to a place that is not visible by
|
||||
// default (e.g. menu panel) then the disabled attribute will not update
|
||||
// correctly based on the command, so we update the attribute directly as.
|
||||
let cmd = document.getElementById("Social:SharePage");
|
||||
if (disabled) {
|
||||
cmd.setAttribute("disabled", "true");
|
||||
shareButton.setAttribute("disabled", "true");
|
||||
} else {
|
||||
cmd.removeAttribute("disabled");
|
||||
shareButton.removeAttribute("disabled");
|
||||
}
|
||||
},
|
||||
|
||||
_onclick: function() {
|
||||
Services.telemetry.getHistogramById("SOCIAL_PANEL_CLICKS").add(0);
|
||||
},
|
||||
|
@ -607,10 +637,15 @@ SocialShare = {
|
|||
if (!iframe)
|
||||
return;
|
||||
|
||||
iframe.removeAttribute("src");
|
||||
iframe.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
|
||||
encodeURIComponent(iframe.getAttribute("origin")),
|
||||
null, null, null, null);
|
||||
let url;
|
||||
let origin = iframe.getAttribute("origin");
|
||||
if (!origin) {
|
||||
// directory site is down
|
||||
url = "about:socialerror?mode=tryAgainOnly&directory=1&url=" + encodeURIComponent(iframe.getAttribute("src"));
|
||||
} else {
|
||||
url = "about:socialerror?mode=compactInfo&origin=" + encodeURIComponent(origin);
|
||||
}
|
||||
iframe.webNavigation.loadURI(url, null, null, null, null);
|
||||
sizeSocialPanelToContent(this.panel, iframe);
|
||||
},
|
||||
|
||||
|
@ -620,13 +655,6 @@ SocialShare = {
|
|||
// will call sharePage with an origin for us to switch to.
|
||||
this._createFrame();
|
||||
let iframe = this.iframe;
|
||||
let provider;
|
||||
if (providerOrigin)
|
||||
provider = Social._getProviderFromOrigin(providerOrigin);
|
||||
else
|
||||
provider = this.getSelectedProvider();
|
||||
if (!provider || !provider.shareURL)
|
||||
return;
|
||||
|
||||
// graphData is an optional param that either defines the full set of data
|
||||
// to be shared, or partial data about the current page. It is set by a call
|
||||
|
@ -636,7 +664,7 @@ SocialShare = {
|
|||
let pageData = graphData ? graphData : this.currentShare;
|
||||
let sharedURI = pageData ? Services.io.newURI(pageData.url, null, null) :
|
||||
gBrowser.currentURI;
|
||||
if (!this.canSharePage(sharedURI))
|
||||
if (!SocialUI.canShareOrMarkPage(sharedURI))
|
||||
return;
|
||||
|
||||
// the point of this action type is that we can use existing share
|
||||
|
@ -658,20 +686,26 @@ SocialShare = {
|
|||
}
|
||||
this.currentShare = pageData;
|
||||
|
||||
let provider;
|
||||
if (providerOrigin)
|
||||
provider = Social._getProviderFromOrigin(providerOrigin);
|
||||
else
|
||||
provider = this.getSelectedProvider();
|
||||
if (!provider || !provider.shareURL) {
|
||||
this.showDirectory();
|
||||
return;
|
||||
}
|
||||
// check the menu button
|
||||
let hbox = document.getElementById("social-share-provider-buttons");
|
||||
let btn = hbox.querySelector("[origin='" + provider.origin + "']");
|
||||
if (btn)
|
||||
btn.checked = true;
|
||||
|
||||
let shareEndpoint = OpenGraphBuilder.generateEndpointURL(provider.shareURL, pageData);
|
||||
|
||||
let size = provider.getPageSize("share");
|
||||
if (size) {
|
||||
if (this._dynamicResizer) {
|
||||
this._dynamicResizer.stop();
|
||||
this._dynamicResizer = null;
|
||||
}
|
||||
let {width, height} = size;
|
||||
width += this.panel.boxObject.width - iframe.boxObject.width;
|
||||
height += this.panel.boxObject.height - iframe.boxObject.height;
|
||||
this.panel.sizeTo(width, height);
|
||||
} else {
|
||||
this._dynamicResizer = new DynamicResizeWatcher();
|
||||
this._dynamicResizer.stop();
|
||||
}
|
||||
|
||||
// if we've already loaded this provider/page share endpoint, we don't want
|
||||
|
@ -683,7 +717,7 @@ SocialShare = {
|
|||
reload = shareEndpoint != iframe.contentDocument.location.spec;
|
||||
}
|
||||
if (!reload) {
|
||||
if (this._dynamicResizer)
|
||||
if (!size)
|
||||
this._dynamicResizer.start(this.panel, iframe);
|
||||
iframe.docShell.isActive = true;
|
||||
iframe.docShell.isAppTab = true;
|
||||
|
@ -701,7 +735,13 @@ SocialShare = {
|
|||
// should close the window when done.
|
||||
iframe.contentWindow.opener = iframe.contentWindow;
|
||||
setTimeout(function() {
|
||||
if (SocialShare._dynamicResizer) { // may go null if hidden quickly
|
||||
if (size) {
|
||||
let panel = SocialShare.panel;
|
||||
let {width, height} = size;
|
||||
width += panel.boxObject.width - iframe.boxObject.width;
|
||||
height += panel.boxObject.height - iframe.boxObject.height;
|
||||
panel.sizeTo(width, height);
|
||||
} else {
|
||||
SocialShare._dynamicResizer.start(iframe.parentNode, iframe);
|
||||
}
|
||||
}, 0);
|
||||
|
@ -722,10 +762,31 @@ SocialShare = {
|
|||
let uri = Services.io.newURI(shareEndpoint, null, null);
|
||||
iframe.setAttribute("origin", provider.origin);
|
||||
iframe.setAttribute("src", shareEndpoint);
|
||||
this._openPanel();
|
||||
},
|
||||
|
||||
showDirectory: function() {
|
||||
this._createFrame();
|
||||
let iframe = this.iframe;
|
||||
iframe.removeAttribute("origin");
|
||||
iframe.addEventListener("load", function panelBrowserOnload(e) {
|
||||
iframe.removeEventListener("load", panelBrowserOnload, true);
|
||||
hookWindowCloseForPanelClose(iframe.contentWindow);
|
||||
SocialShare._dynamicResizer.start(iframe.parentNode, iframe);
|
||||
|
||||
iframe.addEventListener("unload", function panelBrowserOnload(e) {
|
||||
iframe.removeEventListener("unload", panelBrowserOnload, true);
|
||||
SocialShare._dynamicResizer.stop();
|
||||
}, true);
|
||||
}, true);
|
||||
iframe.setAttribute("src", "about:providerdirectory");
|
||||
this._openPanel();
|
||||
},
|
||||
|
||||
_openPanel: function() {
|
||||
let anchor = document.getAnonymousElementByAttribute(this.anchor, "class", "toolbarbutton-icon");
|
||||
this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
|
||||
Social.setErrorListener(iframe, this.setErrorMessage.bind(this));
|
||||
Social.setErrorListener(this.iframe, this.setErrorMessage.bind(this));
|
||||
Services.telemetry.getHistogramById("SOCIAL_TOOLBAR_BUTTONS").add(0);
|
||||
}
|
||||
};
|
||||
|
@ -1306,20 +1367,27 @@ SocialStatus = {
|
|||
* Handles updates to toolbox and signals all buttons to update when necessary.
|
||||
*/
|
||||
SocialMarks = {
|
||||
update: function() {
|
||||
// querySelectorAll does not work on the menu panel the panel, so we have to
|
||||
// do this the hard way.
|
||||
let providers = SocialMarks.getProviders();
|
||||
get nodes() {
|
||||
let providers = [p for (p of Social.providers) if (p.markURL)];
|
||||
for (let p of providers) {
|
||||
let widgetId = SocialMarks._toolbarHelper.idFromOrigin(p.origin);
|
||||
let widget = CustomizableUI.getWidget(widgetId);
|
||||
if (!widget)
|
||||
continue;
|
||||
let node = widget.forWindow(window).node;
|
||||
if (node)
|
||||
yield node;
|
||||
}
|
||||
},
|
||||
update: function() {
|
||||
// querySelectorAll does not work on the menu panel, so we have to do this
|
||||
// the hard way.
|
||||
for (let node of this.nodes) {
|
||||
// xbl binding is not complete on startup when buttons are not in toolbar,
|
||||
// verify update is available
|
||||
if (node && node.update)
|
||||
if (node.update) {
|
||||
node.update();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -199,6 +199,7 @@ let gInitialPages = [
|
|||
|
||||
#include browser-addons.js
|
||||
#include browser-customization.js
|
||||
#include browser-devedition.js
|
||||
#include browser-feeds.js
|
||||
#include browser-fullScreen.js
|
||||
#include browser-fullZoom.js
|
||||
|
@ -787,7 +788,7 @@ function gKeywordURIFixup({ target: browser, data: fixupInfo }) {
|
|||
// Called when a docshell has attempted to load a page in an incorrect process.
|
||||
// This function is responsible for loading the page in the correct process.
|
||||
function RedirectLoad({ target: browser, data }) {
|
||||
let tab = gBrowser._getTabForBrowser(browser);
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
// Flush the tab state before getting it
|
||||
TabState.flush(browser);
|
||||
let tabState = JSON.parse(SessionStore.getTabState(tab));
|
||||
|
@ -834,6 +835,7 @@ var gBrowserInit = {
|
|||
gPageStyleMenu.init();
|
||||
LanguageDetectionListener.init();
|
||||
BrowserOnClick.init();
|
||||
DevEdition.init();
|
||||
|
||||
let mm = window.getGroupMessageManager("browsers");
|
||||
mm.loadFrameScript("chrome://browser/content/content.js", true);
|
||||
|
@ -1391,6 +1393,8 @@ var gBrowserInit = {
|
|||
|
||||
BrowserOnClick.uninit();
|
||||
|
||||
DevEdition.uninit();
|
||||
|
||||
var enumerator = Services.wm.getEnumerator(null);
|
||||
enumerator.getNext();
|
||||
if (!enumerator.hasMoreElements()) {
|
||||
|
@ -3770,7 +3774,7 @@ var XULBrowserWindow = {
|
|||
URLBarSetURI(aLocationURI);
|
||||
|
||||
BookmarkingUI.onLocationChange();
|
||||
SocialUI.updateState();
|
||||
SocialUI.updateState(location);
|
||||
}
|
||||
|
||||
// Utility functions for disabling find
|
||||
|
|
|
@ -246,7 +246,11 @@
|
|||
onpopuphidden="SocialShare.onHidden()"
|
||||
hidden="true">
|
||||
<vbox class="social-share-toolbar">
|
||||
<arrowscrollbox id="social-share-provider-buttons" orient="vertical" flex="1"/>
|
||||
<arrowscrollbox id="social-share-provider-buttons" orient="vertical" flex="1">
|
||||
<toolbarbutton id="add-share-provider" class="toolbarbutton share-provider-button" type="radio"
|
||||
group="share-providers" tooltiptext="&findShareServices.label;"
|
||||
oncommand="SocialShare.showDirectory()"/>
|
||||
</arrowscrollbox>
|
||||
</vbox>
|
||||
</panel>
|
||||
|
||||
|
@ -496,6 +500,11 @@
|
|||
#endif
|
||||
</tooltip>
|
||||
|
||||
<tooltip id="share-button-tooltip" onpopupshowing="SocialShare.createTooltip(event);">
|
||||
<label class="tooltip-label"/>
|
||||
<label class="tooltip-label"/>
|
||||
</tooltip>
|
||||
|
||||
#include popup-notifications.inc
|
||||
|
||||
#include ../../components/customizableui/content/panelUI.inc.xul
|
||||
|
@ -669,7 +678,7 @@
|
|||
aria-label="&navbarCmd.label;"
|
||||
fullscreentoolbar="true" mode="icons" customizable="true"
|
||||
iconsize="small"
|
||||
defaultset="urlbar-container,search-container,bookmarks-menu-button,downloads-button,home-button,loop-call-button,social-share-button,social-toolbar-item"
|
||||
defaultset="urlbar-container,search-container,bookmarks-menu-button,downloads-button,home-button,loop-call-button"
|
||||
customizationtarget="nav-bar-customization-target"
|
||||
overflowable="true"
|
||||
overflowbutton="nav-bar-overflow-button"
|
||||
|
@ -916,15 +925,6 @@
|
|||
onclick="BrowserGoHome(event);"
|
||||
cui-areatype="toolbar"
|
||||
aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
|
||||
|
||||
<toolbarbutton id="social-share-button"
|
||||
class="toolbarbutton-1 chromeclass-toolbar-additional"
|
||||
label="&sharePageCmd.label;"
|
||||
tooltiptext="&sharePageCmd.label;"
|
||||
cui-areatype="toolbar"
|
||||
removable="true"
|
||||
hidden="true"
|
||||
command="Social:SharePage"/>
|
||||
</hbox>
|
||||
|
||||
<toolbarbutton id="nav-bar-overflow-button"
|
||||
|
|
|
@ -345,7 +345,7 @@ nsContextMenu.prototype = {
|
|||
let shareEnabled = shareButton && !shareButton.disabled && !this.onSocial;
|
||||
let pageShare = shareEnabled && !(this.isContentSelected ||
|
||||
this.onTextInput || this.onLink || this.onImage ||
|
||||
this.onVideo || this.onAudio);
|
||||
this.onVideo || this.onAudio || this.onCanvas);
|
||||
this.showItem("context-sharepage", pageShare);
|
||||
this.showItem("context-shareselect", shareEnabled && this.isContentSelected);
|
||||
this.showItem("context-sharelink", shareEnabled && (this.onLink || this.onPlainTextLink) && !this.onMailtoLink);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<binding id="toolbarbutton-marks" display="xul:button"
|
||||
extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
|
||||
<content disabled="true">
|
||||
<content>
|
||||
<xul:panel anonid="panel" hidden="true" type="arrow" class="social-panel"/>
|
||||
<xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/>
|
||||
<xul:label class="toolbarbutton-text" crop="right" flex="1"
|
||||
|
@ -113,16 +113,11 @@
|
|||
if (this._frame.docShell)
|
||||
this._frame.docShell.createAboutBlankContentViewer(null);
|
||||
|
||||
// do we have a savable page loaded?
|
||||
let aURI = gBrowser.currentURI;
|
||||
let disabled = !aURI || !(aURI.schemeIs('http') || aURI.schemeIs('https'));
|
||||
// when overflowed in toolbar, we must have the attribute set
|
||||
if (disabled) {
|
||||
this.setAttribute("disabled", "true");
|
||||
// disabled attr is set by Social:PageShareOrMark command
|
||||
if (this.hasAttribute("disabled")) {
|
||||
this.isMarked = false;
|
||||
} else {
|
||||
this.removeAttribute("disabled");
|
||||
Social.isURIMarked(provider.origin, aURI, (isMarked) => {
|
||||
Social.isURIMarked(provider.origin, gBrowser.currentURI, (isMarked) => {
|
||||
this.isMarked = isMarked;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -389,7 +389,7 @@
|
|||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler;
|
||||
return this._getTabForBrowser(browser);
|
||||
return this.getTabForBrowser(browser);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.browsers.length; i++) {
|
||||
|
|
|
@ -309,6 +309,7 @@ skip-if = e10s # Bug ????? - thumbnail captures need e10s love (tabPreviews_capt
|
|||
skip-if = e10s
|
||||
[browser_datareporting_notification.js]
|
||||
run-if = datareporting
|
||||
[browser_devedition.js]
|
||||
[browser_devices_get_user_media.js]
|
||||
skip-if = buildapp == 'mulet' || (os == "linux" && debug) || e10s # linux: bug 976544; e10s: Bug 973001 - appears user media notifications only happen in the child and don't make their way to the parent?
|
||||
[browser_devices_get_user_media_about_urls.js]
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Testing changes for Developer Edition theme.
|
||||
* A special stylesheet should be added to the browser.xul document
|
||||
* when browser.devedition.theme.enabled is set to true and no themes
|
||||
* are applied.
|
||||
*/
|
||||
|
||||
const PREF_DEVEDITION_THEME = "browser.devedition.theme.enabled";
|
||||
const PREF_THEME = "general.skins.selectedSkin";
|
||||
const PREF_LWTHEME = "lightweightThemes.isThemeSelected";
|
||||
const PREF_DEVTOOLS_THEME = "devtools.theme";
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
// Set preferences back to their original values
|
||||
Services.prefs.clearUserPref(PREF_DEVEDITION_THEME);
|
||||
Services.prefs.clearUserPref(PREF_THEME);
|
||||
Services.prefs.clearUserPref(PREF_LWTHEME);
|
||||
Services.prefs.clearUserPref(PREF_DEVTOOLS_THEME);
|
||||
});
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
startTests();
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
ok (!DevEdition.styleSheet, "There is no devedition style sheet by default.");
|
||||
|
||||
info ("Setting browser.devedition.theme.enabled to true.");
|
||||
Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
|
||||
ok (DevEdition.styleSheet, "There is a devedition stylesheet when no themes are applied and pref is set.");
|
||||
|
||||
info ("Adding a lightweight theme.");
|
||||
Services.prefs.setBoolPref(PREF_LWTHEME, true);
|
||||
ok (!DevEdition.styleSheet, "The devedition stylesheet has been removed when a lightweight theme is applied.");
|
||||
|
||||
info ("Removing a lightweight theme.");
|
||||
Services.prefs.setBoolPref(PREF_LWTHEME, false);
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet has been added when a lightweight theme is removed.");
|
||||
|
||||
// There are no listeners for the complete theme pref since applying the theme
|
||||
// requires a restart.
|
||||
info ("Setting general.skins.selectedSkin to a custom string.");
|
||||
Services.prefs.setCharPref(PREF_THEME, "custom-theme");
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet is still here when a complete theme is added.");
|
||||
|
||||
info ("Resetting general.skins.selectedSkin to default value.");
|
||||
Services.prefs.clearUserPref(PREF_THEME);
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet is still here when a complete theme is removed.");
|
||||
|
||||
info ("Setting browser.devedition.theme.enabled to false.");
|
||||
Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, false);
|
||||
ok (!DevEdition.styleSheet, "The devedition stylesheet has been removed.");
|
||||
|
||||
info ("Checking :root attributes based on devtools theme.");
|
||||
is (document.documentElement.getAttribute("devtoolstheme"), "light",
|
||||
"The documentElement has an attribute based on devtools theme.");
|
||||
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "dark");
|
||||
is (document.documentElement.getAttribute("devtoolstheme"), "dark",
|
||||
"The documentElement has an attribute based on devtools theme.");
|
||||
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "light");
|
||||
is (document.documentElement.getAttribute("devtoolstheme"), "light",
|
||||
"The documentElement has an attribute based on devtools theme.");
|
||||
|
||||
finish();
|
||||
}
|
|
@ -11,6 +11,7 @@ support-files =
|
|||
opengraph/shorturl_linkrel.html
|
||||
microdata.html
|
||||
share.html
|
||||
share_activate.html
|
||||
social_activate.html
|
||||
social_activate_iframe.html
|
||||
social_chat.html
|
||||
|
|
|
@ -19,7 +19,7 @@ let snippet =
|
|||
' "iconURL": "chrome://branding/content/icon16.png",' +
|
||||
' "icon32URL": "chrome://branding/content/favicon32.png",' +
|
||||
' "icon64URL": "chrome://branding/content/icon64.png",' +
|
||||
' "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",' +
|
||||
' "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",' +
|
||||
' "postActivationURL": "https://example.com/browser/browser/base/content/test/social/social_postActivation.html",' +
|
||||
' };' +
|
||||
' function activateProvider(node) {' +
|
||||
|
@ -41,7 +41,7 @@ let snippet2 =
|
|||
' "iconURL": "chrome://branding/content/icon16.png",' +
|
||||
' "icon32URL": "chrome://branding/content/favicon32.png",' +
|
||||
' "icon64URL": "chrome://branding/content/icon64.png",' +
|
||||
' "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",' +
|
||||
' "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",' +
|
||||
' "postActivationURL": "https://example.com/browser/browser/base/content/test/social/social_postActivation.html",' +
|
||||
' "oneclick": true' +
|
||||
' };' +
|
||||
|
|
|
@ -10,11 +10,47 @@ let manifest = { // normal provider
|
|||
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
|
||||
shareURL: "https://example.com/browser/browser/base/content/test/social/share.html"
|
||||
};
|
||||
let activationPage = "https://example.com/browser/browser/base/content/test/social/share_activate.html";
|
||||
|
||||
function sendActivationEvent(subframe) {
|
||||
// hack Social.lastEventReceived so we don't hit the "too many events" check.
|
||||
Social.lastEventReceived = 0;
|
||||
let doc = subframe.contentDocument;
|
||||
// if our test has a frame, use it
|
||||
let button = doc.getElementById("activation");
|
||||
ok(!!button, "got the activation button");
|
||||
EventUtils.synthesizeMouseAtCenter(button, {}, doc.defaultView);
|
||||
}
|
||||
|
||||
function promiseShareFrameEvent(iframe, eventName) {
|
||||
let deferred = Promise.defer();
|
||||
iframe.addEventListener(eventName, function load() {
|
||||
info("page load is " + iframe.contentDocument.location.href);
|
||||
if (iframe.contentDocument.location.href != "data:text/plain;charset=utf8,") {
|
||||
iframe.removeEventListener(eventName, load, true);
|
||||
deferred.resolve();
|
||||
}
|
||||
}, true);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
runSocialTests(tests);
|
||||
Services.prefs.setCharPref("social.shareDirectory", activationPage);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref("social.directories");
|
||||
Services.prefs.clearUserPref("social.shareDirectory");
|
||||
Services.prefs.clearUserPref("social.share.activationPanelEnabled");
|
||||
});
|
||||
runSocialTests(tests, undefined, function(next) {
|
||||
let shareButton = SocialShare.shareButton;
|
||||
if (shareButton) {
|
||||
CustomizableUI.removeWidgetFromArea("social-share-button", CustomizableUI.AREA_NAVBAR)
|
||||
shareButton.remove();
|
||||
}
|
||||
ok(CustomizableUI.inDefaultState, "Should start in default state.");
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
let corpus = [
|
||||
|
@ -72,16 +108,6 @@ let corpus = [
|
|||
}
|
||||
];
|
||||
|
||||
function loadURLInTab(url, callback) {
|
||||
info("Loading tab with "+url);
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(url);
|
||||
tab.linkedBrowser.addEventListener("load", function listener() {
|
||||
is(tab.linkedBrowser.currentURI.spec, url, "tab loaded")
|
||||
tab.linkedBrowser.removeEventListener("load", listener, true);
|
||||
executeSoon(function() { callback(tab) });
|
||||
}, true);
|
||||
}
|
||||
|
||||
function hasoptions(testOptions, options) {
|
||||
let msg;
|
||||
for (let option in testOptions) {
|
||||
|
@ -105,12 +131,17 @@ var tests = {
|
|||
// starting on about:blank page, share should be visible but disabled when
|
||||
// adding provider
|
||||
is(gBrowser.contentDocument.location.href, "about:blank");
|
||||
|
||||
// initialize the button into the navbar
|
||||
CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
|
||||
// ensure correct state
|
||||
SocialUI.onCustomizeEnd(window);
|
||||
|
||||
SocialService.addProvider(manifest, function(provider) {
|
||||
is(SocialUI.enabled, true, "SocialUI is enabled");
|
||||
checkSocialUI();
|
||||
// share should not be enabled since we only have about:blank page
|
||||
let shareButton = SocialShare.shareButton;
|
||||
is(shareButton.disabled, true, "share button is disabled");
|
||||
// verify the attribute for proper css
|
||||
is(shareButton.getAttribute("disabled"), "true", "share button attribute is disabled");
|
||||
// button should be visible
|
||||
|
@ -121,14 +152,18 @@ var tests = {
|
|||
testShareEnabledOnActivation: function(next) {
|
||||
// starting from *some* page, share should be visible and enabled when
|
||||
// activating provider
|
||||
// initialize the button into the navbar
|
||||
CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
|
||||
// ensure correct state
|
||||
SocialUI.onCustomizeEnd(window);
|
||||
|
||||
let testData = corpus[0];
|
||||
loadURLInTab(testData.url, function(tab) {
|
||||
addTab(testData.url, function(tab) {
|
||||
SocialService.addProvider(manifest, function(provider) {
|
||||
is(SocialUI.enabled, true, "SocialUI is enabled");
|
||||
checkSocialUI();
|
||||
// share should not be enabled since we only have about:blank page
|
||||
let shareButton = SocialShare.shareButton;
|
||||
is(shareButton.disabled, false, "share button is enabled");
|
||||
// verify the attribute for proper css
|
||||
ok(!shareButton.hasAttribute("disabled"), "share button is enabled");
|
||||
// button should be visible
|
||||
|
@ -147,9 +182,9 @@ var tests = {
|
|||
let testData = corpus[testIndex++];
|
||||
|
||||
function runOneTest() {
|
||||
loadURLInTab(testData.url, function(tab) {
|
||||
addTab(testData.url, function(tab) {
|
||||
testTab = tab;
|
||||
SocialShare.sharePage();
|
||||
SocialShare.sharePage(manifest.origin);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -241,5 +276,48 @@ var tests = {
|
|||
SocialShare.sharePage(manifest.origin, null, target);
|
||||
});
|
||||
});
|
||||
},
|
||||
testSharePanelActivation: function(next) {
|
||||
let testTab;
|
||||
// cleared in the cleanup function
|
||||
Services.prefs.setCharPref("social.directories", "https://example.com");
|
||||
Services.prefs.setBoolPref("social.share.activationPanelEnabled", true);
|
||||
// make the iframe so we can wait on the load
|
||||
SocialShare._createFrame();
|
||||
let iframe = SocialShare.iframe;
|
||||
|
||||
promiseShareFrameEvent(iframe, "load").then(() => {
|
||||
let subframe = iframe.contentDocument.getElementById("activation-frame");
|
||||
waitForCondition(() => {
|
||||
// sometimes the iframe is ready before the panel is open, we need to
|
||||
// wait for both conditions
|
||||
return SocialShare.panel.state == "open"
|
||||
&& subframe.contentDocument
|
||||
&& subframe.contentDocument.readyState == "complete";
|
||||
}, () => {
|
||||
is(subframe.contentDocument.location.href, activationPage, "activation page loaded");
|
||||
promiseObserverNotified("social:provider-enabled").then(() => {
|
||||
let provider = Social._getProviderFromOrigin(manifest.origin);
|
||||
let port = provider.getWorkerPort();
|
||||
ok(!!port, "got port");
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
switch (topic) {
|
||||
case "got-share-data-message":
|
||||
ok(true, "share completed");
|
||||
gBrowser.removeTab(testTab);
|
||||
SocialService.uninstallProvider(manifest.origin, next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
port.postMessage({topic: "test-init"});
|
||||
});
|
||||
sendActivationEvent(subframe);
|
||||
}, "share panel did not open and load share page");
|
||||
});
|
||||
addTab(activationPage, function(tab) {
|
||||
testTab = tab;
|
||||
SocialShare.sharePage();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,13 +28,19 @@ let manifests = [
|
|||
}
|
||||
];
|
||||
|
||||
let ports = [];
|
||||
function getProviderPort(provider) {
|
||||
let port = provider.getWorkerPort();
|
||||
ok(port, "provider has a port");
|
||||
ports.push(port);
|
||||
return port;
|
||||
}
|
||||
let chatId = 0;
|
||||
function openChat(provider, callback) {
|
||||
let chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
|
||||
let port = provider.getWorkerPort();
|
||||
let port = getProviderPort(provider);
|
||||
port.onmessage = function(e) {
|
||||
if (e.data.topic == "got-chatbox-message") {
|
||||
port.close();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +48,7 @@ function openChat(provider, callback) {
|
|||
port.postMessage({topic: "test-init"});
|
||||
port.postMessage({topic: "test-worker-chat", data: url});
|
||||
gURLsNotRemembered.push(url);
|
||||
return port;
|
||||
}
|
||||
|
||||
function windowHasChats(win) {
|
||||
|
@ -56,15 +63,22 @@ function test() {
|
|||
let oldleft = window.screenX;
|
||||
window.moveTo(0, window.screenY)
|
||||
let postSubTest = function(cb) {
|
||||
// ensure ports are closed
|
||||
for (let port of ports) {
|
||||
port.close()
|
||||
ok(port._closed, "port closed");
|
||||
}
|
||||
ports = [];
|
||||
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
ok(chats.children.length == 0, "no chatty children left behind");
|
||||
cb();
|
||||
};
|
||||
runSocialTestWithProvider(manifests, function (finishcb) {
|
||||
ok(Social.enabled, "Social is enabled");
|
||||
ok(Social.providers[0].getWorkerPort(), "provider 0 has port");
|
||||
ok(Social.providers[1].getWorkerPort(), "provider 1 has port");
|
||||
ok(Social.providers[2].getWorkerPort(), "provider 2 has port");
|
||||
ok(getProviderPort(Social.providers[0]), "provider 0 has port");
|
||||
ok(getProviderPort(Social.providers[1]), "provider 1 has port");
|
||||
ok(getProviderPort(Social.providers[2]), "provider 2 has port");
|
||||
SocialSidebar.show();
|
||||
runSocialTests(tests, undefined, postSubTest, function() {
|
||||
window.moveTo(oldleft, window.screenY)
|
||||
|
@ -77,8 +91,7 @@ function test() {
|
|||
var tests = {
|
||||
testOpenCloseChat: function(next) {
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
ok(port, "provider has a port");
|
||||
let port = getProviderPort(SocialSidebar.provider);
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
switch (topic) {
|
||||
|
@ -96,7 +109,6 @@ var tests = {
|
|||
content.addEventListener("unload", function chatUnload() {
|
||||
content.removeEventListener("unload", chatUnload, true);
|
||||
ok(true, "got chatbox unload on close");
|
||||
port.close();
|
||||
next();
|
||||
}, true);
|
||||
chats.selectedChat.close();
|
||||
|
@ -114,8 +126,7 @@ var tests = {
|
|||
testWorkerChatWindow: function(next) {
|
||||
const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
ok(port, "provider has a port");
|
||||
let port = getProviderPort(SocialSidebar.provider);
|
||||
port.postMessage({topic: "test-init"});
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
|
@ -128,7 +139,6 @@ var tests = {
|
|||
}
|
||||
ok(!chats.selectedChat, "chats are all closed");
|
||||
gURLsNotRemembered.push(chatUrl);
|
||||
port.close();
|
||||
next();
|
||||
break;
|
||||
}
|
||||
|
@ -138,8 +148,7 @@ var tests = {
|
|||
},
|
||||
testCloseSelf: function(next) {
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
ok(port, "provider has a port");
|
||||
let port = getProviderPort(SocialSidebar.provider);
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
switch (topic) {
|
||||
|
@ -157,7 +166,6 @@ var tests = {
|
|||
evt.initCustomEvent("socialTest-CloseSelf", true, true, {});
|
||||
doc.documentElement.dispatchEvent(evt);
|
||||
ok(!chat.parentNode, "chat is now closed");
|
||||
port.close();
|
||||
next();
|
||||
break;
|
||||
}
|
||||
|
@ -172,6 +180,8 @@ var tests = {
|
|||
let num = 0;
|
||||
is(chatbar.childNodes.length, 0, "chatbar starting empty");
|
||||
is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
|
||||
let port = getProviderPort(SocialSidebar.provider);
|
||||
port.postMessage({topic: "test-init"});
|
||||
|
||||
makeChat("normal", "first chat", function() {
|
||||
// got the first one.
|
||||
|
@ -202,22 +212,21 @@ var tests = {
|
|||
},
|
||||
|
||||
testShowWhenCollapsed: function(next) {
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
let port = getProviderPort(SocialSidebar.provider);
|
||||
port.postMessage({topic: "test-init"});
|
||||
get3ChatsForCollapsing("normal", function(first, second, third) {
|
||||
let chatbar = getChatBar();
|
||||
chatbar.showChat(first);
|
||||
ok(!first.collapsed, "first should no longer be collapsed");
|
||||
ok(second.collapsed || third.collapsed, false, "one of the others should be collapsed");
|
||||
is(second.collapsed || third.collapsed, true, "one of the others should be collapsed");
|
||||
closeAllChats();
|
||||
port.close();
|
||||
next();
|
||||
});
|
||||
},
|
||||
|
||||
testOnlyOneCallback: function(next) {
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
let port = getProviderPort(SocialSidebar.provider);
|
||||
let numOpened = 0;
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
|
@ -233,7 +242,6 @@ var tests = {
|
|||
executeSoon(function() {
|
||||
is(numOpened, 1, "only got one open message");
|
||||
chats.selectedChat.close();
|
||||
port.close();
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
@ -243,24 +251,21 @@ var tests = {
|
|||
|
||||
testMultipleProviderChat: function(next) {
|
||||
// test incomming chats from all providers
|
||||
openChat(Social.providers[0], function() {
|
||||
openChat(Social.providers[1], function() {
|
||||
openChat(Social.providers[2], function() {
|
||||
let port0 = openChat(Social.providers[0], function() {
|
||||
let port1 = openChat(Social.providers[1], function() {
|
||||
let port2 = openChat(Social.providers[2], function() {
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
waitForCondition(function() chats.children.length == Social.providers.length,
|
||||
function() {
|
||||
ok(true, "one chat window per provider opened");
|
||||
// test logout of a single provider
|
||||
let provider = Social.providers[2];
|
||||
let port = provider.getWorkerPort();
|
||||
port.postMessage({topic: "test-logout"});
|
||||
port2.postMessage({topic: "test-logout"});
|
||||
waitForCondition(function() chats.children.length == Social.providers.length - 1,
|
||||
function() {
|
||||
closeAllChats();
|
||||
waitForCondition(function() chats.children.length == 0,
|
||||
function() {
|
||||
ok(!chats.selectedChat, "multiprovider chats are all closed");
|
||||
port.close();
|
||||
next();
|
||||
},
|
||||
"chat windows didn't close");
|
||||
|
@ -277,6 +282,7 @@ var tests = {
|
|||
testCloseOnLogout: function(next) {
|
||||
const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
|
||||
let port = SocialSidebar.provider.getWorkerPort();
|
||||
ports.push(port);
|
||||
ok(port, "provider has a port");
|
||||
let opened = false;
|
||||
port.onmessage = function (e) {
|
||||
|
@ -292,7 +298,6 @@ var tests = {
|
|||
port.postMessage({topic: "test-logout"});
|
||||
waitForCondition(function() document.getElementById("pinnedchats").firstChild == null,
|
||||
function() {
|
||||
port.close();
|
||||
next();
|
||||
},
|
||||
"chat windows didn't close");
|
||||
|
|
|
@ -15,34 +15,55 @@ let openChatWindow = Cu.import("resource://gre/modules/MozSocialAPI.jsm", {}).op
|
|||
// (via browser/base/content/test/browser_bookmark_titles.js)
|
||||
let origProxyType = Services.prefs.getIntPref('network.proxy.type');
|
||||
|
||||
function toggleOfflineStatus(goOffline) {
|
||||
// Bug 968887 fix. when going on/offline, wait for notification before continuing
|
||||
let deferred = Promise.defer();
|
||||
if (!goOffline) {
|
||||
Services.prefs.setIntPref('network.proxy.type', origProxyType);
|
||||
}
|
||||
if (goOffline != Services.io.offline) {
|
||||
info("initial offline state " + Services.io.offline);
|
||||
let expect = !Services.io.offline;
|
||||
Services.obs.addObserver(function offlineChange(subject, topic, data) {
|
||||
Services.obs.removeObserver(offlineChange, "network:offline-status-changed");
|
||||
info("offline state changed to " + Services.io.offline);
|
||||
is(expect, Services.io.offline, "network:offline-status-changed successful toggle");
|
||||
deferred.resolve();
|
||||
}, "network:offline-status-changed", false);
|
||||
BrowserOffline.toggleOfflineStatus();
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
if (goOffline) {
|
||||
Services.prefs.setIntPref('network.proxy.type', 0);
|
||||
// LOAD_FLAGS_BYPASS_CACHE isn't good enough. So clear the cache.
|
||||
Services.cache2.clear();
|
||||
}
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function goOffline() {
|
||||
// Simulate a network outage with offline mode. (Localhost is still
|
||||
// accessible in offline mode, so disable the test proxy as well.)
|
||||
if (!Services.io.offline)
|
||||
BrowserOffline.toggleOfflineStatus();
|
||||
Services.prefs.setIntPref('network.proxy.type', 0);
|
||||
// LOAD_FLAGS_BYPASS_CACHE isn't good enough. So clear the cache.
|
||||
Services.cache2.clear();
|
||||
return toggleOfflineStatus(true);
|
||||
}
|
||||
|
||||
function goOnline(callback) {
|
||||
Services.prefs.setIntPref('network.proxy.type', origProxyType);
|
||||
if (Services.io.offline)
|
||||
BrowserOffline.toggleOfflineStatus();
|
||||
if (callback)
|
||||
callback();
|
||||
return toggleOfflineStatus(false);
|
||||
}
|
||||
|
||||
function openPanel(url, panelCallback, loadCallback) {
|
||||
// open a flyout
|
||||
SocialFlyout.open(url, 0, panelCallback);
|
||||
SocialFlyout.panel.firstChild.addEventListener("load", function panelLoad(evt) {
|
||||
if (evt.target != SocialFlyout.panel.firstChild.contentDocument) {
|
||||
return;
|
||||
}
|
||||
SocialFlyout.panel.firstChild.removeEventListener("load", panelLoad, true);
|
||||
loadCallback();
|
||||
}, true);
|
||||
// wait for both open and loaded before callback. Since the test doesn't close
|
||||
// the panel between opens, we cannot rely on events here. We need to ensure
|
||||
// popupshown happens before we finish out the tests.
|
||||
waitForCondition(function() {
|
||||
return SocialFlyout.panel.state == "open" &&
|
||||
SocialFlyout.iframe.contentDocument.readyState == "complete";
|
||||
},
|
||||
function () { executeSoon(loadCallback) },
|
||||
"flyout is open and loaded");
|
||||
}
|
||||
|
||||
function openChat(url, panelCallback, loadCallback) {
|
||||
|
@ -51,7 +72,7 @@ function openChat(url, panelCallback, loadCallback) {
|
|||
openChatWindow(null, SocialSidebar.provider, url, panelCallback);
|
||||
chatbar.firstChild.addEventListener("DOMContentLoaded", function panelLoad() {
|
||||
chatbar.firstChild.removeEventListener("DOMContentLoaded", panelLoad, true);
|
||||
loadCallback();
|
||||
executeSoon(loadCallback);
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
@ -59,27 +80,14 @@ function onSidebarLoad(callback) {
|
|||
let sbrowser = document.getElementById("social-sidebar-browser");
|
||||
sbrowser.addEventListener("load", function load() {
|
||||
sbrowser.removeEventListener("load", load, true);
|
||||
callback();
|
||||
executeSoon(callback);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function ensureWorkerLoaded(provider, callback) {
|
||||
// once the worker responds to a ping we know it must be up.
|
||||
let port = provider.getWorkerPort();
|
||||
port.onmessage = function(msg) {
|
||||
if (msg.data.topic == "pong") {
|
||||
port.close();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
port.postMessage({topic: "ping"})
|
||||
}
|
||||
|
||||
let manifest = { // normal provider
|
||||
name: "provider 1",
|
||||
origin: "https://example.com",
|
||||
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",
|
||||
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
|
||||
};
|
||||
|
||||
|
@ -87,7 +95,7 @@ function test() {
|
|||
waitForExplicitFinish();
|
||||
|
||||
runSocialTestWithProvider(manifest, function (finishcb) {
|
||||
runSocialTests(tests, undefined, goOnline, finishcb);
|
||||
runSocialTests(tests, undefined, function(next) { goOnline().then(next) }, finishcb);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -95,122 +103,123 @@ var tests = {
|
|||
testSidebar: function(next) {
|
||||
let sbrowser = document.getElementById("social-sidebar-browser");
|
||||
onSidebarLoad(function() {
|
||||
ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
|
||||
ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "sidebar is on social error page");
|
||||
gc();
|
||||
// Add a new load listener, then find and click the "try again" button.
|
||||
onSidebarLoad(function() {
|
||||
// should still be on the error page.
|
||||
ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "is still on social error page");
|
||||
ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "sidebar is still on social error page");
|
||||
// go online and try again - this should work.
|
||||
goOnline();
|
||||
onSidebarLoad(function() {
|
||||
// should now be on the correct page.
|
||||
is(sbrowser.contentDocument.location.href, manifest.sidebarURL, "is now on social sidebar page");
|
||||
next();
|
||||
goOnline().then(function () {
|
||||
onSidebarLoad(function() {
|
||||
// should now be on the correct page.
|
||||
is(sbrowser.contentDocument.location.href, manifest.sidebarURL, "sidebar is now on social sidebar page");
|
||||
next();
|
||||
});
|
||||
sbrowser.contentDocument.getElementById("btnTryAgain").click();
|
||||
});
|
||||
sbrowser.contentDocument.getElementById("btnTryAgain").click();
|
||||
});
|
||||
sbrowser.contentDocument.getElementById("btnTryAgain").click();
|
||||
});
|
||||
// we want the worker to be fully loaded before going offline, otherwise
|
||||
// it might fail due to going offline.
|
||||
ensureWorkerLoaded(SocialSidebar.provider, function() {
|
||||
// go offline then attempt to load the sidebar - it should fail.
|
||||
goOffline();
|
||||
// go offline then attempt to load the sidebar - it should fail.
|
||||
goOffline().then(function() {
|
||||
SocialSidebar.show();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testFlyout: function(next) {
|
||||
let panelCallbackCount = 0;
|
||||
let panel = document.getElementById("social-flyout-panel");
|
||||
// go offline and open a flyout.
|
||||
goOffline();
|
||||
openPanel(
|
||||
"https://example.com/browser/browser/base/content/test/social/social_panel.html",
|
||||
function() { // the panel api callback
|
||||
panelCallbackCount++;
|
||||
},
|
||||
function() { // the "load" callback.
|
||||
executeSoon(function() {
|
||||
goOffline().then(function() {
|
||||
openPanel(
|
||||
manifest.sidebarURL, /* empty html page */
|
||||
function() { // the panel api callback
|
||||
panelCallbackCount++;
|
||||
},
|
||||
function() { // the "load" callback.
|
||||
todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
|
||||
ok(panel.firstChild.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
|
||||
let href = panel.firstChild.contentDocument.location.href;
|
||||
ok(href.indexOf("about:socialerror?")==0, "flyout is on social error page");
|
||||
// Bug 832943 - the listeners previously stopped working after a GC, so
|
||||
// force a GC now and try again.
|
||||
gc();
|
||||
openPanel(
|
||||
"https://example.com/browser/browser/base/content/test/social/social_panel.html",
|
||||
manifest.sidebarURL, /* empty html page */
|
||||
function() { // the panel api callback
|
||||
panelCallbackCount++;
|
||||
},
|
||||
function() { // the "load" callback.
|
||||
executeSoon(function() {
|
||||
todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
|
||||
ok(panel.firstChild.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
|
||||
gc();
|
||||
executeSoon(function() {
|
||||
SocialFlyout.unload();
|
||||
next();
|
||||
});
|
||||
});
|
||||
todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
|
||||
let href = panel.firstChild.contentDocument.location.href;
|
||||
ok(href.indexOf("about:socialerror?")==0, "flyout is on social error page");
|
||||
gc();
|
||||
SocialFlyout.unload();
|
||||
next();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
testChatWindow: function(next) {
|
||||
let panelCallbackCount = 0;
|
||||
// go offline and open a chat.
|
||||
goOffline();
|
||||
openChat(
|
||||
"https://example.com/browser/browser/base/content/test/social/social_chat.html",
|
||||
function() { // the panel api callback
|
||||
panelCallbackCount++;
|
||||
},
|
||||
function() { // the "load" callback.
|
||||
executeSoon(function() {
|
||||
// chatwindow tests throw errors, which muddy test output, if the worker
|
||||
// doesn't get test-init
|
||||
goOffline().then(function() {
|
||||
openChat(
|
||||
manifest.sidebarURL, /* empty html page */
|
||||
function() { // the panel api callback
|
||||
panelCallbackCount++;
|
||||
},
|
||||
function() { // the "load" callback.
|
||||
todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
|
||||
let chat = getChatBar().selectedChat;
|
||||
waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
|
||||
waitForCondition(function() chat.content != null && chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
|
||||
function() {
|
||||
chat.close();
|
||||
next();
|
||||
},
|
||||
"error page didn't appear");
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
testChatWindowAfterTearOff: function(next) {
|
||||
// Ensure that the error listener survives the chat window being detached.
|
||||
let url = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
|
||||
let url = manifest.sidebarURL; /* empty html page */
|
||||
let panelCallbackCount = 0;
|
||||
// chatwindow tests throw errors, which muddy test output, if the worker
|
||||
// doesn't get test-init
|
||||
// open a chat while we are still online.
|
||||
openChat(
|
||||
url,
|
||||
null,
|
||||
function() { // the "load" callback.
|
||||
executeSoon(function() {
|
||||
let chat = getChatBar().selectedChat;
|
||||
is(chat.contentDocument.location.href, url, "correct url loaded");
|
||||
// toggle to a detached window.
|
||||
chat.swapWindows().then(
|
||||
chat => {
|
||||
let chat = getChatBar().selectedChat;
|
||||
is(chat.contentDocument.location.href, url, "correct url loaded");
|
||||
// toggle to a detached window.
|
||||
chat.swapWindows().then(
|
||||
chat => {
|
||||
ok(!!chat.content, "we have chat content 1");
|
||||
waitForCondition(function() chat.content != null && chat.contentDocument.readyState == "complete",
|
||||
function() {
|
||||
// now go offline and reload the chat - about:socialerror should be loaded.
|
||||
goOffline();
|
||||
chat.contentDocument.location.reload();
|
||||
waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
|
||||
function() {
|
||||
chat.close();
|
||||
next();
|
||||
},
|
||||
"error page didn't appear");
|
||||
}
|
||||
);
|
||||
});
|
||||
goOffline().then(function() {
|
||||
ok(!!chat.content, "we have chat content 2");
|
||||
chat.contentDocument.location.reload();
|
||||
info("chat reload called");
|
||||
waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
|
||||
function() {
|
||||
chat.close();
|
||||
next();
|
||||
},
|
||||
"error page didn't appear");
|
||||
});
|
||||
}, "swapped window loaded");
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,16 @@ function waitForCondition(condition, nextTest, errorMsg) {
|
|||
var moveOn = function() { clearInterval(interval); nextTest(); };
|
||||
}
|
||||
|
||||
|
||||
function promiseObserverNotified(aTopic) {
|
||||
let deferred = Promise.defer();
|
||||
Services.obs.addObserver(function onNotification(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(onNotification, aTopic);
|
||||
deferred.resolve({subject: aSubject, data: aData});
|
||||
}, aTopic, false);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Check that a specified (string) URL hasn't been "remembered" (ie, is not
|
||||
// in history, will not appear in about:newtab or auto-complete, etc.)
|
||||
function promiseSocialUrlNotRemembered(url) {
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<html>
|
||||
<!-- 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/. -->
|
||||
<head>
|
||||
<title>Activation test</title>
|
||||
</head>
|
||||
<script>
|
||||
|
||||
var data = {
|
||||
// currently required
|
||||
"name": "Demo Social Service",
|
||||
// browser_share.js serves this page from "https://example.com"
|
||||
"origin": "https://example.com",
|
||||
"iconURL": "chrome://branding/content/icon16.png",
|
||||
"icon32URL": "chrome://branding/content/favicon32.png",
|
||||
"icon64URL": "chrome://branding/content/icon64.png",
|
||||
"workerURL": "/browser/browser/base/content/test/social/social_worker.js",
|
||||
"shareURL": "/browser/browser/base/content/test/social/share.html"
|
||||
}
|
||||
|
||||
function activate(node) {
|
||||
node.setAttribute("data-service", JSON.stringify(data));
|
||||
var event = new CustomEvent("ActivateSocialFeature");
|
||||
node.dispatchEvent(event);
|
||||
}
|
||||
|
||||
</script>
|
||||
<body>
|
||||
|
||||
nothing to see here
|
||||
|
||||
<button id="activation" onclick="activate(this, true)">Activate the share provider</button>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -68,6 +68,7 @@ browser.jar:
|
|||
content/browser/aboutRobots-icon.png (content/aboutRobots-icon.png)
|
||||
content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png)
|
||||
content/browser/aboutSocialError.xhtml (content/aboutSocialError.xhtml)
|
||||
content/browser/aboutProviderDirectory.xhtml (content/aboutProviderDirectory.xhtml)
|
||||
content/browser/aboutTabCrashed.js (content/aboutTabCrashed.js)
|
||||
content/browser/aboutTabCrashed.xhtml (content/aboutTabCrashed.xhtml)
|
||||
* content/browser/browser.css (content/browser.css)
|
||||
|
|
|
@ -47,6 +47,9 @@ static RedirEntry kRedirMap[] = {
|
|||
{ "socialerror", "chrome://browser/content/aboutSocialError.xhtml",
|
||||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
|
||||
{ "providerdirectory", "chrome://browser/content/aboutProviderDirectory.xhtml",
|
||||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
|
||||
{ "tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
|
|
|
@ -90,6 +90,7 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
|
|||
#endif
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "certerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "socialerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "providerdirectory", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "tabcrashed", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "feeds", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "privatebrowsing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
|
|
|
@ -207,7 +207,6 @@ let CustomizableUIInternal = {
|
|||
"downloads-button",
|
||||
"home-button",
|
||||
"loop-call-button",
|
||||
"social-share-button",
|
||||
],
|
||||
defaultCollapsed: false,
|
||||
}, true);
|
||||
|
|
|
@ -393,6 +393,24 @@ const CustomizableWidgets = [
|
|||
let doc = aEvent.target.ownerDocument;
|
||||
clearSubview(doc.getElementById("PanelUI-sidebarItems"));
|
||||
}
|
||||
}, {
|
||||
id: "social-share-button",
|
||||
tooltiptext: "social-share-button.label",
|
||||
label: "social-share-button.tooltiptext",
|
||||
// custom build our button so we can attach to the share command
|
||||
type: "custom",
|
||||
onBuild: function(aDocument) {
|
||||
let node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
|
||||
node.setAttribute("id", this.id);
|
||||
node.classList.add("toolbarbutton-1");
|
||||
node.classList.add("chromeclass-toolbar-additional");
|
||||
node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label"));
|
||||
node.setAttribute("tooltiptext", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
|
||||
node.setAttribute("removable", "true");
|
||||
node.setAttribute("observes", "Social:PageShareOrMark");
|
||||
node.setAttribute("command", "Social:SharePage");
|
||||
return node;
|
||||
}
|
||||
}, {
|
||||
id: "add-ons-button",
|
||||
shortcutId: "key_openAddons",
|
||||
|
@ -905,9 +923,8 @@ const CustomizableWidgets = [
|
|||
}, {
|
||||
id: "loop-call-button",
|
||||
type: "custom",
|
||||
// XXX Bug 1013989 will provide a label for the button
|
||||
label: "loop-call-button.label",
|
||||
tooltiptext: "loop-call-button.tooltiptext",
|
||||
label: "loop-call-button2.label",
|
||||
tooltiptext: "loop-call-button2.tooltiptext",
|
||||
defaultArea: CustomizableUI.AREA_NAVBAR,
|
||||
introducedInVersion: 1,
|
||||
onBuild: function(aDocument) {
|
||||
|
|
|
@ -222,7 +222,7 @@ this.GoogleImporter.prototype = {
|
|||
throw new Error("Popup window was closed before authentication succeeded");
|
||||
}
|
||||
|
||||
let matches = gAuthWindow.document.title.match(/(error|code)=(.*)$/);
|
||||
let matches = gAuthWindow.document.title.match(/(error|code)=([^\s]+)/);
|
||||
if (matches && matches.length) {
|
||||
let [, type, message] = matches;
|
||||
gAuthWindow.close();
|
||||
|
|
|
@ -25,7 +25,9 @@ XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
|
|||
|
||||
this.EXPORTED_SYMBOLS = ["LoopStorage"];
|
||||
|
||||
const kDatabaseName = "loop";
|
||||
const kDatabasePrefix = "loop-";
|
||||
const kDefaultDatabaseName = "default";
|
||||
let gDatabaseName = kDatabasePrefix + kDefaultDatabaseName;
|
||||
const kDatabaseVersion = 1;
|
||||
|
||||
let gWaitForOpenCallbacks = new Set();
|
||||
|
@ -83,7 +85,7 @@ const ensureDatabaseOpen = function(onOpen) {
|
|||
gWaitForOpenCallbacks.clear();
|
||||
};
|
||||
|
||||
let openRequest = indexedDB.open(kDatabaseName, kDatabaseVersion);
|
||||
let openRequest = indexedDB.open(gDatabaseName, kDatabaseVersion);
|
||||
|
||||
openRequest.onblocked = function(event) {
|
||||
invokeCallbacks(new Error("Database cannot be upgraded cause in use: " + event.target.error));
|
||||
|
@ -92,7 +94,7 @@ const ensureDatabaseOpen = function(onOpen) {
|
|||
openRequest.onerror = function(event) {
|
||||
// Try to delete the old database so that we can start this process over
|
||||
// next time.
|
||||
indexedDB.deleteDatabase(kDatabaseName);
|
||||
indexedDB.deleteDatabase(gDatabaseName);
|
||||
invokeCallbacks(new Error("Error while opening database: " + event.target.errorCode));
|
||||
};
|
||||
|
||||
|
@ -109,6 +111,33 @@ const ensureDatabaseOpen = function(onOpen) {
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Switch to a database with a different name by closing the current connection
|
||||
* and making sure that the next connection attempt will be made using the updated
|
||||
* name.
|
||||
*
|
||||
* @param {String} name New name of the database to switch to.
|
||||
*/
|
||||
const switchDatabase = function(name) {
|
||||
if (!name) {
|
||||
name = kDefaultDatabaseName;
|
||||
}
|
||||
name = kDatabasePrefix + name;
|
||||
if (name == gDatabaseName) {
|
||||
// This is already the current database, so there's no need to switch.
|
||||
return;
|
||||
}
|
||||
|
||||
gDatabaseName = name;
|
||||
if (gDatabase) {
|
||||
try {
|
||||
gDatabase.close();
|
||||
} finally {
|
||||
gDatabase = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a transaction on the loop database and return it.
|
||||
*
|
||||
|
@ -179,6 +208,14 @@ const getStore = function(store, callback, mode) {
|
|||
* database is loaded in memory and consumers will be able to change its structure.
|
||||
*/
|
||||
this.LoopStorage = Object.freeze({
|
||||
/**
|
||||
* @var {String} databaseName The name of the database that is currently active,
|
||||
* WITHOUT the prefix
|
||||
*/
|
||||
get databaseName() {
|
||||
return gDatabaseName.substr(kDatabasePrefix.length);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open a connection to the IndexedDB database and return the database object.
|
||||
*
|
||||
|
@ -191,6 +228,16 @@ this.LoopStorage = Object.freeze({
|
|||
ensureDatabaseOpen(callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Switch to a database with a different name.
|
||||
*
|
||||
* @param {String} name New name of the database to switch to. Defaults to
|
||||
* `kDefaultDatabaseName`
|
||||
*/
|
||||
switchDatabase: function(name = kDefaultDatabaseName) {
|
||||
switchDatabase(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Start a transaction on the loop database and return it.
|
||||
* If only two arguments are passed, the default mode will be assumed and the
|
||||
|
|
|
@ -56,6 +56,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "HawkClient",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "deriveHawkCredentials",
|
||||
"resource://services-common/hawkrequest.js");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage",
|
||||
"resource:///modules/loop/LoopStorage.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
|
||||
"resource:///modules/loop/MozLoopPushHandler.jsm");
|
||||
|
||||
|
@ -367,6 +370,8 @@ let MozLoopServiceInternal = {
|
|||
|
||||
notifyStatusChanged: function(aReason = null) {
|
||||
log.debug("notifyStatusChanged with reason:", aReason);
|
||||
let profile = MozLoopService.userProfile;
|
||||
LoopStorage.switchDatabase(profile ? profile.uid : null);
|
||||
Services.obs.notifyObservers(null, "loop-status-changed", aReason);
|
||||
},
|
||||
|
||||
|
@ -1348,7 +1353,7 @@ this.MozLoopService = {
|
|||
getStrings: function(key) {
|
||||
var stringData = MozLoopServiceInternal.localizedStrings;
|
||||
if (!(key in stringData)) {
|
||||
Cu.reportError('No string for key: ' + key + 'found');
|
||||
log.error("No string found for key: ", key);
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -153,14 +153,14 @@ loop.contacts = (function(_, mozL10n) {
|
|||
document.body.removeEventListener("click", this._onBodyClick);
|
||||
},
|
||||
|
||||
componentShouldUpdate: function(nextProps, nextState) {
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
let currContact = this.props.contact;
|
||||
let nextContact = nextProps.contact;
|
||||
return (
|
||||
currContact.name[0] !== nextContact.name[0] ||
|
||||
currContact.blocked !== nextContact.blocked ||
|
||||
getPreferredEmail(currContact).value !==
|
||||
getPreferredEmail(nextContact).value
|
||||
getPreferredEmail(currContact).value !== getPreferredEmail(nextContact).value ||
|
||||
nextState.showMenu !== this.state.showMenu
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -215,20 +215,32 @@ loop.contacts = (function(_, mozL10n) {
|
|||
const ContactsList = React.createClass({displayName: 'ContactsList',
|
||||
mixins: [React.addons.LinkedStateMixin],
|
||||
|
||||
/**
|
||||
* Contacts collection object
|
||||
*/
|
||||
contacts: null,
|
||||
|
||||
/**
|
||||
* User profile
|
||||
*/
|
||||
_userProfile: null,
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
contacts: {},
|
||||
importBusy: false,
|
||||
filter: "",
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
refresh: function(callback = function() {}) {
|
||||
let contactsAPI = navigator.mozLoop.contacts;
|
||||
|
||||
this.handleContactRemoveAll();
|
||||
|
||||
contactsAPI.getAll((err, contacts) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add contacts already present in the DB. We do this in timed chunks to
|
||||
|
@ -239,11 +251,32 @@ loop.contacts = (function(_, mozL10n) {
|
|||
});
|
||||
if (contacts.length) {
|
||||
setTimeout(addContactsInChunks, 0);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
addContactsInChunks(contacts);
|
||||
});
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
// Take the time to initialize class variables that are used outside
|
||||
// `this.state`.
|
||||
this.contacts = {};
|
||||
this._userProfile = navigator.mozLoop.userProfile;
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
window.addEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
|
||||
this.refresh(err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
let contactsAPI = navigator.mozLoop.contacts;
|
||||
|
||||
// Listen for contact changes/ updates.
|
||||
contactsAPI.on("add", (eventName, contact) => {
|
||||
|
@ -261,8 +294,24 @@ loop.contacts = (function(_, mozL10n) {
|
|||
});
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
},
|
||||
|
||||
_onStatusChanged: function() {
|
||||
let profile = navigator.mozLoop.userProfile;
|
||||
let currUid = this._userProfile ? this._userProfile.uid : null;
|
||||
let newUid = profile ? profile.uid : null;
|
||||
if (currUid != newUid) {
|
||||
// On profile change (login, logout), reload all contacts.
|
||||
this._userProfile = profile;
|
||||
// The following will do a forceUpdate() for us.
|
||||
this.refresh();
|
||||
}
|
||||
},
|
||||
|
||||
handleContactAddOrUpdate: function(contact, render = true) {
|
||||
let contacts = this.state.contacts;
|
||||
let contacts = this.contacts;
|
||||
let guid = String(contact._guid);
|
||||
contacts[guid] = contact;
|
||||
if (render) {
|
||||
|
@ -271,7 +320,7 @@ loop.contacts = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
handleContactRemove: function(contact) {
|
||||
let contacts = this.state.contacts;
|
||||
let contacts = this.contacts;
|
||||
let guid = String(contact._guid);
|
||||
if (!contacts[guid]) {
|
||||
return;
|
||||
|
@ -281,7 +330,9 @@ loop.contacts = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
handleContactRemoveAll: function() {
|
||||
this.setState({contacts: {}});
|
||||
// Do not allow any race conditions when removing all contacts.
|
||||
this.contacts = {};
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
handleImportButtonClick: function() {
|
||||
|
@ -364,11 +415,11 @@ loop.contacts = (function(_, mozL10n) {
|
|||
handleContactAction: this.handleContactAction})
|
||||
};
|
||||
|
||||
let shownContacts = _.groupBy(this.state.contacts, function(contact) {
|
||||
let shownContacts = _.groupBy(this.contacts, function(contact) {
|
||||
return contact.blocked ? "blocked" : "available";
|
||||
});
|
||||
|
||||
let showFilter = Object.getOwnPropertyNames(this.state.contacts).length >=
|
||||
let showFilter = Object.getOwnPropertyNames(this.contacts).length >=
|
||||
MIN_CONTACTS_FOR_FILTERING;
|
||||
if (showFilter) {
|
||||
let filter = this.state.filter.trim().toLocaleLowerCase();
|
||||
|
|
|
@ -153,14 +153,14 @@ loop.contacts = (function(_, mozL10n) {
|
|||
document.body.removeEventListener("click", this._onBodyClick);
|
||||
},
|
||||
|
||||
componentShouldUpdate: function(nextProps, nextState) {
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
let currContact = this.props.contact;
|
||||
let nextContact = nextProps.contact;
|
||||
return (
|
||||
currContact.name[0] !== nextContact.name[0] ||
|
||||
currContact.blocked !== nextContact.blocked ||
|
||||
getPreferredEmail(currContact).value !==
|
||||
getPreferredEmail(nextContact).value
|
||||
getPreferredEmail(currContact).value !== getPreferredEmail(nextContact).value ||
|
||||
nextState.showMenu !== this.state.showMenu
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -215,20 +215,32 @@ loop.contacts = (function(_, mozL10n) {
|
|||
const ContactsList = React.createClass({
|
||||
mixins: [React.addons.LinkedStateMixin],
|
||||
|
||||
/**
|
||||
* Contacts collection object
|
||||
*/
|
||||
contacts: null,
|
||||
|
||||
/**
|
||||
* User profile
|
||||
*/
|
||||
_userProfile: null,
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
contacts: {},
|
||||
importBusy: false,
|
||||
filter: "",
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
refresh: function(callback = function() {}) {
|
||||
let contactsAPI = navigator.mozLoop.contacts;
|
||||
|
||||
this.handleContactRemoveAll();
|
||||
|
||||
contactsAPI.getAll((err, contacts) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add contacts already present in the DB. We do this in timed chunks to
|
||||
|
@ -239,11 +251,32 @@ loop.contacts = (function(_, mozL10n) {
|
|||
});
|
||||
if (contacts.length) {
|
||||
setTimeout(addContactsInChunks, 0);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
addContactsInChunks(contacts);
|
||||
});
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
// Take the time to initialize class variables that are used outside
|
||||
// `this.state`.
|
||||
this.contacts = {};
|
||||
this._userProfile = navigator.mozLoop.userProfile;
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
window.addEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
|
||||
this.refresh(err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
let contactsAPI = navigator.mozLoop.contacts;
|
||||
|
||||
// Listen for contact changes/ updates.
|
||||
contactsAPI.on("add", (eventName, contact) => {
|
||||
|
@ -261,8 +294,24 @@ loop.contacts = (function(_, mozL10n) {
|
|||
});
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
},
|
||||
|
||||
_onStatusChanged: function() {
|
||||
let profile = navigator.mozLoop.userProfile;
|
||||
let currUid = this._userProfile ? this._userProfile.uid : null;
|
||||
let newUid = profile ? profile.uid : null;
|
||||
if (currUid != newUid) {
|
||||
// On profile change (login, logout), reload all contacts.
|
||||
this._userProfile = profile;
|
||||
// The following will do a forceUpdate() for us.
|
||||
this.refresh();
|
||||
}
|
||||
},
|
||||
|
||||
handleContactAddOrUpdate: function(contact, render = true) {
|
||||
let contacts = this.state.contacts;
|
||||
let contacts = this.contacts;
|
||||
let guid = String(contact._guid);
|
||||
contacts[guid] = contact;
|
||||
if (render) {
|
||||
|
@ -271,7 +320,7 @@ loop.contacts = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
handleContactRemove: function(contact) {
|
||||
let contacts = this.state.contacts;
|
||||
let contacts = this.contacts;
|
||||
let guid = String(contact._guid);
|
||||
if (!contacts[guid]) {
|
||||
return;
|
||||
|
@ -281,7 +330,9 @@ loop.contacts = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
handleContactRemoveAll: function() {
|
||||
this.setState({contacts: {}});
|
||||
// Do not allow any race conditions when removing all contacts.
|
||||
this.contacts = {};
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
handleImportButtonClick: function() {
|
||||
|
@ -364,11 +415,11 @@ loop.contacts = (function(_, mozL10n) {
|
|||
handleContactAction={this.handleContactAction} />
|
||||
};
|
||||
|
||||
let shownContacts = _.groupBy(this.state.contacts, function(contact) {
|
||||
let shownContacts = _.groupBy(this.contacts, function(contact) {
|
||||
return contact.blocked ? "blocked" : "available";
|
||||
});
|
||||
|
||||
let showFilter = Object.getOwnPropertyNames(this.state.contacts).length >=
|
||||
let showFilter = Object.getOwnPropertyNames(this.contacts).length >=
|
||||
MIN_CONTACTS_FOR_FILTERING;
|
||||
if (showFilter) {
|
||||
let filter = this.state.filter.trim().toLocaleLowerCase();
|
||||
|
|
|
@ -168,7 +168,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
var terms_of_use_url = navigator.mozLoop.getLoopCharPref('legal.ToS_url');
|
||||
var privacy_notice_url = navigator.mozLoop.getLoopCharPref('legal.privacy_url');
|
||||
var tosHTML = __("legal_text_and_links3", {
|
||||
"clientShortname": __("client_shortname_fallback"),
|
||||
"clientShortname": __("clientShortname2"),
|
||||
"terms_of_use": React.renderComponentToStaticMarkup(
|
||||
React.DOM.a({href: terms_of_use_url, target: "_blank"},
|
||||
__("legal_text_tos")
|
||||
|
@ -352,6 +352,9 @@ loop.panel = (function(_, mozL10n) {
|
|||
var token = callUrlData.callToken ||
|
||||
callUrl.pathname.split('/').pop();
|
||||
|
||||
// Now that a new URL is available, indicate it has not been shared.
|
||||
this.linkExfiltrated = false;
|
||||
|
||||
this.setState({pending: false, copied: false,
|
||||
callUrl: callUrl.href,
|
||||
callUrlExpiry: callUrlData.expiresAt});
|
||||
|
@ -366,8 +369,11 @@ loop.panel = (function(_, mozL10n) {
|
|||
handleEmailButtonClick: function(event) {
|
||||
this.handleLinkExfiltration(event);
|
||||
|
||||
navigator.mozLoop.composeEmail(__("share_email_subject3"),
|
||||
__("share_email_body3", { callUrl: this.state.callUrl }));
|
||||
navigator.mozLoop.composeEmail(
|
||||
__("share_email_subject4", { clientShortname: __("clientShortname2")}),
|
||||
__("share_email_body4", { callUrl: this.state.callUrl,
|
||||
clientShortname: __("clientShortname2"),
|
||||
learnMoreUrl: navigator.mozLoop.getLoopCharPref("learnMoreUrl") }));
|
||||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
|
@ -378,12 +384,20 @@ loop.panel = (function(_, mozL10n) {
|
|||
this.setState({copied: true});
|
||||
},
|
||||
|
||||
linkExfiltrated: false,
|
||||
|
||||
handleLinkExfiltration: function(event) {
|
||||
try {
|
||||
navigator.mozLoop.telemetryAdd("LOOP_CLIENT_CALL_URL_SHARED", true);
|
||||
} catch (err) {
|
||||
console.error("Error recording telemetry", err);
|
||||
// Update the count of shared URLs only once per generated URL.
|
||||
if (!this.linkExfiltrated) {
|
||||
this.linkExfiltrated = true;
|
||||
try {
|
||||
navigator.mozLoop.telemetryAdd("LOOP_CLIENT_CALL_URL_SHARED", true);
|
||||
} catch (err) {
|
||||
console.error("Error recording telemetry", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Note URL expiration every time it is shared.
|
||||
if (this.state.callUrlExpiry) {
|
||||
navigator.mozLoop.noteCallUrlExpiry(this.state.callUrlExpiry);
|
||||
}
|
||||
|
@ -625,7 +639,9 @@ loop.panel = (function(_, mozL10n) {
|
|||
|
||||
_onStatusChanged: function() {
|
||||
var profile = navigator.mozLoop.userProfile;
|
||||
if (profile != this.state.userProfile) {
|
||||
var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
|
||||
var newUid = profile ? profile.uid : null;
|
||||
if (currUid != newUid) {
|
||||
// On profile change (login, logout), switch back to the default tab.
|
||||
this.selectTab("call");
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
var terms_of_use_url = navigator.mozLoop.getLoopCharPref('legal.ToS_url');
|
||||
var privacy_notice_url = navigator.mozLoop.getLoopCharPref('legal.privacy_url');
|
||||
var tosHTML = __("legal_text_and_links3", {
|
||||
"clientShortname": __("client_shortname_fallback"),
|
||||
"clientShortname": __("clientShortname2"),
|
||||
"terms_of_use": React.renderComponentToStaticMarkup(
|
||||
<a href={terms_of_use_url} target="_blank">
|
||||
{__("legal_text_tos")}
|
||||
|
@ -352,6 +352,9 @@ loop.panel = (function(_, mozL10n) {
|
|||
var token = callUrlData.callToken ||
|
||||
callUrl.pathname.split('/').pop();
|
||||
|
||||
// Now that a new URL is available, indicate it has not been shared.
|
||||
this.linkExfiltrated = false;
|
||||
|
||||
this.setState({pending: false, copied: false,
|
||||
callUrl: callUrl.href,
|
||||
callUrlExpiry: callUrlData.expiresAt});
|
||||
|
@ -366,8 +369,11 @@ loop.panel = (function(_, mozL10n) {
|
|||
handleEmailButtonClick: function(event) {
|
||||
this.handleLinkExfiltration(event);
|
||||
|
||||
navigator.mozLoop.composeEmail(__("share_email_subject3"),
|
||||
__("share_email_body3", { callUrl: this.state.callUrl }));
|
||||
navigator.mozLoop.composeEmail(
|
||||
__("share_email_subject4", { clientShortname: __("clientShortname2")}),
|
||||
__("share_email_body4", { callUrl: this.state.callUrl,
|
||||
clientShortname: __("clientShortname2"),
|
||||
learnMoreUrl: navigator.mozLoop.getLoopCharPref("learnMoreUrl") }));
|
||||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
|
@ -378,12 +384,20 @@ loop.panel = (function(_, mozL10n) {
|
|||
this.setState({copied: true});
|
||||
},
|
||||
|
||||
linkExfiltrated: false,
|
||||
|
||||
handleLinkExfiltration: function(event) {
|
||||
try {
|
||||
navigator.mozLoop.telemetryAdd("LOOP_CLIENT_CALL_URL_SHARED", true);
|
||||
} catch (err) {
|
||||
console.error("Error recording telemetry", err);
|
||||
// Update the count of shared URLs only once per generated URL.
|
||||
if (!this.linkExfiltrated) {
|
||||
this.linkExfiltrated = true;
|
||||
try {
|
||||
navigator.mozLoop.telemetryAdd("LOOP_CLIENT_CALL_URL_SHARED", true);
|
||||
} catch (err) {
|
||||
console.error("Error recording telemetry", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Note URL expiration every time it is shared.
|
||||
if (this.state.callUrlExpiry) {
|
||||
navigator.mozLoop.noteCallUrlExpiry(this.state.callUrlExpiry);
|
||||
}
|
||||
|
@ -625,7 +639,9 @@ loop.panel = (function(_, mozL10n) {
|
|||
|
||||
_onStatusChanged: function() {
|
||||
var profile = navigator.mozLoop.userProfile;
|
||||
if (profile != this.state.userProfile) {
|
||||
var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
|
||||
var newUid = profile ? profile.uid : null;
|
||||
if (currUid != newUid) {
|
||||
// On profile change (login, logout), switch back to the default tab.
|
||||
this.selectTab("call");
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||
return (
|
||||
React.DOM.h1({className: "standalone-header-title"},
|
||||
React.DOM.strong(null, mozL10n.get("brandShortname")),
|
||||
mozL10n.get("clientShortname")
|
||||
mozL10n.get("clientShortname2")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||
return (
|
||||
<h1 className="standalone-header-title">
|
||||
<strong>{mozL10n.get("brandShortname")}</strong>
|
||||
{mozL10n.get("clientShortname")}
|
||||
{mozL10n.get("clientShortname2")}
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -41,8 +41,15 @@ initiate_call_cancel_button=Cancel
|
|||
legal_text_and_links=By using this product you agree to the {{terms_of_use_url}} and {{privacy_notice_url}}
|
||||
terms_of_use_link_text=Terms of use
|
||||
privacy_notice_link_text=Privacy notice
|
||||
invite_header_text=Invite someone to join you.
|
||||
|
||||
## LOCALIZATION NOTE(brandShortname): This should not be localized and
|
||||
## should remain "Firefox" for all locales.
|
||||
brandShortname=Firefox
|
||||
clientShortname=WebRTC!
|
||||
## LOCALIZATION NOTE(clientShortname2): This should not be localized and
|
||||
## should remain "Firefox Hello" for all locales.
|
||||
clientShortname2=Firefox Hello
|
||||
|
||||
## LOCALIZATION NOTE (call_url_creation_date_label): Example output: (from May 26, 2014)
|
||||
call_url_creation_date_label=(from {{call_url_creation_date}})
|
||||
call_progress_connecting_description=Connecting…
|
||||
|
@ -77,3 +84,26 @@ feedback_rejoin_button=Rejoin
|
|||
## LOCALIZATION NOTE (feedback_report_user_button): Used to report a user in the case of
|
||||
## an abusive user.
|
||||
feedback_report_user_button=Report User
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience.title): clientShortname will be
|
||||
## replaced by the brand name
|
||||
first_time_experience_title={{clientShortname}} — Join the conversation
|
||||
first_time_experience_button_label=Get Started
|
||||
|
||||
help_label=Help
|
||||
tour_label=Tour
|
||||
|
||||
rooms_default_room_name_template=Conversation {{conversationLabel}}
|
||||
rooms_leave_button_label=Leave
|
||||
rooms_list_copy_url_tooltip=Copy Link
|
||||
rooms_list_delete_tooltip=Delete conversation
|
||||
rooms_list_deleteConfirmation_label=Are you sure?
|
||||
rooms_name_this_room_label=Name this conversation
|
||||
rooms_new_room_button_label=Start a conversation
|
||||
rooms_only_occupant_label=You're the first one here.
|
||||
rooms_panel_title=Choose a conversation or start a new one
|
||||
rooms_room_full_label=There are already two people in this conversation.
|
||||
rooms_room_full_call_to_action_nonFx_label=Download {{brandShortname}} to start your own
|
||||
rooms_room_full_call_to_action_label=Learn more about {{clientShortname}} »
|
||||
rooms_room_joined_label=Someone has joined the conversation!
|
||||
rooms_room_join_label=Join the conversation
|
||||
|
|
|
@ -394,9 +394,9 @@ describe("loop.panel", function() {
|
|||
getStrings: function(key) {
|
||||
var text;
|
||||
|
||||
if (key === "share_email_subject3")
|
||||
if (key === "share_email_subject4")
|
||||
text = "email-subject";
|
||||
else if (key === "share_email_body3")
|
||||
else if (key === "share_email_body4")
|
||||
text = "{{callUrl}}";
|
||||
|
||||
return JSON.stringify({textContent: text});
|
||||
|
@ -513,6 +513,8 @@ describe("loop.panel", function() {
|
|||
callUrlExpiry: 6000
|
||||
});
|
||||
|
||||
// Multiple clicks should result in the URL being counted only once.
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-copy"));
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-copy"));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.telemetryAdd);
|
||||
|
@ -554,6 +556,8 @@ describe("loop.panel", function() {
|
|||
callUrlExpiry: 6000
|
||||
});
|
||||
|
||||
// Multiple clicks should result in the URL being counted only once.
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-email"));
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-email"));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.telemetryAdd);
|
||||
|
@ -596,8 +600,10 @@ describe("loop.panel", function() {
|
|||
callUrlExpiry: 6000
|
||||
});
|
||||
|
||||
// Multiple copies should result in the URL being counted only once.
|
||||
var urlField = view.getDOMNode().querySelector("input[type='url']");
|
||||
TestUtils.Simulate.copy(urlField);
|
||||
TestUtils.Simulate.copy(urlField);
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.telemetryAdd);
|
||||
sinon.assert.calledWith(navigator.mozLoop.telemetryAdd,
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const {LoopContacts} = Cu.import("resource:///modules/loop/LoopContacts.jsm", {});
|
||||
const {LoopStorage} = Cu.import("resource:///modules/loop/LoopStorage.jsm", {});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
const kContacts = [{
|
||||
id: 1,
|
||||
|
@ -400,3 +405,31 @@ add_task(function* () {
|
|||
Assert.strictEqual(gExpectedRemovals.length, 0, "No contact removals should be expected anymore");
|
||||
Assert.strictEqual(gExpectedUpdates.length, 0, "No contact updates should be expected anymore");
|
||||
});
|
||||
|
||||
// Test switching between different databases.
|
||||
add_task(function* () {
|
||||
Assert.equal(LoopStorage.databaseName, "default", "First active partition should be the default");
|
||||
yield promiseLoadContacts();
|
||||
|
||||
let uuid = uuidgen.generateUUID().toString().replace(/[{}]+/g, "");
|
||||
LoopStorage.switchDatabase(uuid);
|
||||
Assert.equal(LoopStorage.databaseName, uuid, "The active partition should have changed");
|
||||
|
||||
yield promiseLoadContacts();
|
||||
|
||||
let contacts = yield promiseLoadContacts();
|
||||
for (let i = 0, l = contacts.length; i < l; ++i) {
|
||||
compareContacts(contacts[i], kContacts[i]);
|
||||
}
|
||||
|
||||
LoopStorage.switchDatabase();
|
||||
Assert.equal(LoopStorage.databaseName, "default", "The active partition should have changed");
|
||||
|
||||
LoopContacts.getAll(function(err, contacts) {
|
||||
Assert.equal(err, null, "There shouldn't be an error");
|
||||
|
||||
for (let i = 0, l = contacts.length; i < l; ++i) {
|
||||
compareContacts(contacts[i], kContacts[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -740,13 +740,6 @@ BrowserGlue.prototype = {
|
|||
#endif
|
||||
webrtcUI.uninit();
|
||||
FormValidationHandler.uninit();
|
||||
|
||||
// XXX: Temporary hack to allow Loop FxA login after a restart to work.
|
||||
// Remove this once bug 1071247 is deployed.
|
||||
if (Services.prefs.getPrefType("loop.autologin-after-restart") != Ci.nsIPrefBranch.PREF_BOOL ||
|
||||
!Services.prefs.getBoolPref("loop.autologin-after-restart")) {
|
||||
Services.prefs.clearUserPref("loop.hawk-session-token.fxa");
|
||||
}
|
||||
},
|
||||
|
||||
// All initial windows have opened.
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/* 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 = ["RevivableWindows"];
|
||||
|
||||
// List of closed windows that we can revive when closing
|
||||
// windows in succession until the browser quits.
|
||||
let closedWindows = [];
|
||||
|
||||
/**
|
||||
* This module keeps track of closed windows that are revivable. On Windows
|
||||
* and Linux we can revive windows before saving to disk - i.e. moving them
|
||||
* from state._closedWindows[] to state.windows[] so that they're opened
|
||||
* automatically on next startup. This feature lets us properly support
|
||||
* closing windows in succession until the browser quits.
|
||||
*
|
||||
* The length of the list is not capped by max_undo_windows unlike
|
||||
* state._closedWindows[].
|
||||
*/
|
||||
this.RevivableWindows = Object.freeze({
|
||||
// Returns whether there are windows to revive.
|
||||
get isEmpty() {
|
||||
return closedWindows.length == 0;
|
||||
},
|
||||
|
||||
// Add a window to the list.
|
||||
add(winState) {
|
||||
#ifndef XP_MACOSX
|
||||
closedWindows.push(winState);
|
||||
#endif
|
||||
},
|
||||
|
||||
// Get the list of revivable windows.
|
||||
get() {
|
||||
return [...closedWindows];
|
||||
},
|
||||
|
||||
// Clear the list of revivable windows.
|
||||
clear() {
|
||||
closedWindows.length = 0;
|
||||
}
|
||||
});
|
|
@ -19,6 +19,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "console",
|
|||
"resource://gre/modules/devtools/Console.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyFilter",
|
||||
"resource:///modules/sessionstore/PrivacyFilter.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RevivableWindows",
|
||||
"resource:///modules/sessionstore/RevivableWindows.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
|
||||
|
@ -205,23 +207,24 @@ let SessionSaverInternal = {
|
|||
delete state.deferredInitialState;
|
||||
}
|
||||
|
||||
#ifndef XP_MACOSX
|
||||
// We want to restore closed windows that are marked with _shouldRestore.
|
||||
// We're doing this here because we want to control this only when saving
|
||||
// the file.
|
||||
while (state._closedWindows.length) {
|
||||
let i = state._closedWindows.length - 1;
|
||||
// We want to revive closed windows that have been closed in succession
|
||||
// without any user action in between closing those. This happens here in
|
||||
// the SessionSaver because we only want to revive when saving to disk.
|
||||
// On Mac OS X this list will always be empty.
|
||||
let windowsToRevive = RevivableWindows.get();
|
||||
state.windows.unshift(...windowsToRevive);
|
||||
let revivedWindows = state._closedWindows.splice(0, windowsToRevive.length);
|
||||
#ifdef DEBUG
|
||||
// Check that the windows to revive equal the windows
|
||||
// that we removed from the list of closed windows.
|
||||
let match = revivedWindows.every((win, idx) => {
|
||||
return win == windowsToRevive[windowsToRevive.length - 1 - idx];
|
||||
});
|
||||
|
||||
if (!state._closedWindows[i]._shouldRestore) {
|
||||
// We only need to go until _shouldRestore
|
||||
// is falsy since we're going in reverse.
|
||||
break;
|
||||
}
|
||||
|
||||
delete state._closedWindows[i]._shouldRestore;
|
||||
state.windows.unshift(state._closedWindows.pop());
|
||||
if (!match) {
|
||||
throw new Error("SessionStore: revived windows didn't match closed windows");
|
||||
}
|
||||
#endif
|
||||
#endif DEBUG
|
||||
|
||||
stopWatchFinish("COLLECT_DATA_MS", "COLLECT_DATA_LONGEST_OP_MS");
|
||||
return this._writeState(state);
|
||||
|
|
|
@ -107,6 +107,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "GlobalState",
|
|||
"resource:///modules/sessionstore/GlobalState.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyFilter",
|
||||
"resource:///modules/sessionstore/PrivacyFilter.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RevivableWindows",
|
||||
"resource:///modules/sessionstore/RevivableWindows.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RunState",
|
||||
"resource:///modules/sessionstore/RunState.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
|
||||
|
@ -701,7 +703,9 @@ let SessionStoreInternal = {
|
|||
this.saveStateDelayed(win);
|
||||
break;
|
||||
}
|
||||
this._clearRestoringWindows();
|
||||
|
||||
// Any event handled here indicates a user action.
|
||||
RevivableWindows.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1013,11 +1017,9 @@ let SessionStoreInternal = {
|
|||
SessionCookies.update([winData]);
|
||||
}
|
||||
|
||||
#ifndef XP_MACOSX
|
||||
// Until we decide otherwise elsewhere, this window is part of a series
|
||||
// of closing windows to quit.
|
||||
winData._shouldRestore = true;
|
||||
#endif
|
||||
RevivableWindows.add(winData);
|
||||
|
||||
// Store the window's close date to figure out when each individual tab
|
||||
// was closed. This timestamp should allow re-arranging data based on how
|
||||
|
@ -1039,9 +1041,8 @@ let SessionStoreInternal = {
|
|||
// with tabs we deem not worth saving then we might end up with a
|
||||
// random closed or even a pop-up window re-opened. To prevent that
|
||||
// we explicitly allow saving an "empty" window state.
|
||||
let isLastWindow =
|
||||
Object.keys(this._windows).length == 1 &&
|
||||
!this._closedWindows.some(win => win._shouldRestore || false);
|
||||
let numOpenWindows = Object.keys(this._windows).length;
|
||||
let isLastWindow = numOpenWindows == 1 && RevivableWindows.isEmpty;
|
||||
|
||||
if (hasSaveableTabs || isLastWindow) {
|
||||
// we don't want to save the busy state
|
||||
|
@ -1155,8 +1156,11 @@ let SessionStoreInternal = {
|
|||
delete this._windows[ix];
|
||||
}
|
||||
}
|
||||
|
||||
// also clear all data about closed windows
|
||||
this._closedWindows = [];
|
||||
RevivableWindows.clear();
|
||||
|
||||
// give the tabbrowsers a chance to clear their histories first
|
||||
var win = this._getMostRecentBrowserWindow();
|
||||
if (win) {
|
||||
|
@ -1164,8 +1168,6 @@ let SessionStoreInternal = {
|
|||
} else if (RunState.isRunning) {
|
||||
SessionSaver.run();
|
||||
}
|
||||
|
||||
this._clearRestoringWindows();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1219,11 +1221,12 @@ let SessionStoreInternal = {
|
|||
}
|
||||
}
|
||||
|
||||
// Purging domain data indicates a user action.
|
||||
RevivableWindows.clear();
|
||||
|
||||
if (RunState.isRunning) {
|
||||
SessionSaver.run();
|
||||
}
|
||||
|
||||
this._clearRestoringWindows();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -3346,21 +3349,6 @@ let SessionStoreInternal = {
|
|||
this._closedWindows.splice(spliceTo, this._closedWindows.length);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the set of windows that are "resurrected" before writing to disk to
|
||||
* make closing windows one after the other until shutdown work as expected.
|
||||
*
|
||||
* This function should only be called when we are sure that there has been
|
||||
* a user action that indicates the browser is actively being used and all
|
||||
* windows that have been closed before are not part of a series of closing
|
||||
* windows.
|
||||
*/
|
||||
_clearRestoringWindows: function ssi_clearRestoringWindows() {
|
||||
for (let i = 0; i < this._closedWindows.length; i++) {
|
||||
delete this._closedWindows[i]._shouldRestore;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset state to prepare for a new session state to be restored.
|
||||
*/
|
||||
|
|
|
@ -46,6 +46,7 @@ EXTRA_JS_MODULES.sessionstore = [
|
|||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES.sessionstore += [
|
||||
'RevivableWindows.jsm',
|
||||
'SessionSaver.jsm',
|
||||
'SessionStore.jsm',
|
||||
]
|
||||
|
|
|
@ -81,6 +81,7 @@ skip-if = buildapp == 'mulet'
|
|||
[browser_merge_closed_tabs.js]
|
||||
[browser_pageStyle.js]
|
||||
[browser_privatetabs.js]
|
||||
[browser_revive_windows.js]
|
||||
[browser_scrollPositions.js]
|
||||
[browser_sessionHistory.js]
|
||||
skip-if = e10s
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const IS_MAC = ("nsILocalFileMac" in Ci);
|
||||
const URL_PREFIX = "about:mozilla?t=browser_revive_windows&r=";
|
||||
const PREF_MAX_UNDO = "browser.sessionstore.max_windows_undo";
|
||||
|
||||
const URL_MAIN_WINDOW = URL_PREFIX + Math.random();
|
||||
const URL_ADD_WINDOW1 = URL_PREFIX + Math.random();
|
||||
const URL_ADD_WINDOW2 = URL_PREFIX + Math.random();
|
||||
const URL_CLOSED_WINDOW = URL_PREFIX + Math.random();
|
||||
|
||||
add_task(function* setup() {
|
||||
registerCleanupFunction(() => Services.prefs.clearUserPref(PREF_MAX_UNDO));
|
||||
});
|
||||
|
||||
/**
|
||||
* This test ensure that when closing windows in succession until the browser
|
||||
* quits we are able to revive more windows than we keep around for the
|
||||
* "Undo Close Window" feature.
|
||||
*/
|
||||
add_task(function* test_revive_windows() {
|
||||
// We can restore a single window max.
|
||||
Services.prefs.setIntPref(PREF_MAX_UNDO, 1);
|
||||
|
||||
// Clear list of closed windows.
|
||||
forgetClosedWindows();
|
||||
|
||||
let windows = [];
|
||||
|
||||
// Create three windows.
|
||||
for (let i = 0; i < 3; i++) {
|
||||
let win = yield promiseNewWindow();
|
||||
windows.push(win);
|
||||
|
||||
let tab = win.gBrowser.addTab("about:mozilla");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
}
|
||||
|
||||
// Close all windows.
|
||||
for (let win of windows) {
|
||||
yield promiseWindowClosed(win);
|
||||
}
|
||||
|
||||
is(ss.getClosedWindowCount(), 1, "one window restorable");
|
||||
|
||||
// Save to disk and read.
|
||||
let state = JSON.parse(yield promiseRecoveryFileContents());
|
||||
|
||||
// Check number of windows.
|
||||
if (IS_MAC) {
|
||||
is(state.windows.length, 1, "one open window");
|
||||
is(state._closedWindows.length, 1, "one closed window");
|
||||
} else {
|
||||
is(state.windows.length, 4, "four open windows");
|
||||
is(state._closedWindows.length, 0, "closed windows");
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This test ensures that when closing windows one after the other until the
|
||||
* browser shuts down (on Windows and Linux) we revive closed windows in the
|
||||
* right order.
|
||||
*/
|
||||
add_task(function* test_revive_windows_order() {
|
||||
// We can restore up to three windows max.
|
||||
Services.prefs.setIntPref(PREF_MAX_UNDO, 3);
|
||||
|
||||
// Clear list of closed windows.
|
||||
forgetClosedWindows();
|
||||
|
||||
let tab = gBrowser.addTab(URL_MAIN_WINDOW);
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
registerCleanupFunction(() => gBrowser.removeTab(tab));
|
||||
|
||||
let win0 = yield promiseNewWindow();
|
||||
let tab0 = win0.gBrowser.addTab(URL_CLOSED_WINDOW);
|
||||
yield promiseBrowserLoaded(tab0.linkedBrowser);
|
||||
|
||||
yield promiseWindowClosed(win0);
|
||||
let data = ss.getClosedWindowData();
|
||||
ok(data.contains(URL_CLOSED_WINDOW), "window is restorable");
|
||||
|
||||
let win1 = yield promiseNewWindow();
|
||||
let tab1 = win1.gBrowser.addTab(URL_ADD_WINDOW1);
|
||||
yield promiseBrowserLoaded(tab1.linkedBrowser);
|
||||
|
||||
let win2 = yield promiseNewWindow();
|
||||
let tab2 = win2.gBrowser.addTab(URL_ADD_WINDOW2);
|
||||
yield promiseBrowserLoaded(tab2.linkedBrowser);
|
||||
|
||||
// Close both windows so that |win1| will be opened first and would be
|
||||
// behind |win2| that was closed later.
|
||||
yield promiseWindowClosed(win1);
|
||||
yield promiseWindowClosed(win2);
|
||||
|
||||
// Repeat the checks once.
|
||||
for (let i = 0; i < 2; i++) {
|
||||
info(`checking window data, iteration #${i}`);
|
||||
let contents = yield promiseRecoveryFileContents();
|
||||
let {windows, _closedWindows: closedWindows} = JSON.parse(contents);
|
||||
|
||||
if (IS_MAC) {
|
||||
// Check number of windows.
|
||||
is(windows.length, 1, "one open window");
|
||||
is(closedWindows.length, 3, "three closed windows");
|
||||
|
||||
// Check open window.
|
||||
ok(JSON.stringify(windows).contains(URL_MAIN_WINDOW),
|
||||
"open window is correct");
|
||||
|
||||
// Check closed windows.
|
||||
ok(JSON.stringify(closedWindows[0]).contains(URL_ADD_WINDOW2),
|
||||
"correct first additional window");
|
||||
ok(JSON.stringify(closedWindows[1]).contains(URL_ADD_WINDOW1),
|
||||
"correct second additional window");
|
||||
ok(JSON.stringify(closedWindows[2]).contains(URL_CLOSED_WINDOW),
|
||||
"correct main window");
|
||||
} else {
|
||||
// Check number of windows.
|
||||
is(windows.length, 3, "three open windows");
|
||||
is(closedWindows.length, 1, "one closed window");
|
||||
|
||||
// Check closed window.
|
||||
ok(JSON.stringify(closedWindows).contains(URL_CLOSED_WINDOW),
|
||||
"closed window is correct");
|
||||
|
||||
// Check that windows are in the right order.
|
||||
ok(JSON.stringify(windows[0]).contains(URL_ADD_WINDOW1),
|
||||
"correct first additional window");
|
||||
ok(JSON.stringify(windows[1]).contains(URL_ADD_WINDOW2),
|
||||
"correct second additional window");
|
||||
ok(JSON.stringify(windows[2]).contains(URL_MAIN_WINDOW),
|
||||
"correct main window");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function promiseNewWindow() {
|
||||
return new Promise(resolve => whenNewWindowLoaded({private: false}, resolve));
|
||||
}
|
||||
|
||||
function forgetClosedWindows() {
|
||||
while (ss.getClosedWindowCount()) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
}
|
|
@ -139,7 +139,6 @@ skip-if = os == "mac" || e10s # Bug 895426
|
|||
[browser_dbg_breakpoints-other-tabs.js]
|
||||
[browser_dbg_breakpoints-pane.js]
|
||||
[browser_dbg_breakpoints-reload.js]
|
||||
skip-if = (os == "linux") && debug # Bug 1076830
|
||||
[browser_dbg_chrome-create.js]
|
||||
[browser_dbg_chrome-debugging.js]
|
||||
[browser_dbg_clean-exit-window.js]
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<title>Debugger Breakpoints Other Tabs Test Page</title>
|
||||
</head>
|
||||
<script>
|
||||
(function () {
|
||||
function theTest() {
|
||||
window.foo = "break on me";
|
||||
}());
|
||||
}
|
||||
theTest();
|
||||
</script>
|
||||
|
|
|
@ -77,7 +77,7 @@ function getFindBar(domWindow) {
|
|||
var browser = getContainingBrowser(domWindow);
|
||||
try {
|
||||
var tabbrowser = browser.getTabBrowser();
|
||||
var tab = tabbrowser._getTabForBrowser(browser);
|
||||
var tab = tabbrowser.getTabForBrowser(browser);
|
||||
return tabbrowser.getFindBar(tab);
|
||||
} catch (e) {
|
||||
// FF22 has no _getTabForBrowser, and FF24 has no getFindBar
|
||||
|
|
|
@ -285,7 +285,7 @@ let PdfjsChromeUtils = {
|
|||
*/
|
||||
function PdfjsFindbarWrapper(aBrowser) {
|
||||
let tabbrowser = aBrowser.getTabBrowser();
|
||||
let tab = tabbrowser._getTabForBrowser(aBrowser);
|
||||
let tab = tabbrowser.getTabForBrowser(aBrowser);
|
||||
this._findbar = tabbrowser.getFindBar(tab);
|
||||
};
|
||||
|
||||
|
|
|
@ -140,17 +140,22 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
|||
<!ENTITY editThisBookmarkCmd.label "Edit This Bookmark">
|
||||
<!ENTITY bookmarkThisPageCmd.commandkey "d">
|
||||
<!ENTITY markPageCmd.commandkey "l">
|
||||
<!-- LOCALIZATION NOTE (findShareServices.label):
|
||||
- Use the unicode ellipsis char, \u2026,
|
||||
- or use "..." if \u2026 doesn't suit traditions in your locale. -->
|
||||
<!ENTITY findShareServices.label "Find more Share services…">
|
||||
<!ENTITY sharePageCmd.label "Share This Page">
|
||||
<!ENTITY sharePageCmd.commandkey "S">
|
||||
<!ENTITY sharePageCmd.accesskey "s">
|
||||
<!ENTITY shareLinkCmd.label "Share This Link">
|
||||
<!ENTITY shareLinkCmd.accesskey "s">
|
||||
<!ENTITY shareImageCmd.label "Share This Image">
|
||||
<!ENTITY shareImageCmd.accesskey "s">
|
||||
<!ENTITY shareSelectCmd.label "Share Selection">
|
||||
<!ENTITY shareSelectCmd.accesskey "s">
|
||||
<!ENTITY shareVideoCmd.label "Share This Video">
|
||||
<!ENTITY shareVideoCmd.accesskey "s">
|
||||
<!-- LOCALIZATION NOTE (shareLink.accesskey): must be different than the following share access keys -->
|
||||
<!ENTITY shareLink.label "Share This Link">
|
||||
<!ENTITY shareLink.accesskey "h">
|
||||
<!ENTITY shareImage.label "Share This Image">
|
||||
<!ENTITY shareImage.accesskey "r">
|
||||
<!ENTITY shareSelect.label "Share Selection">
|
||||
<!ENTITY shareSelect.accesskey "r">
|
||||
<!ENTITY shareVideo.label "Share This Video">
|
||||
<!ENTITY shareVideo.accesskey "r">
|
||||
<!ENTITY feedsMenu.label "Subscribe">
|
||||
<!ENTITY subscribeToPageMenupopup.label "Subscribe to This Page">
|
||||
<!ENTITY subscribeToPageMenuitem.label "Subscribe to This Page…">
|
||||
|
@ -692,7 +697,11 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
|||
<!ENTITY social.learnMore.accesskey "l">
|
||||
<!ENTITY social.closeNotificationItem.label "Not Now">
|
||||
|
||||
|
||||
<!ENTITY social.directory.label "Activations Directory">
|
||||
<!ENTITY social.directory.text "You can activate Share services from the directory.">
|
||||
<!ENTITY social.directory.button "Take me there!">
|
||||
<!ENTITY social.directory.introText "Click on a service to add it to &brandShortName;.">
|
||||
<!ENTITY social.directory.viewmore.text "View More">
|
||||
|
||||
<!ENTITY customizeMode.tabTitle "Customize &brandShortName;">
|
||||
<!ENTITY customizeMode.menuAndToolbars.header2 "Additional Tools and Features">
|
||||
|
|
|
@ -97,8 +97,11 @@ quit-button.tooltiptext.linux2 = Quit %1$S (%2$S)
|
|||
# %2$S is the keyboard shortcut
|
||||
quit-button.tooltiptext.mac = Quit %1$S (%2$S)
|
||||
|
||||
loop-call-button.label = Invite someone to talk
|
||||
loop-call-button.tooltiptext = Invite someone to talk
|
||||
loop-call-button2.label = Start a conversation
|
||||
loop-call-button2.tooltiptext = Start a conversation
|
||||
|
||||
social-share-button.label = Share This Page
|
||||
social-share-button.tooltiptext = Share This Page
|
||||
|
||||
panic-button.label = Forget
|
||||
panic-button.tooltiptext = Forget about some browsing history
|
||||
|
|
|
@ -4,7 +4,17 @@
|
|||
|
||||
# Panel Strings
|
||||
|
||||
## LOCALIZATION NOTE(clientShortname2): This should not be localized and
|
||||
## should remain "Firefox Hello" for all locales.
|
||||
clientShortname2=Firefox Hello
|
||||
|
||||
## LOCALIZATION_NOTE(first_time_experience.title): clientShortname will be
|
||||
## replaced by the brand name
|
||||
first_time_experience_title={{clientShortname}} — Join the conversation
|
||||
first_time_experience_button_label=Get Started
|
||||
|
||||
share_link_header_text=Share this link to invite someone to talk:
|
||||
invite_header_text=Invite someone to join you.
|
||||
|
||||
## LOCALIZATION NOTE(invitee_name_label): Displayed when obtaining a url.
|
||||
## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#precall-firstrun
|
||||
|
@ -52,12 +62,14 @@ problem_accessing_account=There Was A Problem Accessing Your Account
|
|||
## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#error for location
|
||||
retry_button=Retry
|
||||
|
||||
share_email_subject3=You have been invited to a conversation
|
||||
## LOCALIZATION NOTE (share_email_body3): In this item, don't translate the
|
||||
share_email_subject4={{clientShortname}} — Join the conversation
|
||||
## LOCALIZATION NOTE (share_email_body4): In this item, don't translate the
|
||||
## part between {{..}} and leave the \r\n\r\n part alone
|
||||
share_email_body3=To accept this invitation, just copy or click this link to start your conversation:\r\n\r\n{{callUrl}}
|
||||
share_email_body4=Hello!\r\n\r\nJoin me for a video conversation using {{clientShortname}}:\r\n\r\nYou don't have to download or install anything. Just copy and paste this URL into your browser:\r\n\r\n{{callUrl}}\r\n\r\nIf you want, you can also learn more about {{clientShortname}} at {{learnMoreUrl}}\r\n\r\nTalk to you soon!
|
||||
share_button=Email
|
||||
share_button2=Email Link
|
||||
copy_url_button=Copy
|
||||
copy_url_button2=Copy Link
|
||||
copied_url_button=Copied!
|
||||
|
||||
panel_footer_signin_or_signup_link=Sign In or Sign Up
|
||||
|
@ -242,12 +254,17 @@ connection_error_see_console_notification=Call failed; see console for details.
|
|||
## LOCALIZATION NOTE (legal_text_and_links3): In this item, don't translate the
|
||||
## parts between {{..}} because these will be replaced with links with the labels
|
||||
## from legal_text_tos and legal_text_privacy. clientShortname will be replaced
|
||||
## by the brand name, or fall back to client_shortname_fallback
|
||||
## by the brand name.
|
||||
legal_text_and_links3=By using {{clientShortname}} you agree to the {{terms_of_use}} \
|
||||
and {{privacy_notice}}.
|
||||
legal_text_tos = Terms of Use
|
||||
legal_text_privacy = Privacy Notice
|
||||
client_shortname_fallback=this product
|
||||
|
||||
## LOCALIZATION NOTE (powered_by_beforeLogo, powered_by_afterLogo):
|
||||
## These 2 strings are displayed before and after a 'Telefonica'
|
||||
## logo.
|
||||
powered_by_beforeLogo=Powered by
|
||||
powered_by_afterLogo=
|
||||
|
||||
feedback_call_experience_heading2=How was your conversation?
|
||||
feedback_what_makes_you_sad=What makes you sad?
|
||||
|
@ -273,7 +290,26 @@ feedback_rejoin_button=Rejoin
|
|||
## an abusive user.
|
||||
feedback_report_user_button=Report User
|
||||
|
||||
help_label=Help
|
||||
tour_label=Tour
|
||||
|
||||
## LOCALIZATION NOTE(rooms_default_room_name_template): {{conversationLabel}}
|
||||
## will be replaced by a number. For example "Conversation 1" or "Conversation 12".
|
||||
rooms_default_room_name_template=Conversation {{conversationLabel}}
|
||||
rooms_leave_button_label=Leave
|
||||
rooms_list_copy_url_tooltip=Copy Link
|
||||
## LOCALIZATION NOTE (rooms_list_current_conversations): We prefer to have no
|
||||
## number in the string, but if you need it for your language please use {{num}}.
|
||||
rooms_list_current_conversations=Current conversation;Current conversations
|
||||
rooms_list_delete_tooltip=Delete conversation
|
||||
rooms_list_deleteConfirmation_label=Are you sure?
|
||||
rooms_list_no_current_conversations=No current conversations
|
||||
rooms_name_this_room_label=Name this conversation
|
||||
rooms_new_room_button_label=Start a conversation
|
||||
rooms_only_occupant_label=You're the first one here.
|
||||
rooms_panel_title=Choose a conversation or start a new one
|
||||
rooms_room_full_label=There are already two people in this conversation.
|
||||
rooms_room_full_call_to_action_nonFx_label=Download {{brandShortname}} to start your own
|
||||
rooms_room_full_call_to_action_label=Learn more about {{clientShortname}} »
|
||||
rooms_room_joined_label=Someone has joined the conversation!
|
||||
rooms_room_join_label=Join the conversation
|
||||
|
|
|
@ -12,8 +12,9 @@ const Ci = Components.interfaces;
|
|||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
|
||||
// The minimum sizes for the auto-resize panel code.
|
||||
const PANEL_MIN_HEIGHT = 100;
|
||||
// The minimum sizes for the auto-resize panel code, minimum size necessary to
|
||||
// properly show the error page in the panel.
|
||||
const PANEL_MIN_HEIGHT = 200;
|
||||
const PANEL_MIN_WIDTH = 330;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
@ -313,6 +314,7 @@ function CreateSocialMarkWidget(aId, aProvider) {
|
|||
let menuLabel = window.gNavigatorBundle.getFormattedString("social.markpageMenu.label", [aProvider.name]);
|
||||
node.setAttribute("label", menuLabel);
|
||||
node.setAttribute("tooltiptext", menuLabel);
|
||||
node.setAttribute("observes", "Social:PageShareOrMark");
|
||||
|
||||
return node;
|
||||
}
|
||||
|
@ -350,10 +352,12 @@ SocialErrorListener.prototype = {
|
|||
if (aRequest instanceof Ci.nsIHttpChannel) {
|
||||
try {
|
||||
// Change the frame to an error page on 4xx (client errors)
|
||||
// and 5xx (server errors)
|
||||
// and 5xx (server errors). responseStatus throws if it is not set.
|
||||
failure = aRequest.responseStatus >= 400 &&
|
||||
aRequest.responseStatus < 600;
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
failure = aStatus == Components.results.NS_ERROR_CONNECTION_REFUSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,8 +365,11 @@ SocialErrorListener.prototype = {
|
|||
// so avoid doing that more than once
|
||||
if (failure && aStatus != Components.results.NS_BINDING_ABORTED) {
|
||||
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
|
||||
let provider = Social._getProviderFromOrigin(this.iframe.getAttribute("origin"));
|
||||
provider.errorState = "content-error";
|
||||
let origin = this.iframe.getAttribute("origin");
|
||||
if (origin) {
|
||||
let provider = Social._getProviderFromOrigin(origin);
|
||||
provider.errorState = "content-error";
|
||||
}
|
||||
this.setErrorMessage(aWebProgress.QueryInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler);
|
||||
}
|
||||
|
@ -371,9 +378,12 @@ SocialErrorListener.prototype = {
|
|||
onLocationChange: function SPL_onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
|
||||
if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
|
||||
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
|
||||
let provider = Social._getProviderFromOrigin(this.iframe.getAttribute("origin"));
|
||||
if (!provider.errorState)
|
||||
provider.errorState = "content-error";
|
||||
let origin = this.iframe.getAttribute("origin");
|
||||
if (origin) {
|
||||
let provider = Social._getProviderFromOrigin(origin);
|
||||
if (!provider.errorState)
|
||||
provider.errorState = "content-error";
|
||||
}
|
||||
schedule(function() {
|
||||
this.setErrorMessage(aWebProgress.QueryInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler);
|
||||
|
|
|
@ -65,7 +65,7 @@ this.webrtcUI = {
|
|||
let browser = aStream.browser;
|
||||
let browserWindow = browser.ownerDocument.defaultView;
|
||||
let tab = browserWindow.gBrowser &&
|
||||
browserWindow.gBrowser._getTabForBrowser(browser);
|
||||
browserWindow.gBrowser.getTabForBrowser(browser);
|
||||
return {uri: state.documentURI, tab: tab, browser: browser, types: types};
|
||||
});
|
||||
},
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
% This Source Code Form is subject to the terms of the Mozilla Public
|
||||
% License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
% file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
%include ../shared/devedition.inc.css
|
|
@ -15,13 +15,15 @@ browser.jar:
|
|||
skin/classic/browser/aboutCertError_sectionExpanded.png
|
||||
skin/classic/browser/aboutNetError.css (../shared/aboutNetError.css)
|
||||
skin/classic/browser/aboutNetError_info.svg (../shared/aboutNetError_info.svg)
|
||||
skin/classic/browser/aboutSocialError.css
|
||||
skin/classic/browser/aboutSocialError.css (../shared/aboutSocialError.css)
|
||||
* skin/classic/browser/aboutProviderDirectory.css (../shared/aboutProviderDirectory.css)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/aboutSyncTabs.css
|
||||
#endif
|
||||
skin/classic/browser/aboutTabCrashed.css
|
||||
skin/classic/browser/actionicon-tab.png
|
||||
* skin/classic/browser/browser.css
|
||||
* skin/classic/browser/devedition.css
|
||||
* skin/classic/browser/browser-lightweightTheme.css
|
||||
skin/classic/browser/click-to-play-warning-stripes.png
|
||||
* skin/classic/browser/content-contextmenu.svg
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
body {
|
||||
background-color: rgb(241, 244, 248);
|
||||
margin-top: 2em;
|
||||
font: message-box;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
#error-box {
|
||||
background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
|
||||
-moz-padding-start: 30px;
|
||||
}
|
||||
|
||||
#error-box:-moz-locale-dir(rtl) {
|
||||
background-position: right 4px;
|
||||
}
|
||||
|
||||
#main-error-msg {
|
||||
color: #4b4b4b;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
#button-box {
|
||||
text-align: center;
|
||||
width: 75%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media all and (min-width: 300px) {
|
||||
#error-box {
|
||||
max-width: 50%;
|
||||
margin: 0 auto;
|
||||
background-image: url('chrome://global/skin/icons/information-32.png');
|
||||
min-height: 36px;
|
||||
-moz-padding-start: 38px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: auto !important;
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 780px) {
|
||||
#error-box {
|
||||
max-width: 30%;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
font: message-box;
|
||||
font-size: 0.6875em;
|
||||
-moz-appearance: none;
|
||||
-moz-user-select: none;
|
||||
width: 100%;
|
||||
margin: 2px 0;
|
||||
padding: 2px 6px;
|
||||
line-height: 1.2;
|
||||
background-color: hsla(210,30%,95%,.1);
|
||||
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
|
||||
background-clip: padding-box;
|
||||
border: 1px solid hsla(210,15%,25%,.4);
|
||||
border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4);
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
|
||||
0 0 0 1px hsla(0,0%,100%,.3) inset,
|
||||
0 1px 0 hsla(0,0%,100%,.1);
|
||||
|
||||
transition-property: background-color, border-color, box-shadow;
|
||||
transition-duration: 150ms;
|
||||
transition-timing-function: ease;
|
||||
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: hsla(210,30%,95%,.8);
|
||||
border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55);
|
||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
|
||||
0 0 0 1px hsla(0,0%,100%,.3) inset,
|
||||
0 1px 0 hsla(0,0%,100%,.1),
|
||||
0 0 3px hsla(210,15%,25%,.1);
|
||||
transition-property: background-color, border-color, box-shadow;
|
||||
transition-duration: 150ms;
|
||||
transition-timing-function: ease;
|
||||
}
|
||||
|
||||
button:hover:active {
|
||||
background-color: hsla(210,15%,25%,.2);
|
||||
box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset,
|
||||
0 0 2px hsla(210,15%,25%,.4) inset;
|
||||
transition-property: background-color, border-color, box-shadow;
|
||||
transition-duration: 10ms;
|
||||
transition-timing-function: linear;
|
||||
}
|
|
@ -1368,6 +1368,11 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
|
|||
width: 16px;
|
||||
}
|
||||
|
||||
#add-share-provider {
|
||||
list-style-image: url(chrome://browser/skin/menuPanel-small@2x.png);
|
||||
-moz-image-region: rect(0px, 192px, 32px, 160px);
|
||||
}
|
||||
|
||||
#loop-call-button > .toolbarbutton-badge-container {
|
||||
list-style-image: url("chrome://browser/skin/loop/toolbar@2x.png");
|
||||
-moz-image-region: rect(0, 36px, 36px, 0);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
% This Source Code Form is subject to the terms of the Mozilla Public
|
||||
% License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
% file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
%include ../shared/devedition.inc.css
|
|
@ -14,7 +14,8 @@ browser.jar:
|
|||
skin/classic/browser/aboutCertError_sectionCollapsed.png
|
||||
skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
|
||||
skin/classic/browser/aboutCertError_sectionExpanded.png
|
||||
skin/classic/browser/aboutSocialError.css
|
||||
skin/classic/browser/aboutSocialError.css (../shared/aboutSocialError.css)
|
||||
* skin/classic/browser/aboutProviderDirectory.css (../shared/aboutProviderDirectory.css)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/aboutSyncTabs.css
|
||||
#endif
|
||||
|
@ -22,6 +23,7 @@ browser.jar:
|
|||
skin/classic/browser/actionicon-tab.png
|
||||
skin/classic/browser/actionicon-tab@2x.png
|
||||
* skin/classic/browser/browser.css (browser.css)
|
||||
* skin/classic/browser/devedition.css
|
||||
* skin/classic/browser/browser-lightweightTheme.css
|
||||
skin/classic/browser/click-to-play-warning-stripes.png
|
||||
* skin/classic/browser/content-contextmenu.svg
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
%include aboutSocialError.css
|
||||
|
||||
body {
|
||||
width: 310px;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
#message-box {
|
||||
margin-top: 2em;
|
||||
background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
|
||||
-moz-padding-start: 30px;
|
||||
}
|
||||
|
||||
#activation-frame {
|
||||
border: none;
|
||||
margin: 0;
|
||||
width: 310px;
|
||||
height: 200px;
|
||||
}
|
||||
#activation > p {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
line-height: 2em;
|
||||
}
|
||||
.link {
|
||||
text-decoration: none;
|
||||
color: -moz-nativehyperlinktext;
|
||||
cursor: pointer;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
% 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/.
|
|
@ -234,3 +234,8 @@ toolbarpaletteitem[place="palette"] > #zoom-controls > #zoom-out-button {
|
|||
toolbarpaletteitem[place="palette"] > #zoom-controls > #zoom-in-button {
|
||||
-moz-image-region: rect(0px, 96px, 16px, 80px);
|
||||
}
|
||||
|
||||
#add-share-provider {
|
||||
list-style-image: url(chrome://browser/skin/menuPanel-small.png);
|
||||
-moz-image-region: rect(0px, 96px, 16px, 80px);
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
body {
|
||||
background-color: rgb(241, 244, 248);
|
||||
margin-top: 2em;
|
||||
font: message-box;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
#error-box {
|
||||
background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
|
||||
-moz-padding-start: 30px;
|
||||
}
|
||||
|
||||
#error-box:-moz-locale-dir(rtl) {
|
||||
background-position: right 4px;
|
||||
}
|
||||
|
||||
#main-error-msg {
|
||||
color: #4b4b4b;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
#button-box {
|
||||
text-align: center;
|
||||
width: 75%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media all and (min-width: 300px) {
|
||||
#error-box {
|
||||
max-width: 50%;
|
||||
margin: 0 auto;
|
||||
background-image: url('chrome://global/skin/icons/information-32.png');
|
||||
min-height: 36px;
|
||||
-moz-padding-start: 38px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: auto !important;
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 780px) {
|
||||
#error-box {
|
||||
max-width: 30%;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
font: message-box;
|
||||
font-size: 0.6875em;
|
||||
-moz-appearance: none;
|
||||
-moz-user-select: none;
|
||||
width: 100%;
|
||||
margin: 2px 0;
|
||||
padding: 2px 6px;
|
||||
line-height: 1.2;
|
||||
background-color: hsla(210,30%,95%,.1);
|
||||
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
|
||||
background-clip: padding-box;
|
||||
border: 1px solid hsla(210,15%,25%,.4);
|
||||
border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4);
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
|
||||
0 0 0 1px hsla(0,0%,100%,.3) inset,
|
||||
0 1px 0 hsla(0,0%,100%,.1);
|
||||
|
||||
transition-property: background-color, border-color, box-shadow;
|
||||
transition-duration: 150ms;
|
||||
transition-timing-function: ease;
|
||||
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: hsla(210,30%,95%,.8);
|
||||
border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55);
|
||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
|
||||
0 0 0 1px hsla(0,0%,100%,.3) inset,
|
||||
0 1px 0 hsla(0,0%,100%,.1),
|
||||
0 0 3px hsla(210,15%,25%,.1);
|
||||
transition-property: background-color, border-color, box-shadow;
|
||||
transition-duration: 150ms;
|
||||
transition-timing-function: ease;
|
||||
}
|
||||
|
||||
button:hover:active {
|
||||
background-color: hsla(210,15%,25%,.2);
|
||||
box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset,
|
||||
0 0 2px hsla(210,15%,25%,.4) inset;
|
||||
transition-property: background-color, border-color, box-shadow;
|
||||
transition-duration: 10ms;
|
||||
transition-timing-function: linear;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
% 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/.
|
||||
|
||||
%define WINDOWS_AERO
|
||||
%include devedition.css
|
||||
%undef WINDOWS_AERO
|
|
@ -0,0 +1,5 @@
|
|||
% This Source Code Form is subject to the terms of the Mozilla Public
|
||||
% License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
% file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
%include ../shared/devedition.inc.css
|
|
@ -17,13 +17,15 @@ browser.jar:
|
|||
skin/classic/browser/aboutCertError_sectionExpanded.png
|
||||
skin/classic/browser/aboutNetError.css (../shared/aboutNetError.css)
|
||||
skin/classic/browser/aboutNetError_info.svg (../shared/aboutNetError_info.svg)
|
||||
skin/classic/browser/aboutSocialError.css
|
||||
skin/classic/browser/aboutSocialError.css (../shared/aboutSocialError.css)
|
||||
* skin/classic/browser/aboutProviderDirectory.css (../shared/aboutProviderDirectory.css)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/aboutSyncTabs.css
|
||||
#endif
|
||||
skin/classic/browser/aboutTabCrashed.css
|
||||
skin/classic/browser/actionicon-tab.png
|
||||
* skin/classic/browser/browser.css
|
||||
* skin/classic/browser/devedition.css
|
||||
* skin/classic/browser/browser-lightweightTheme.css
|
||||
skin/classic/browser/click-to-play-warning-stripes.png
|
||||
* skin/classic/browser/content-contextmenu.svg
|
||||
|
@ -438,7 +440,8 @@ browser.jar:
|
|||
skin/classic/aero/browser/aboutCertError_sectionExpanded.png
|
||||
skin/classic/aero/browser/aboutNetError.css (../shared/aboutNetError.css)
|
||||
skin/classic/aero/browser/aboutNetError_info.svg (../shared/aboutNetError_info.svg)
|
||||
skin/classic/aero/browser/aboutSocialError.css
|
||||
skin/classic/aero/browser/aboutSocialError.css (../shared/aboutSocialError.css)
|
||||
* skin/classic/aero/browser/aboutProviderDirectory.css (../shared/aboutProviderDirectory.css)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/aero/browser/aboutSyncTabs.css
|
||||
#endif
|
||||
|
@ -446,6 +449,7 @@ browser.jar:
|
|||
skin/classic/aero/browser/aboutWelcomeBack.css (../shared/aboutWelcomeBack.css)
|
||||
skin/classic/aero/browser/actionicon-tab.png
|
||||
* skin/classic/aero/browser/browser.css (browser-aero.css)
|
||||
* skin/classic/aero/browser/devedition.css (devedition-aero.css)
|
||||
* skin/classic/aero/browser/browser-lightweightTheme.css
|
||||
skin/classic/aero/browser/click-to-play-warning-stripes.png
|
||||
* skin/classic/aero/browser/content-contextmenu.svg
|
||||
|
|
|
@ -153,7 +153,10 @@ GsmIccInfo::GsmIccInfo(nsPIDOMWindow* aWindow)
|
|||
void
|
||||
GsmIccInfo::Update(nsIGsmIccInfo* aInfo)
|
||||
{
|
||||
IccInfo::Update(aInfo);
|
||||
nsCOMPtr<nsIIccInfo> iccInfo = do_QueryInterface(aInfo);
|
||||
MOZ_ASSERT(iccInfo);
|
||||
|
||||
IccInfo::Update(iccInfo);
|
||||
mGsmIccInfo = aInfo;
|
||||
}
|
||||
|
||||
|
@ -192,7 +195,10 @@ CdmaIccInfo::CdmaIccInfo(nsPIDOMWindow* aWindow)
|
|||
void
|
||||
CdmaIccInfo::Update(nsICdmaIccInfo* aInfo)
|
||||
{
|
||||
IccInfo::Update(aInfo);
|
||||
nsCOMPtr<nsIIccInfo> iccInfo = do_QueryInterface(aInfo);
|
||||
MOZ_ASSERT(iccInfo);
|
||||
|
||||
IccInfo::Update(iccInfo);
|
||||
mCdmaIccInfo = aInfo;
|
||||
}
|
||||
|
||||
|
|
|
@ -229,6 +229,8 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
private BrowserHealthReporter mBrowserHealthReporter;
|
||||
|
||||
private SystemBarTintManager mTintManager;
|
||||
|
||||
// The tab to be selected on editing mode exit.
|
||||
private Integer mTargetTabForEditingMode;
|
||||
|
||||
|
@ -681,9 +683,9 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
|
||||
SystemBarTintManager tintManager = new SystemBarTintManager(this);
|
||||
tintManager.setTintColor(getResources().getColor(R.color.background_tabs));
|
||||
tintManager.setStatusBarTintEnabled(true);
|
||||
mTintManager = new SystemBarTintManager(this);
|
||||
mTintManager.setTintColor(getResources().getColor(R.color.background_tabs));
|
||||
mTintManager.setStatusBarTintEnabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2374,6 +2376,11 @@ public class BrowserApp extends GeckoApp
|
|||
view.getHitRect(mTempRect);
|
||||
mTempRect.offset(-view.getScrollX(), -view.getScrollY());
|
||||
|
||||
if (mTintManager != null) {
|
||||
SystemBarTintManager.SystemBarConfig config = mTintManager.getConfig();
|
||||
mTempRect.offset(0, -config.getPixelInsetTop(false));
|
||||
}
|
||||
|
||||
int[] viewCoords = new int[2];
|
||||
view.getLocationOnScreen(viewCoords);
|
||||
|
||||
|
@ -2649,6 +2656,10 @@ public class BrowserApp extends GeckoApp
|
|||
mLayerView.getLayerMarginsAnimator().setMaxMargins(0, mToolbarHeight, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (mTintManager != null) {
|
||||
mTintManager.setStatusBarTintEnabled(!fullscreen);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,22 +5,18 @@
|
|||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.lang.StringBuilder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
import java.util.Set;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
|
@ -87,14 +83,34 @@ public class RestrictedProfiles {
|
|||
throw new IllegalArgumentException("Unknown action " + action);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
@RobocopTarget
|
||||
private static Bundle getRestrictions() {
|
||||
final UserManager mgr = (UserManager) GeckoAppShell.getContext().getSystemService(Context.USER_SERVICE);
|
||||
return mgr.getUserRestrictions();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method does the system version check for you.
|
||||
*
|
||||
* Returns false if the system doesn't support restrictions,
|
||||
* or the provided value is not present in the set of user
|
||||
* restrictions.
|
||||
*
|
||||
* Returns true otherwise.
|
||||
*/
|
||||
private static boolean getRestriction(final String name) {
|
||||
// Early versions don't support restrictions at all,
|
||||
// so no action can be restricted.
|
||||
if (Versions.preJBMR2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getRestrictions().getBoolean(name, false);
|
||||
}
|
||||
|
||||
private static boolean canLoadUrl(final String url) {
|
||||
// Null urls are always allowed
|
||||
// Null URLs are always permitted.
|
||||
if (url == null) {
|
||||
return true;
|
||||
}
|
||||
|
@ -102,10 +118,10 @@ public class RestrictedProfiles {
|
|||
try {
|
||||
// If we're not in guest mode, and the system restriction isn't in place, everything is allowed.
|
||||
if (!getInGuest() &&
|
||||
!getRestrictions().getBoolean(Restriction.DISALLOW_BROWSE_FILES.name, false)) {
|
||||
!getRestriction(Restriction.DISALLOW_BROWSE_FILES.name)) {
|
||||
return true;
|
||||
}
|
||||
} catch(IllegalArgumentException ex) {
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Log.i(LOGTAG, "Invalid action", ex);
|
||||
}
|
||||
|
||||
|
@ -121,13 +137,13 @@ public class RestrictedProfiles {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: The UserManager should support blacklisting urls by the device owner.
|
||||
// TODO: The UserManager should support blacklisting URLs by the device owner.
|
||||
return true;
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
public static boolean isUserRestricted() {
|
||||
// Guest mode is supported in all Android versions
|
||||
// Guest mode is supported in all Android versions.
|
||||
if (getInGuest()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -145,34 +161,27 @@ public class RestrictedProfiles {
|
|||
|
||||
@WrapElementForJNI
|
||||
public static boolean isAllowed(int action, String url) {
|
||||
// Guest users can't do anything.
|
||||
if (getInGuest()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Restriction restriction;
|
||||
try {
|
||||
restriction = geckoActionToRestriction(action);
|
||||
} catch(IllegalArgumentException ex) {
|
||||
return true;
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Unknown actions represent a coding error, so we
|
||||
// refuse the action and log.
|
||||
Log.e(LOGTAG, "Unknown action " + action + "; check calling code.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Restriction.DISALLOW_BROWSE_FILES == restriction) {
|
||||
return canLoadUrl(url);
|
||||
}
|
||||
|
||||
// ALl actions are blocked in Guest mode
|
||||
if (getInGuest()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Versions.preJBMR2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
// NOTE: Restrictions hold the opposite intention, so we need to flip it
|
||||
return !getRestrictions().getBoolean(restriction.name, false);
|
||||
} catch(IllegalArgumentException ex) {
|
||||
Log.i(LOGTAG, "Invalid action", ex);
|
||||
}
|
||||
|
||||
return true;
|
||||
// NOTE: Restrictions hold the opposite intention, so we need to flip it.
|
||||
return !getRestriction(restriction.name);
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.text.TextUtils;
|
|||
import android.util.Log;
|
||||
|
||||
public class StringUtils {
|
||||
private static final String LOGTAG = "GeckoStringUtils";
|
||||
|
||||
private static final String FILTER_URL_PREFIX = "filter://";
|
||||
private static final String USER_ENTERED_URL_PREFIX = "user-entered:";
|
||||
|
@ -193,7 +194,10 @@ public class StringUtils {
|
|||
try {
|
||||
return intent.getStringExtra(name);
|
||||
} catch (android.os.BadParcelableException ex) {
|
||||
Log.w("GeckoUtils", "Couldn't get string extra: malformed intent.");
|
||||
Log.w(LOGTAG, "Couldn't get string extra: malformed intent.");
|
||||
return null;
|
||||
} catch (RuntimeException re) {
|
||||
Log.w(LOGTAG, "Couldn't get string extra.", re);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -185,7 +185,7 @@ this.FxAccountsOAuthClient.prototype = {
|
|||
let tabbrowser = target.getTabBrowser();
|
||||
|
||||
if (tabbrowser) {
|
||||
let tab = tabbrowser._getTabForBrowser(target);
|
||||
let tab = tabbrowser.getTabForBrowser(target);
|
||||
|
||||
if (tab) {
|
||||
tabbrowser.removeTab(tab);
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
/* 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";
|
||||
|
||||
/**
|
||||
* Asynchronous API for managing history.
|
||||
*
|
||||
*
|
||||
* The API makes use of `PageInfo` and `VisitInfo` objects, defined as follows.
|
||||
*
|
||||
* A `PageInfo` object is any object that contains A SUBSET of the
|
||||
* following properties:
|
||||
* - guid: (string)
|
||||
* The globally unique id of the page.
|
||||
* - uri: (URL)
|
||||
* or (nsIURI)
|
||||
* or (string)
|
||||
* The full URI of the page. Note that `PageInfo` values passed as
|
||||
* argument may hold `nsIURI` or `string` values for property `uri`,
|
||||
* but `PageInfo` objects returned by this module always hold `URL`
|
||||
* values.
|
||||
* - title: (string)
|
||||
* The title associated with the page, if any.
|
||||
* - frecency: (number)
|
||||
* The frecency of the page, if any.
|
||||
* See https://developer.mozilla.org/en-US/docs/Mozilla/Tech/Places/Frecency_algorithm
|
||||
* Note that this property may not be used to change the actualy frecency
|
||||
* score of a page, only to retrieve it. In other words, any `frecency` field
|
||||
* passed as argument to a function of this API will be ignored.
|
||||
* - visits: (Array<VisitInfo>)
|
||||
* All the visits for this page, if any.
|
||||
*
|
||||
* See the documentation of individual methods to find out which properties
|
||||
* are required for `PageInfo` arguments or returned for `PageInfo` results.
|
||||
*
|
||||
* A `VisitInfo` object is any object that contains A SUBSET of the following
|
||||
* properties:
|
||||
* - date: (Date)
|
||||
* The time the visit occurred.
|
||||
* - transition: (number)
|
||||
* How the user reached the page. See constants `TRANSITION_*`
|
||||
* for the possible transition types.
|
||||
* - referrer: (URL)
|
||||
* or (nsIURI)
|
||||
* or (string)
|
||||
* The referring URI of this visit. Note that `VisitInfo` passed
|
||||
* as argument may hold `nsIURI` or `string` values for property `referrer`,
|
||||
* but `VisitInfo` objects returned by this module always hold `URL`
|
||||
* values.
|
||||
* See the documentation of individual methods to find out which properties
|
||||
* are required for `VisitInfo` arguments or returned for `VisitInfo` results.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Each successful operation notifies through the nsINavHistoryObserver
|
||||
* interface. To listen to such notifications you must register using
|
||||
* nsINavHistoryService `addObserver` and `removeObserver` methods.
|
||||
* @see nsINavHistoryObserver
|
||||
*/
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "History" ];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
|
||||
"resource://gre/modules/Sqlite.jsm");
|
||||
|
||||
|
||||
this.History = Object.freeze({
|
||||
/**
|
||||
* Fetch the available information for one page.
|
||||
*
|
||||
* @param guidOrURI: (URL or nsIURI)
|
||||
* The full URI of the page.
|
||||
* or (string)
|
||||
* Either the full URI of the page or the GUID of the page.
|
||||
*
|
||||
* @return (Promise)
|
||||
* A promise resolved once the operation is complete.
|
||||
* @resolves (PageInfo | null) If the page could be found, the information
|
||||
* on that page. Note that this `PageInfo` does NOT contain the visit
|
||||
* data (i.e. `visits` is `undefined`).
|
||||
*
|
||||
* @throws (Error)
|
||||
* If `guidOrURI` does not have the expected type or if it is a string
|
||||
* that may be parsed neither as a valid URL nor as a valid GUID.
|
||||
*/
|
||||
fetch: function (guidOrURI) {
|
||||
throw new Error("Method not implemented");
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a set of visits for one or more page.
|
||||
*
|
||||
* Any change may be observed through nsINavHistoryObserver
|
||||
*
|
||||
* @note This function recomputes the frecency of the page automatically,
|
||||
* regardless of the value of property `frecency` passed as argument.
|
||||
* @note If there is no entry for the page, the entry is created.
|
||||
*
|
||||
* @param infos: (PageInfo)
|
||||
* Information on a page. This `PageInfo` MUST contain
|
||||
* - either a property `guid` or a property `uri`, as specified
|
||||
* by the definition of `PageInfo`;
|
||||
* - a property `visits`, as specified by the definition of
|
||||
* `PageInfo`, which MUST contain at least one visit.
|
||||
* If a property `title` is provided, the title of the page
|
||||
* is updated.
|
||||
* If the `visitDate` of a visit is not provided, it defaults
|
||||
* to now.
|
||||
* or (Array<PageInfo>)
|
||||
* An array of the above, to batch requests.
|
||||
* @param onResult: (function(PageInfo), [optional])
|
||||
* A callback invoked for each page, with the updated
|
||||
* information on that page. Note that this `PageInfo`
|
||||
* does NOT contain the visit data (i.e. `visits` is
|
||||
* `undefined`).
|
||||
*
|
||||
* @return (Promise)
|
||||
* A promise resolved once the operation is complete, including
|
||||
* all calls to `onResult`.
|
||||
* @resolves (bool)
|
||||
* `true` if at least one page entry was created, `false` otherwise
|
||||
* (i.e. if page entries were updated but not created).
|
||||
*
|
||||
* @throws (Error)
|
||||
* If the `uri` specified was for a protocol that should not be
|
||||
* stored (e.g. "chrome:", "mailbox:", "about:", "imap:", "news:",
|
||||
* "moz-anno:", "view-source:", "resource:", "data:", "wyciwyg:",
|
||||
* "javascript:", "blob:").
|
||||
* @throws (Error)
|
||||
* If `infos` has an unexpected type.
|
||||
* @throws (Error)
|
||||
* If a `PageInfo` has neither `guid` nor `uri`,
|
||||
* @throws (Error)
|
||||
* If a `guid` property provided is not a valid GUID.
|
||||
* @throws (Error)
|
||||
* If a `PageInfo` does not have a `visits` property or if the
|
||||
* value of `visits` is ill-typed or is an empty array.
|
||||
* @throws (Error)
|
||||
* If an element of `visits` has an invalid `date`.
|
||||
* @throws (Error)
|
||||
* If an element of `visits` is missing `transition` or if
|
||||
* the value of `transition` is invalid.
|
||||
*/
|
||||
update: function (infos, onResult) {
|
||||
throw new Error("Method not implemented");
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove pages from the database.
|
||||
*
|
||||
* Any change may be observed through nsINavHistoryObserver
|
||||
*
|
||||
*
|
||||
* @param page: (URL or nsIURI)
|
||||
* The full URI of the page.
|
||||
* or (string)
|
||||
* Either the full URI of the page or the GUID of the page.
|
||||
* or (Array<URL|nsIURI|string>)
|
||||
* An array of the above, to batch requests.
|
||||
* @param onResult: (function(PageInfo))
|
||||
* A callback invoked for each page found.
|
||||
*
|
||||
* @return (Promise)
|
||||
* A promise resoled once the operation is complete.
|
||||
* @resolve (bool)
|
||||
* `true` if at least one page was removed, `false` otherwise.
|
||||
* @throws (Error)
|
||||
* If `pages` has an unexpected type or if a string provided
|
||||
* is neither a valid GUID nor a valid URI.
|
||||
*/
|
||||
remove: function (pages, onResult) {
|
||||
throw new Error("Method not implemented");
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if a page has been visited.
|
||||
*
|
||||
* @param pages: (URL or nsIURI)
|
||||
* The full URI of the page.
|
||||
* or (string)
|
||||
* The full URI of the page or the GUID of the page.
|
||||
*
|
||||
* @return (Promise)
|
||||
* A promise resoled once the operation is complete.
|
||||
* @resolve (bool)
|
||||
* `true` if the page has been visited, `false` otherwise.
|
||||
* @throws (Error)
|
||||
* If `pages` has an unexpected type or if a string provided
|
||||
* is neither not a valid GUID nor a valid URI.
|
||||
*/
|
||||
hasVisits: function(page, onResult) {
|
||||
throw new Error("Method not implemented");
|
||||
},
|
||||
|
||||
/**
|
||||
* Possible values for the `transition` property of `VisitInfo`
|
||||
* objects.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The user followed a link and got a new toplevel window.
|
||||
*/
|
||||
TRANSITION_LINK: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
|
||||
/**
|
||||
* The user typed the page's URL in the URL bar or selected it from
|
||||
* URL bar autocomplete results, clicked on it from a history query
|
||||
* (from the History sidebar, History menu, or history query in the
|
||||
* personal toolbar or Places organizer.
|
||||
*/
|
||||
TRANSITION_TYPED: Ci.nsINavHistoryService.TRANSITION_TYPED,
|
||||
|
||||
/**
|
||||
* The user followed a bookmark to get to the page.
|
||||
*/
|
||||
TRANSITION_BOOKMARK: Ci.nsINavHistoryService.TRANSITION_BOOKMARK,
|
||||
|
||||
/**
|
||||
* Some inner content is loaded. This is true of all images on a
|
||||
* page, and the contents of the iframe. It is also true of any
|
||||
* content in a frame if the user did not explicitly follow a link
|
||||
* to get there.
|
||||
*/
|
||||
TRANSITION_EMBED: Ci.nsINavHistoryService.TRANSITION_EMBED,
|
||||
|
||||
/**
|
||||
* Set when the transition was a permanent redirect.
|
||||
*/
|
||||
TRANSITION_REDIRECT_PERMANENT: Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT,
|
||||
|
||||
/**
|
||||
* Set when the transition was a temporary redirect.
|
||||
*/
|
||||
TRANSITION_REDIRECT_TEMPORARY: Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY,
|
||||
|
||||
/**
|
||||
* Set when the transition is a download.
|
||||
*/
|
||||
TRANSITION_DOWNLOAD: Ci.nsINavHistoryService.TRANSITION_REDIRECT_DOWNLOAD,
|
||||
|
||||
/**
|
||||
* The user followed a link and got a visit in a frame.
|
||||
*/
|
||||
TRANSITION_FRAMED_LINK: Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK,
|
||||
});
|
||||
|
|
@ -44,6 +44,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
|
|||
"resource://gre/modules/Deprecated.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Bookmarks",
|
||||
"resource://gre/modules/Bookmarks.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "History",
|
||||
"resource://gre/modules/History.jsm");
|
||||
|
||||
// The minimum amount of transactions before starting a batch. Usually we do
|
||||
// do incremental updates, a batch will cause views to completely
|
||||
|
@ -1831,10 +1833,26 @@ this.PlacesUtils = {
|
|||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(PlacesUtils, "history", function() {
|
||||
return Cc["@mozilla.org/browser/nav-history-service;1"]
|
||||
.getService(Ci.nsINavHistoryService)
|
||||
.QueryInterface(Ci.nsIBrowserHistory)
|
||||
.QueryInterface(Ci.nsPIPlacesDatabase);
|
||||
let hs = Cc["@mozilla.org/browser/nav-history-service;1"]
|
||||
.getService(Ci.nsINavHistoryService)
|
||||
.QueryInterface(Ci.nsIBrowserHistory)
|
||||
.QueryInterface(Ci.nsPIPlacesDatabase);
|
||||
return Object.freeze(new Proxy(hs, {
|
||||
get: function(target, name) {
|
||||
let property, object;
|
||||
if (name in target) {
|
||||
property = target[name];
|
||||
object = target;
|
||||
} else {
|
||||
property = History[name];
|
||||
object = History;
|
||||
}
|
||||
if (typeof property == "function") {
|
||||
return property.bind(object);
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "asyncHistory",
|
||||
|
|
|
@ -66,6 +66,7 @@ if CONFIG['MOZ_PLACES']:
|
|||
'ClusterLib.js',
|
||||
'ColorAnalyzer_worker.js',
|
||||
'ColorConversion.js',
|
||||
'History.jsm',
|
||||
'PlacesBackups.jsm',
|
||||
'PlacesDBUtils.jsm',
|
||||
'PlacesSearchAutocompleteProvider.jsm',
|
||||
|
|
|
@ -152,8 +152,9 @@ XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () {
|
|||
|
||||
function getOriginActivationType(origin) {
|
||||
// if this is an about uri, treat it as a directory
|
||||
let originUri = Services.io.newURI(origin, null, null);
|
||||
if (originUri.scheme == "moz-safe-about") {
|
||||
let URI = Services.io.newURI(origin, null, null);
|
||||
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(URI);
|
||||
if (Services.scriptSecurityManager.isSystemPrincipal(principal) || origin == "moz-safe-about:home") {
|
||||
return "internal";
|
||||
}
|
||||
|
||||
|
@ -585,26 +586,24 @@ this.SocialService = {
|
|||
action, [], options);
|
||||
},
|
||||
|
||||
installProvider: function(aDOMDocument, data, installCallback, aBypassUserEnable=false) {
|
||||
installProvider: function(aDOMDocument, data, installCallback, options={}) {
|
||||
let manifest;
|
||||
let installOrigin = aDOMDocument.nodePrincipal.origin;
|
||||
|
||||
if (data) {
|
||||
let installType = getOriginActivationType(installOrigin);
|
||||
// if we get data, we MUST have a valid manifest generated from the data
|
||||
manifest = this._manifestFromData(installType, data, aDOMDocument.nodePrincipal);
|
||||
if (!manifest)
|
||||
throw new Error("SocialService.installProvider: service configuration is invalid from " + aDOMDocument.location.href);
|
||||
let installType = getOriginActivationType(installOrigin);
|
||||
// if we get data, we MUST have a valid manifest generated from the data
|
||||
manifest = this._manifestFromData(installType, data, aDOMDocument.nodePrincipal);
|
||||
if (!manifest)
|
||||
throw new Error("SocialService.installProvider: service configuration is invalid from " + aDOMDocument.location.href);
|
||||
|
||||
let addon = new AddonWrapper(manifest);
|
||||
if (addon && addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
|
||||
throw new Error("installProvider: provider with origin [" +
|
||||
installOrigin + "] is blocklisted");
|
||||
// manifestFromData call above will enforce correct origin. To support
|
||||
// activation from about: uris, we need to be sure to use the updated
|
||||
// origin on the manifest.
|
||||
installOrigin = manifest.origin;
|
||||
}
|
||||
let addon = new AddonWrapper(manifest);
|
||||
if (addon && addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
|
||||
throw new Error("installProvider: provider with origin [" +
|
||||
installOrigin + "] is blocklisted");
|
||||
// manifestFromData call above will enforce correct origin. To support
|
||||
// activation from about: uris, we need to be sure to use the updated
|
||||
// origin on the manifest.
|
||||
installOrigin = manifest.origin;
|
||||
|
||||
let id = getAddonIDFromOrigin(installOrigin);
|
||||
AddonManager.getAddonByID(id, function(aAddon) {
|
||||
|
@ -613,7 +612,7 @@ this.SocialService = {
|
|||
aAddon.userDisabled = false;
|
||||
}
|
||||
schedule(function () {
|
||||
this._installProvider(aDOMDocument, manifest, aBypassUserEnable, aManifest => {
|
||||
this._installProvider(aDOMDocument, manifest, options, aManifest => {
|
||||
this._notifyProviderListeners("provider-installed", aManifest.origin);
|
||||
installCallback(aManifest);
|
||||
});
|
||||
|
@ -621,43 +620,21 @@ this.SocialService = {
|
|||
}.bind(this));
|
||||
},
|
||||
|
||||
_installProvider: function(aDOMDocument, manifest, aBypassUserEnable, installCallback) {
|
||||
let sourceURI = aDOMDocument.location.href;
|
||||
let installOrigin = aDOMDocument.nodePrincipal.origin;
|
||||
_installProvider: function(aDOMDocument, manifest, options, installCallback) {
|
||||
if (!manifest)
|
||||
throw new Error("Cannot install provider without manifest data");
|
||||
|
||||
let installType = getOriginActivationType(installOrigin);
|
||||
let installer;
|
||||
switch(installType) {
|
||||
case "foreign":
|
||||
if (!Services.prefs.getBoolPref("social.remote-install.enabled"))
|
||||
throw new Error("Remote install of services is disabled");
|
||||
if (!manifest)
|
||||
throw new Error("Cannot install provider without manifest data");
|
||||
let installType = getOriginActivationType(aDOMDocument.nodePrincipal.origin);
|
||||
if (installType == "foreign" && !Services.prefs.getBoolPref("social.remote-install.enabled"))
|
||||
throw new Error("Remote install of services is disabled");
|
||||
|
||||
installer = new AddonInstaller(sourceURI, manifest, installCallback);
|
||||
this._showInstallNotification(aDOMDocument, installer);
|
||||
break;
|
||||
case "internal":
|
||||
// double check here since "builtin" falls through this as well.
|
||||
aBypassUserEnable = installType == "internal" && manifest.oneclick;
|
||||
case "directory":
|
||||
// a manifest is requried, and will have been vetted by reviewers. We
|
||||
// also handle in-product installations without the verification step.
|
||||
if (aBypassUserEnable) {
|
||||
installer = new AddonInstaller(sourceURI, manifest, installCallback);
|
||||
installer.install();
|
||||
return;
|
||||
}
|
||||
// a manifest is required, we'll catch a missing manifest below.
|
||||
if (!manifest)
|
||||
throw new Error("Cannot install provider without manifest data");
|
||||
installer = new AddonInstaller(sourceURI, manifest, installCallback);
|
||||
this._showInstallNotification(aDOMDocument, installer);
|
||||
break;
|
||||
default:
|
||||
throw new Error("SocialService.installProvider: Invalid install type "+installType+"\n");
|
||||
break;
|
||||
}
|
||||
let installer = new AddonInstaller(aDOMDocument.location.href, manifest, installCallback);
|
||||
let bypassPanel = options.bypassInstallPanel ||
|
||||
(installType == "internal" && manifest.oneclick);
|
||||
if (bypassPanel)
|
||||
installer.install();
|
||||
else
|
||||
this._showInstallNotification(aDOMDocument, installer);
|
||||
},
|
||||
|
||||
createWrapper: function(manifest) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче