This commit is contained in:
Wes Kocher 2014-10-09 21:40:05 -07:00
Родитель 0f68e93b1e dbae32d6c1
Коммит ea0415b62f
89 изменённых файлов: 1847 добавлений и 739 удалений

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

@ -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) {