зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
be2a184fb0
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8472f0c736660072799aaae60e4b6001a6aaceb4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4bb17b24620818cbda0ba0c0d69e0ce3f914e1b7"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8472f0c736660072799aaae60e4b6001a6aaceb4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4bb17b24620818cbda0ba0c0d69e0ce3f914e1b7"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -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="8472f0c736660072799aaae60e4b6001a6aaceb4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4bb17b24620818cbda0ba0c0d69e0ce3f914e1b7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8472f0c736660072799aaae60e4b6001a6aaceb4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4bb17b24620818cbda0ba0c0d69e0ce3f914e1b7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8472f0c736660072799aaae60e4b6001a6aaceb4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4bb17b24620818cbda0ba0c0d69e0ce3f914e1b7"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8472f0c736660072799aaae60e4b6001a6aaceb4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4bb17b24620818cbda0ba0c0d69e0ce3f914e1b7"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -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="8472f0c736660072799aaae60e4b6001a6aaceb4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4bb17b24620818cbda0ba0c0d69e0ce3f914e1b7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8472f0c736660072799aaae60e4b6001a6aaceb4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4bb17b24620818cbda0ba0c0d69e0ce3f914e1b7"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"git": {
|
||||
"git_revision": "8472f0c736660072799aaae60e4b6001a6aaceb4",
|
||||
"git_revision": "4bb17b24620818cbda0ba0c0d69e0ce3f914e1b7",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "e4b5ba76d5a4de0cd220310e0fe2ba334f0e250a",
|
||||
"revision": "0bdd0b54cb40d7e928e9e6de720c0506dc7e52db",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8472f0c736660072799aaae60e4b6001a6aaceb4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4bb17b24620818cbda0ba0c0d69e0ce3f914e1b7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8472f0c736660072799aaae60e4b6001a6aaceb4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4bb17b24620818cbda0ba0c0d69e0ce3f914e1b7"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -1367,8 +1367,6 @@ pref("devtools.command-button-rulers.enabled", false);
|
|||
pref("devtools.inspector.enabled", true);
|
||||
// What was the last active sidebar in the inspector
|
||||
pref("devtools.inspector.activeSidebar", "ruleview");
|
||||
// Enable the markup preview
|
||||
pref("devtools.inspector.markupPreview", false);
|
||||
pref("devtools.inspector.remote", false);
|
||||
// Collapse pseudo-elements by default in the rule-view
|
||||
pref("devtools.inspector.show_pseudo_elements", false);
|
||||
|
|
|
@ -72,7 +72,7 @@ function init(aEvent)
|
|||
let defaults = Services.prefs.getDefaultBranch("");
|
||||
let channelLabel = document.getElementById("currentChannel");
|
||||
let currentChannelText = document.getElementById("currentChannelText");
|
||||
channelLabel.value = UpdateChannel.get();
|
||||
channelLabel.value = UpdateUtils.UpdateChannel;
|
||||
if (/^release($|\-)/.test(channelLabel.value))
|
||||
currentChannelText.hidden = true;
|
||||
#endif
|
||||
|
@ -89,8 +89,8 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
var gAppUpdater;
|
||||
|
||||
|
|
|
@ -167,5 +167,17 @@ var FeedHandler = {
|
|||
clearTimeout(this._updateFeedTimeout);
|
||||
this._updateFeedTimeout = setTimeout(this.updateFeeds.bind(this), 100);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
init() {
|
||||
window.messageManager.addMessageListener("FeedWriter:ShownFirstRun", this);
|
||||
},
|
||||
|
||||
receiveMessage(msg) {
|
||||
switch (msg.name) {
|
||||
case "FeedWriter:ShownFirstRun":
|
||||
Services.prefs.setBoolPref("browser.feeds.showFirstRunUI", false);
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -42,8 +42,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Log",
|
|||
"resource://gre/modules/Log.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "Favicons",
|
||||
"@mozilla.org/browser/favicon-service;1",
|
||||
"mozIAsyncFavicons");
|
||||
|
@ -965,6 +965,7 @@ var gBrowserInit = {
|
|||
gPageStyleMenu.init();
|
||||
LanguageDetectionListener.init();
|
||||
BrowserOnClick.init();
|
||||
FeedHandler.init();
|
||||
DevEdition.init();
|
||||
AboutPrivateBrowsingListener.init();
|
||||
TrackingProtection.init();
|
||||
|
@ -2918,7 +2919,7 @@ var BrowserOnClick = {
|
|||
version: 1,
|
||||
build: gAppInfo.appBuildID,
|
||||
product: gAppInfo.name,
|
||||
channel: UpdateChannel.get()
|
||||
channel: UpdateUtils.UpdateChannel
|
||||
}
|
||||
|
||||
let reportURL = Services.prefs.getCharPref("security.ssl.errorReporting.url");
|
||||
|
|
|
@ -18,8 +18,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "Rect",
|
|||
"resource://gre/modules/Geometry.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
var {
|
||||
links: gLinks,
|
||||
|
|
|
@ -60,7 +60,7 @@ add_task(function*() {
|
|||
|
||||
let updateChannel = null;
|
||||
try {
|
||||
updateChannel = Cu.import("resource://gre/modules/UpdateChannel.jsm", {}).UpdateChannel.get();
|
||||
updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
|
||||
} catch (ex) {}
|
||||
if (!updateChannel) {
|
||||
Assert.ok(!('updateChannel' in got.message.application),
|
||||
|
|
|
@ -9,6 +9,7 @@ const Cr = Components.results;
|
|||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const FEEDWRITER_CID = Components.ID("{49bb6593-3aff-4eb3-a068-2712c28bd58e}");
|
||||
const FEEDWRITER_CONTRACTID = "@mozilla.org/browser/feeds/result-writer;1";
|
||||
|
@ -713,9 +714,7 @@ FeedWriter.prototype = {
|
|||
if (checkbox) {
|
||||
var alwaysUse = false;
|
||||
try {
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
if (prefs.getCharPref(getPrefActionForType(feedType)) != "ask")
|
||||
if (Services.prefs.getCharPref(getPrefActionForType(feedType)) != "ask")
|
||||
alwaysUse = true;
|
||||
}
|
||||
catch(ex) { }
|
||||
|
@ -803,10 +802,7 @@ FeedWriter.prototype = {
|
|||
},
|
||||
|
||||
_setSelectedHandler: function FW__setSelectedHandler(feedType) {
|
||||
var prefs =
|
||||
Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
|
||||
var prefs = Services.prefs;
|
||||
var handler = "bookmarks";
|
||||
try {
|
||||
handler = prefs.getCharPref(getPrefReaderForType(feedType));
|
||||
|
@ -899,10 +895,8 @@ FeedWriter.prototype = {
|
|||
menuItem.className = "menuitem-iconic selectedAppMenuItem";
|
||||
menuItem.setAttribute("handlerType", "client");
|
||||
try {
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
this._selectedApp = prefs.getComplexValue(getPrefAppForType(feedType),
|
||||
Ci.nsILocalFile);
|
||||
this._selectedApp = Services.prefs.getComplexValue(getPrefAppForType(feedType),
|
||||
Ci.nsILocalFile);
|
||||
|
||||
if (this._selectedApp.exists())
|
||||
this._initMenuItemWithFile(menuItem, this._selectedApp);
|
||||
|
@ -958,25 +952,29 @@ FeedWriter.prototype = {
|
|||
var chooseAppSep = liveBookmarksMenuItem.nextSibling.cloneNode(false);
|
||||
handlersMenuPopup.appendChild(chooseAppSep);
|
||||
|
||||
// List of web handlers
|
||||
var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
|
||||
getService(Ci.nsIWebContentConverterService);
|
||||
var handlers = wccr.getContentHandlers(this._getMimeTypeForFeedType(feedType));
|
||||
if (handlers.length != 0) {
|
||||
for (var i = 0; i < handlers.length; ++i) {
|
||||
if (!handlers[i].uri) {
|
||||
LOG("Handler with name " + handlers[i].name + " has no URI!? Skipping...");
|
||||
continue;
|
||||
}
|
||||
menuItem = liveBookmarksMenuItem.cloneNode(false);
|
||||
menuItem.removeAttribute("selected");
|
||||
menuItem.className = "menuitem-iconic";
|
||||
menuItem.setAttribute("label", handlers[i].name);
|
||||
menuItem.setAttribute("handlerType", "web");
|
||||
menuItem.setAttribute("webhandlerurl", handlers[i].uri);
|
||||
handlersMenuPopup.appendChild(menuItem);
|
||||
// FIXME: getting a list of webhandlers doesn't work in the content process
|
||||
// right now, see bug 1109714.
|
||||
if (Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_CONTENT) {
|
||||
// List of web handlers
|
||||
var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
|
||||
getService(Ci.nsIWebContentConverterService);
|
||||
var handlers = wccr.getContentHandlers(this._getMimeTypeForFeedType(feedType));
|
||||
if (handlers.length != 0) {
|
||||
for (var i = 0; i < handlers.length; ++i) {
|
||||
if (!handlers[i].uri) {
|
||||
LOG("Handler with name " + handlers[i].name + " has no URI!? Skipping...");
|
||||
continue;
|
||||
}
|
||||
menuItem = liveBookmarksMenuItem.cloneNode(false);
|
||||
menuItem.removeAttribute("selected");
|
||||
menuItem.className = "menuitem-iconic";
|
||||
menuItem.setAttribute("label", handlers[i].name);
|
||||
menuItem.setAttribute("handlerType", "web");
|
||||
menuItem.setAttribute("webhandlerurl", handlers[i].uri);
|
||||
handlersMenuPopup.appendChild(menuItem);
|
||||
|
||||
this._setFaviconForWebReader(handlers[i].uri, menuItem);
|
||||
this._setFaviconForWebReader(handlers[i].uri, menuItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1000,7 +998,7 @@ FeedWriter.prototype = {
|
|||
// first-run ui
|
||||
var showFirstRunUI = true;
|
||||
try {
|
||||
showFirstRunUI = prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI);
|
||||
showFirstRunUI = Services.prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI);
|
||||
}
|
||||
catch (ex) { }
|
||||
if (showFirstRunUI) {
|
||||
|
@ -1029,7 +1027,7 @@ FeedWriter.prototype = {
|
|||
|
||||
header.setAttribute('firstrun', 'true');
|
||||
|
||||
prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false);
|
||||
this._mm.sendAsyncMessage("FeedWriter:ShownFirstRun");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1090,8 +1088,7 @@ FeedWriter.prototype = {
|
|||
|
||||
// Set up the subscription UI
|
||||
this._initSubscriptionUI();
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
let prefs = Services.prefs;
|
||||
prefs.addObserver(PREF_SELECTED_ACTION, this, false);
|
||||
prefs.addObserver(PREF_SELECTED_READER, this, false);
|
||||
prefs.addObserver(PREF_SELECTED_WEB, this, false);
|
||||
|
@ -1133,8 +1130,7 @@ FeedWriter.prototype = {
|
|||
.removeEventListener("command", this, false);
|
||||
this._document = null;
|
||||
this._window = null;
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
let prefs = Services.prefs;
|
||||
prefs.removeObserver(PREF_SELECTED_ACTION, this);
|
||||
prefs.removeObserver(PREF_SELECTED_READER, this);
|
||||
prefs.removeObserver(PREF_SELECTED_WEB, this);
|
||||
|
@ -1172,8 +1168,7 @@ FeedWriter.prototype = {
|
|||
var feedType = this._getFeedType();
|
||||
|
||||
// Subscribe to the feed using the selected handler and save prefs
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
var prefs = Services.prefs;
|
||||
var defaultHandler = "reader";
|
||||
var useAsDefault = this._getUIElement("alwaysUse").getAttribute("checked");
|
||||
|
||||
|
@ -1320,6 +1315,15 @@ FeedWriter.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
get _mm() {
|
||||
let mm = this._window.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDocShell).
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIContentFrameMessageManager);
|
||||
delete this._mm;
|
||||
return this._mm = mm;
|
||||
},
|
||||
|
||||
classID: FEEDWRITER_CID,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener, Ci.nsIObserver,
|
||||
Ci.nsINavHistoryObserver,
|
||||
|
|
|
@ -10,9 +10,6 @@ support-files =
|
|||
valid-unsniffable-feed.xml
|
||||
|
||||
[test_bug436801.html]
|
||||
skip-if = e10s
|
||||
[test_bug494328.html]
|
||||
skip-if = e10s
|
||||
[test_bug589543.html]
|
||||
skip-if = e10s
|
||||
[test_registerHandler.html]
|
||||
|
|
|
@ -25,8 +25,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
|
|||
"resource://gre/modules/PageMetadata.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
||||
"resource://gre/modules/PluralForm.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
|
||||
"resource:///modules/UITour.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Social",
|
||||
|
@ -810,7 +810,7 @@ function injectLoopAPI(targetWindow) {
|
|||
// which doesn't have what we need, so log an error.
|
||||
try {
|
||||
appVersionInfo = Cu.cloneInto({
|
||||
channel: UpdateChannel.get(),
|
||||
channel: UpdateUtils.UpdateChannel,
|
||||
version: appInfo.version,
|
||||
OS: appInfo.OS
|
||||
}, targetWindow);
|
||||
|
|
|
@ -124,8 +124,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
|
|||
"resource:///modules/ContentSearch.jsm");
|
||||
|
||||
#ifdef E10S_TESTING_ONLY
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
|
@ -2977,7 +2977,7 @@ var E10SUINotification = {
|
|||
checkStatus: function() {
|
||||
let skipE10sChecks = false;
|
||||
try {
|
||||
let updateChannel = UpdateChannel.get();
|
||||
let updateChannel = UpdateUtils.UpdateChannel;
|
||||
let channelAuthorized = updateChannel == "nightly" || updateChannel == "aurora";
|
||||
|
||||
skipE10sChecks = !channelAuthorized ||
|
||||
|
|
|
@ -165,8 +165,8 @@ var gMainPane = {
|
|||
}
|
||||
|
||||
let tmp = {};
|
||||
Components.utils.import("resource://gre/modules/UpdateChannel.jsm", tmp);
|
||||
if (!e10sCheckbox.checked && tmp.UpdateChannel.get() != "default") {
|
||||
Components.utils.import("resource://gre/modules/UpdateUtils.jsm", tmp);
|
||||
if (!e10sCheckbox.checked && tmp.UpdateUtils.UpdateChannel != "default") {
|
||||
Services.prefs.setBoolPref("browser.requestE10sFeedback", true);
|
||||
Services.prompt.alert(window, brandName, bundle.getString("e10sFeedbackAfterRestart"));
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@ Cu.import("resource://gre/modules/Log.jsm");
|
|||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
|
||||
|
@ -275,7 +275,7 @@ Experiments.Policy.prototype = {
|
|||
},
|
||||
|
||||
updatechannel: function () {
|
||||
return UpdateChannel.get();
|
||||
return UpdateUtils.UpdateChannel;
|
||||
},
|
||||
|
||||
locale: function () {
|
||||
|
|
|
@ -26,8 +26,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
|||
"resource://gre/modules/osfile.jsm")
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "eTLD",
|
||||
"@mozilla.org/network/effective-tld-service;1",
|
||||
"nsIEffectiveTLDService");
|
||||
|
@ -280,7 +280,7 @@ var DirectoryLinksProvider = {
|
|||
_fetchAndCacheLinks: function DirectoryLinksProvider_fetchAndCacheLinks(uri) {
|
||||
// Replace with the same display locale used for selecting links data
|
||||
uri = uri.replace("%LOCALE%", this.locale);
|
||||
uri = uri.replace("%CHANNEL%", UpdateChannel.get());
|
||||
uri = uri.replace("%CHANNEL%", UpdateUtils.UpdateChannel);
|
||||
|
||||
return this._downloadJsonData(uri).then(json => {
|
||||
return OS.File.writeAtomic(this._directoryFilePath, json, {tmpPath: this._directoryFilePath + ".tmp"});
|
||||
|
|
|
@ -98,6 +98,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
@media not all and (-moz-windows-default-theme) {
|
||||
#main-window {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
#titlebar-buttonbox,
|
||||
.titlebar-button {
|
||||
-moz-appearance: none !important;
|
||||
|
|
|
@ -349,18 +349,13 @@
|
|||
background-image: linear-gradient(@toolbarHighlight@, transparent);
|
||||
}
|
||||
|
||||
@media (-moz-os-version: windows-xp),
|
||||
(-moz-os-version: windows-vista),
|
||||
(-moz-os-version: windows-win7),
|
||||
(-moz-os-version: windows-win8) {
|
||||
#nav-bar {
|
||||
border-top: 1px solid @toolbarShadowColor@ !important;
|
||||
box-shadow: 0 1px 0 @toolbarHighlight@ inset;
|
||||
}
|
||||
@media not all and (-moz-windows-compositor) {
|
||||
#TabsToolbar[collapsed="true"] + #nav-bar {
|
||||
border-top-style: none !important;
|
||||
}
|
||||
#nav-bar {
|
||||
border-top: 1px solid @toolbarShadowColor@ !important;
|
||||
box-shadow: 0 1px 0 @toolbarHighlight@ inset;
|
||||
}
|
||||
@media not all and (-moz-windows-compositor) {
|
||||
#TabsToolbar[collapsed="true"] + #nav-bar {
|
||||
border-top-style: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2005,17 +2000,24 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
|||
@media not all and (-moz-os-version: windows-vista) {
|
||||
@media not all and (-moz-os-version: windows-win7) {
|
||||
@media not all and (-moz-os-version: windows-win8) {
|
||||
.tab-background-end[visuallyselected=true]::after,
|
||||
.tab-background-start[visuallyselected=true]::after {
|
||||
content: none;
|
||||
}
|
||||
@media (-moz-windows-default-theme) {
|
||||
.tab-background-end[visuallyselected=true]::after,
|
||||
.tab-background-start[visuallyselected=true]::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
#TabsToolbar {
|
||||
--tab-stroke-background-size: 0 0;
|
||||
}
|
||||
#TabsToolbar {
|
||||
--tab-stroke-background-size: 0 0;
|
||||
}
|
||||
|
||||
:root {
|
||||
--tab-toolbar-navbar-overlap: 0;
|
||||
:root {
|
||||
--tab-toolbar-navbar-overlap: 0;
|
||||
}
|
||||
|
||||
#nav-bar {
|
||||
border-top-style: none !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -223,7 +223,9 @@ menu.subviewbutton > .menu-right:-moz-locale-dir(rtl) {
|
|||
}
|
||||
|
||||
#BMB_bookmarksPopup menupopup[placespopup=true] > hbox {
|
||||
box-shadow: none;
|
||||
/* After fixing of bug 1194480 the box-shadow can be removed again */
|
||||
/* box-shadow: none; */
|
||||
box-shadow: 0 0 4px rgba(0,0,0,0.02);
|
||||
background: -moz-field;
|
||||
border: 1px solid ThreeDShadow;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ const {Cc, Cu, Ci} = require("chrome");
|
|||
|
||||
// Page size for pageup/pagedown
|
||||
const PAGE_SIZE = 10;
|
||||
const PREVIEW_AREA = 700;
|
||||
const DEFAULT_MAX_CHILDREN = 100;
|
||||
const COLLAPSE_ATTRIBUTE_LENGTH = 120;
|
||||
const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
|
||||
|
@ -122,7 +121,6 @@ function MarkupView(aInspector, aFrame, aControllerWindow) {
|
|||
|
||||
this._makeTooltipPersistent = this._makeTooltipPersistent.bind(this);
|
||||
|
||||
this._initPreview();
|
||||
this._initTooltips();
|
||||
this._initHighlighter();
|
||||
|
||||
|
@ -647,6 +645,11 @@ MarkupView.prototype = {
|
|||
this.beginEditingOuterHTML(this._selectedContainer.node);
|
||||
break;
|
||||
}
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_S: {
|
||||
let selection = this._selectedContainer.node;
|
||||
this._inspector.scrollNodeIntoView(selection);
|
||||
break;
|
||||
}
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE: {
|
||||
if (this.isDragging) {
|
||||
this.cancelDragging();
|
||||
|
@ -1571,92 +1574,6 @@ MarkupView.prototype = {
|
|||
return this._destroyer;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the preview panel.
|
||||
*/
|
||||
_initPreview: function() {
|
||||
this._previewEnabled = Services.prefs.getBoolPref("devtools.inspector.markupPreview");
|
||||
if (!this._previewEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._previewBar = this.doc.querySelector("#previewbar");
|
||||
this._preview = this.doc.querySelector("#preview");
|
||||
this._viewbox = this.doc.querySelector("#viewbox");
|
||||
|
||||
this._previewBar.classList.remove("disabled");
|
||||
|
||||
this._previewWidth = this._preview.getBoundingClientRect().width;
|
||||
|
||||
this._boundResizePreview = this._resizePreview.bind(this);
|
||||
this._frame.contentWindow.addEventListener("resize",
|
||||
this._boundResizePreview, true);
|
||||
this._frame.contentWindow.addEventListener("overflow",
|
||||
this._boundResizePreview, true);
|
||||
this._frame.contentWindow.addEventListener("underflow",
|
||||
this._boundResizePreview, true);
|
||||
|
||||
this._boundUpdatePreview = this._updatePreview.bind(this);
|
||||
this._frame.contentWindow.addEventListener("scroll",
|
||||
this._boundUpdatePreview, true);
|
||||
this._updatePreview();
|
||||
},
|
||||
|
||||
/**
|
||||
* Move the preview viewbox.
|
||||
*/
|
||||
_updatePreview: function() {
|
||||
if (!this._previewEnabled) {
|
||||
return;
|
||||
}
|
||||
let win = this._frame.contentWindow;
|
||||
|
||||
if (win.scrollMaxY == 0) {
|
||||
this._previewBar.classList.add("disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
this._previewBar.classList.remove("disabled");
|
||||
|
||||
let ratio = this._previewWidth / PREVIEW_AREA;
|
||||
let width = ratio * win.innerWidth;
|
||||
|
||||
let height = ratio * (win.scrollMaxY + win.innerHeight);
|
||||
let scrollTo
|
||||
if (height >= win.innerHeight) {
|
||||
scrollTo = -(height - win.innerHeight) * (win.scrollY / win.scrollMaxY);
|
||||
this._previewBar.setAttribute("style", "height:" + height +
|
||||
"px;transform:translateY(" + scrollTo + "px)");
|
||||
} else {
|
||||
this._previewBar.setAttribute("style", "height:100%");
|
||||
}
|
||||
|
||||
let bgSize = ~~width + "px " + ~~height + "px";
|
||||
this._preview.setAttribute("style", "background-size:" + bgSize);
|
||||
|
||||
height = ~~(win.innerHeight * ratio) + "px";
|
||||
let top = ~~(win.scrollY * ratio) + "px";
|
||||
this._viewbox.setAttribute("style", "height:" + height +
|
||||
";transform: translateY(" + top + ")");
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the preview while resizing, to avoid slowness.
|
||||
*/
|
||||
_resizePreview: function() {
|
||||
if (!this._previewEnabled) {
|
||||
return;
|
||||
}
|
||||
let win = this._frame.contentWindow;
|
||||
this._previewBar.classList.add("hide");
|
||||
clearTimeout(this._resizePreviewTimeout);
|
||||
|
||||
setTimeout(() => {
|
||||
this._updatePreview();
|
||||
this._previewBar.classList.remove("hide");
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an element as it's only argument and marks the element
|
||||
* as the drop target
|
||||
|
|
|
@ -97,9 +97,5 @@
|
|||
--></span>
|
||||
|
||||
</div>
|
||||
<div id="previewbar" class="disabled">
|
||||
<div id="preview"/>
|
||||
<div id="viewbox"/>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -190,6 +190,15 @@ StyleSheetEditor.prototype = {
|
|||
return this._friendlyName;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if transitions are enabled for style changes.
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
get transitionsEnabled() {
|
||||
return Services.prefs.getBoolPref(TRANSITION_PREF);
|
||||
},
|
||||
|
||||
/**
|
||||
* If this is an original source, get the path of the CSS file it generated.
|
||||
*/
|
||||
|
@ -495,9 +504,7 @@ StyleSheetEditor.prototype = {
|
|||
this._state.text = this.sourceEditor.getText();
|
||||
}
|
||||
|
||||
let transitionsEnabled = Services.prefs.getBoolPref(TRANSITION_PREF);
|
||||
|
||||
this.styleSheet.update(this._state.text, transitionsEnabled)
|
||||
this.styleSheet.update(this._state.text, this.transitionsEnabled)
|
||||
.then(null, Cu.reportError);
|
||||
},
|
||||
|
||||
|
@ -683,7 +690,7 @@ StyleSheetEditor.prototype = {
|
|||
let text = decoder.decode(array);
|
||||
|
||||
let relatedSheet = this.styleSheet.relatedStyleSheet;
|
||||
relatedSheet.update(text, true);
|
||||
relatedSheet.update(text, this.transitionsEnabled);
|
||||
}, this.markLinkedFileBroken);
|
||||
},
|
||||
|
||||
|
|
|
@ -52,49 +52,6 @@
|
|||
padding-left: 2px;
|
||||
}
|
||||
|
||||
/* Preview */
|
||||
|
||||
#previewbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 90px;
|
||||
background: black;
|
||||
border-left: 1px solid;
|
||||
border-bottom: 1px solid;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#previewbar {
|
||||
background: var(--theme-tab-toolbar-background);
|
||||
border-color: var(--theme-splitter-color);
|
||||
}
|
||||
|
||||
#preview {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 5px;
|
||||
width: 80px;
|
||||
height: 100%;
|
||||
background-image: -moz-element(#root);
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#previewbar.hide,
|
||||
#previewbar.disabled {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#viewbox {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 5px;
|
||||
width: 80px;
|
||||
border: 1px dashed #888;
|
||||
background: rgba(205,205,255,0.2);
|
||||
outline: 1px solid transparent;
|
||||
}
|
||||
|
||||
/* Events */
|
||||
.markupview-events {
|
||||
font-size: 8px;
|
||||
|
|
|
@ -80,8 +80,8 @@ nsresult
|
|||
AccessibleCaretManager::OnSelectionChanged(nsIDOMDocument* aDoc,
|
||||
nsISelection* aSel, int16_t aReason)
|
||||
{
|
||||
AC_LOG("aSel: %p, GetSelection(): %p, aReason: %d", aSel, GetSelection(),
|
||||
aReason);
|
||||
AC_LOG("%s: aSel: %p, GetSelection(): %p, aReason: %d", __FUNCTION__,
|
||||
aSel, GetSelection(), aReason);
|
||||
|
||||
if (aSel != GetSelection()) {
|
||||
return NS_OK;
|
||||
|
@ -99,6 +99,13 @@ AccessibleCaretManager::OnSelectionChanged(nsIDOMDocument* aDoc,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// OnBlur() might be called between mouse down and mouse up, so we hide carets
|
||||
// upon mouse down anyway, and update carets upon mouse up.
|
||||
if (aReason & nsISelectionListener::MOUSEDOWN_REASON) {
|
||||
HideCarets();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Range will collapse after cutting or copying text.
|
||||
if (aReason & (nsISelectionListener::COLLAPSETOSTART_REASON |
|
||||
nsISelectionListener::COLLAPSETOEND_REASON)) {
|
||||
|
@ -472,8 +479,10 @@ AccessibleCaretManager::OnScrollPositionChanged()
|
|||
return;
|
||||
}
|
||||
|
||||
AC_LOG("%s: UpdateCarets(RespectOldAppearance)", __FUNCTION__);
|
||||
UpdateCarets(UpdateCaretsHint::RespectOldAppearance);
|
||||
if (mFirstCaret->IsLogicallyVisible() || mSecondCaret->IsLogicallyVisible()) {
|
||||
AC_LOG("%s: UpdateCarets(RespectOldAppearance)", __FUNCTION__);
|
||||
UpdateCarets(UpdateCaretsHint::RespectOldAppearance);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -483,9 +492,9 @@ AccessibleCaretManager::OnReflow()
|
|||
return;
|
||||
}
|
||||
|
||||
if (mFirstCaret->IsVisuallyVisible() || mSecondCaret->IsVisuallyVisible()) {
|
||||
AC_LOG("%s: UpdateCarets()", __FUNCTION__);
|
||||
UpdateCarets();
|
||||
if (mFirstCaret->IsLogicallyVisible() || mSecondCaret->IsLogicallyVisible()) {
|
||||
AC_LOG("%s: UpdateCarets(RespectOldAppearance)", __FUNCTION__);
|
||||
UpdateCarets(UpdateCaretsHint::RespectOldAppearance);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,11 +65,6 @@ public:
|
|||
mSecondCaret = MakeUnique<MockAccessibleCaret>();
|
||||
}
|
||||
|
||||
CaretMode LastUpdateCaretMode() const
|
||||
{
|
||||
return mLastUpdateCaretMode;
|
||||
}
|
||||
|
||||
MockAccessibleCaret& FirstCaret()
|
||||
{
|
||||
return static_cast<MockAccessibleCaret&>(*mFirstCaret);
|
||||
|
@ -117,13 +112,14 @@ public:
|
|||
.WillRepeatedly(Return(PositionChangedResult::Changed));
|
||||
}
|
||||
|
||||
void CheckStates(CaretMode aCaretMode,
|
||||
Appearance aFirstCaretAppearance,
|
||||
Appearance aSecondCaretAppearance)
|
||||
AccessibleCaret::Appearance FirstCaretAppearance()
|
||||
{
|
||||
EXPECT_EQ(mManager.LastUpdateCaretMode(), aCaretMode);
|
||||
EXPECT_EQ(mManager.FirstCaret().GetAppearance(), aFirstCaretAppearance);
|
||||
EXPECT_EQ(mManager.SecondCaret().GetAppearance(), aSecondCaretAppearance);
|
||||
return mManager.FirstCaret().GetAppearance();
|
||||
}
|
||||
|
||||
AccessibleCaret::Appearance SecondCaretAppearance()
|
||||
{
|
||||
return mManager.SecondCaret().GetAppearance();
|
||||
}
|
||||
|
||||
// Member variables
|
||||
|
@ -140,16 +136,19 @@ TEST_F(AccessibleCaretManagerTester, TestUpdatesInSelectionMode)
|
|||
CaretChangedReason::Updateposition)).Times(3);
|
||||
|
||||
mManager.UpdateCarets();
|
||||
CheckStates(CaretMode::Selection, Appearance::Normal, Appearance::Normal);
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
|
||||
EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
|
||||
|
||||
mManager.OnReflow();
|
||||
CheckStates(CaretMode::Selection, Appearance::Normal, Appearance::Normal);
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
|
||||
EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
|
||||
|
||||
mManager.OnScrollPositionChanged();
|
||||
CheckStates(CaretMode::Selection, Appearance::Normal, Appearance::Normal);
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
|
||||
EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
|
||||
}
|
||||
|
||||
TEST_F(AccessibleCaretManagerTester, TestUpdatesInCursorModeOnNonEmptyContent)
|
||||
TEST_F(AccessibleCaretManagerTester, TestSingleTapOnNonEmptyInput)
|
||||
{
|
||||
EXPECT_CALL(mManager, GetCaretMode())
|
||||
.WillRepeatedly(Return(CaretMode::Cursor));
|
||||
|
@ -163,46 +162,172 @@ TEST_F(AccessibleCaretManagerTester, TestUpdatesInCursorModeOnNonEmptyContent)
|
|||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Updateposition)).Times(1);
|
||||
EXPECT_CALL(check, Call("mouse down"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Updateposition)).Times(1);
|
||||
EXPECT_CALL(check, Call("reflow"));
|
||||
EXPECT_CALL(check, Call("update"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Visibilitychange)).Times(1);
|
||||
EXPECT_CALL(check, Call("mouse down"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
|
||||
EXPECT_CALL(check, Call("reflow"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
|
||||
EXPECT_CALL(check, Call("blur"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Updateposition)).Times(1);
|
||||
EXPECT_CALL(check, Call("mouse up"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Updateposition)).Times(1);
|
||||
EXPECT_CALL(check, Call("reflow2"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Updateposition)).Times(1);
|
||||
}
|
||||
|
||||
// Simulate a single tap on a non-empty input.
|
||||
mManager.UpdateCarets();
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
|
||||
check.Call("update");
|
||||
|
||||
mManager.OnSelectionChanged(nullptr, nullptr,
|
||||
nsISelectionListener::DRAG_REASON |
|
||||
nsISelectionListener::MOUSEDOWN_REASON);
|
||||
CheckStates(CaretMode::Cursor, Appearance::Normal, Appearance::None);
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
|
||||
check.Call("mouse down");
|
||||
|
||||
mManager.OnReflow();
|
||||
CheckStates(CaretMode::Cursor, Appearance::Normal, Appearance::None);
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
|
||||
check.Call("reflow");
|
||||
|
||||
mManager.OnBlur();
|
||||
CheckStates(CaretMode::Cursor, Appearance::None, Appearance::None);
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
|
||||
check.Call("blur");
|
||||
|
||||
mManager.OnSelectionChanged(nullptr, nullptr,
|
||||
nsISelectionListener::MOUSEUP_REASON);
|
||||
CheckStates(CaretMode::Cursor, Appearance::Normal, Appearance::None);
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
|
||||
check.Call("mouse up");
|
||||
|
||||
mManager.OnReflow();
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
|
||||
check.Call("reflow2");
|
||||
|
||||
mManager.OnScrollPositionChanged();
|
||||
CheckStates(CaretMode::Cursor, Appearance::Normal, Appearance::None);
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
|
||||
}
|
||||
|
||||
TEST_F(AccessibleCaretManagerTester, TestSingleTapOnEmptyInput)
|
||||
{
|
||||
EXPECT_CALL(mManager, GetCaretMode())
|
||||
.WillRepeatedly(Return(CaretMode::Cursor));
|
||||
|
||||
EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
MockFunction<void(std::string aCheckPointName)> check;
|
||||
{
|
||||
InSequence dummy;
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Updateposition)).Times(1);
|
||||
EXPECT_CALL(check, Call("update"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Visibilitychange)).Times(1);
|
||||
EXPECT_CALL(check, Call("mouse down"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
|
||||
EXPECT_CALL(check, Call("reflow"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
|
||||
EXPECT_CALL(check, Call("blur"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Updateposition)).Times(1);
|
||||
EXPECT_CALL(check, Call("mouse up"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Updateposition)).Times(1);
|
||||
EXPECT_CALL(check, Call("reflow2"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Updateposition)).Times(1);
|
||||
}
|
||||
|
||||
// Simulate a single tap on an empty input.
|
||||
mManager.UpdateCarets();
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
|
||||
check.Call("update");
|
||||
|
||||
mManager.OnSelectionChanged(nullptr, nullptr,
|
||||
nsISelectionListener::DRAG_REASON |
|
||||
nsISelectionListener::MOUSEDOWN_REASON);
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
|
||||
check.Call("mouse down");
|
||||
|
||||
mManager.OnReflow();
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
|
||||
check.Call("reflow");
|
||||
|
||||
mManager.OnBlur();
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
|
||||
check.Call("blur");
|
||||
|
||||
mManager.OnSelectionChanged(nullptr, nullptr,
|
||||
nsISelectionListener::MOUSEUP_REASON);
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
|
||||
check.Call("mouse up");
|
||||
|
||||
mManager.OnReflow();
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
|
||||
check.Call("reflow2");
|
||||
|
||||
mManager.OnScrollPositionChanged();
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
|
||||
}
|
||||
|
||||
TEST_F(AccessibleCaretManagerTester, TestTypingAtEndOfInput)
|
||||
{
|
||||
EXPECT_CALL(mManager, GetCaretMode())
|
||||
.WillRepeatedly(Return(CaretMode::Cursor));
|
||||
|
||||
EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
|
||||
.WillRepeatedly(Return(true));
|
||||
|
||||
MockFunction<void(std::string aCheckPointName)> check;
|
||||
{
|
||||
InSequence dummy;
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Updateposition)).Times(1);
|
||||
EXPECT_CALL(check, Call("update"));
|
||||
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
|
||||
CaretChangedReason::Visibilitychange)).Times(1);
|
||||
EXPECT_CALL(check, Call("keyboard"));
|
||||
|
||||
// No CaretStateChanged events should be dispatched since the caret has
|
||||
// being hidden in cursor mode.
|
||||
EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
|
||||
}
|
||||
|
||||
// Simulate typing the end of the input.
|
||||
mManager.UpdateCarets();
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
|
||||
check.Call("update");
|
||||
|
||||
mManager.OnKeyboardEvent();
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
|
||||
check.Call("keyboard");
|
||||
|
||||
mManager.OnSelectionChanged(nullptr, nullptr,
|
||||
nsISelectionListener::NO_REASON);
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
|
||||
|
||||
mManager.OnScrollPositionChanged();
|
||||
EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -22,7 +22,7 @@ const FLOATY_ICON_XXHDPI = "chrome://browser/skin/images/icon_floaty_xxhdpi.png"
|
|||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Messaging.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateChannel.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
document.addEventListener("DOMContentLoaded", init, false);
|
||||
|
||||
function dump(a) {
|
||||
|
@ -138,7 +138,7 @@ function sendFeedback(aEvent) {
|
|||
data["platform"] = Services.appinfo.OS;
|
||||
data["version"] = Services.appinfo.version;
|
||||
data["locale"] = Services.locale.getSystemLocale().getCategory("NSILOCALE_CTYPE");
|
||||
data["channel"] = UpdateChannel.get();
|
||||
data["channel"] = UpdateUtils.UpdateChannel;
|
||||
|
||||
// Source field is added only when Fennec prompts the user.
|
||||
let getParam = window.location.href.split("?");
|
||||
|
|
|
@ -27,7 +27,7 @@ XPCOMUtils.defineLazyModuleGetter(
|
|||
this, "Promise", "resource://gre/modules/Promise.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(
|
||||
this, "UpdateChannel", "resource://gre/modules/UpdateChannel.jsm");
|
||||
this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this, "gUpdateTimer", "@mozilla.org/updates/timer-manager;1", "nsIUpdateTimerManager");
|
||||
|
@ -192,7 +192,7 @@ this.UserAgentUpdates = {
|
|||
"%APP_VERSION%": function() { return gApp.version; },
|
||||
"%BUILD_ID%": function() { return gApp.appBuildID; },
|
||||
"%OS%": function() { return gApp.OS; },
|
||||
"%CHANNEL%": function() { return UpdateChannel.get(); },
|
||||
"%CHANNEL%": function() { return UpdateUtils.UpdateChannel; },
|
||||
"%DISTRIBUTION%": function() { return this._getPref(PREF_APP_DISTRIBUTION, ""); },
|
||||
"%DISTRIBUTION_VERSION%": function() { return this._getPref(PREF_APP_DISTRIBUTION_VERSION, ""); },
|
||||
};
|
||||
|
|
|
@ -25,7 +25,7 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
Cu.import("resource://gre/modules/UpdateChannel.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
// The current policy version number. If the version number stored in the prefs
|
||||
// is smaller than this, data upload will be disabled until the user is re-notified
|
||||
|
@ -388,7 +388,7 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
|||
*/
|
||||
get minimumPolicyVersion() {
|
||||
// First check if the current channel has an ove
|
||||
let channel = UpdateChannel.get(false);
|
||||
let channel = UpdateUtils.getUpdateChannel(false);
|
||||
let channelPref = this._prefs.get("minimumPolicyVersion.channel-" + channel);
|
||||
return channelPref !== undefined ?
|
||||
channelPref : this._prefs.get("minimumPolicyVersion", 1);
|
||||
|
|
|
@ -8,7 +8,7 @@ const {utils: Cu} = Components;
|
|||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/services/datareporting/policy.jsm");
|
||||
Cu.import("resource://testing-common/services/datareporting/mocks.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateChannel.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
function getPolicy(name,
|
||||
|
@ -23,7 +23,7 @@ function getPolicy(name,
|
|||
, defaultBranch: true });
|
||||
defaultPolicyPrefs.set("currentPolicyVersion", aCurrentPolicyVersion);
|
||||
defaultPolicyPrefs.set("minimumPolicyVersion", aMinimumPolicyVersion);
|
||||
let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateChannel.get(false);
|
||||
let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateUtils.getUpdateChannel(false);
|
||||
if (aBranchMinimumVersionOverride !== undefined)
|
||||
defaultPolicyPrefs.set(branchOverridePrefName, aBranchMinimumVersionOverride);
|
||||
else
|
||||
|
|
|
@ -30,8 +30,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryController",
|
||||
"resource://gre/modules/TelemetryController.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
// Oldest year to allow in date preferences. This module was implemented in
|
||||
// 2012 and no dates older than that should be encountered.
|
||||
|
@ -1103,7 +1103,7 @@ AbstractHealthReporter.prototype = Object.freeze({
|
|||
}
|
||||
|
||||
try {
|
||||
out["updateChannel"] = UpdateChannel.get();
|
||||
out["updateChannel"] = UpdateUtils.UpdateChannel;
|
||||
} catch (ex) {
|
||||
this._log.warn("Could not obtain update channel: " +
|
||||
CommonUtils.exceptionStr(ex));
|
||||
|
|
|
@ -46,8 +46,8 @@ Cu.import("resource://services-common/utils.js");
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesDBUtils",
|
||||
"resource://gre/modules/PlacesDBUtils.jsm");
|
||||
|
||||
|
@ -333,7 +333,7 @@ AppInfoProvider.prototype = Object.freeze({
|
|||
}
|
||||
|
||||
try {
|
||||
yield m.setLastText("updateChannel", UpdateChannel.get());
|
||||
yield m.setLastText("updateChannel", UpdateUtils.UpdateChannel);
|
||||
} catch (ex) {
|
||||
this._log.warn("Could not obtain update channel: " +
|
||||
CommonUtils.exceptionStr(ex));
|
||||
|
|
|
@ -451,5 +451,31 @@ this.BrowserTestUtils = {
|
|||
tab.ownerDocument.defaultView.gBrowser.removeTab(tab);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Version of EventUtils' `sendChar` function; it will synthesize a keypress
|
||||
* event in a child process and returns a Promise that will result when the
|
||||
* event was fired. Instead of a Window, a Browser object is required to be
|
||||
* passed to this function.
|
||||
*
|
||||
* @param {String} char
|
||||
* A character for the keypress event that is sent to the browser.
|
||||
* @param {Browser} browser
|
||||
* Browser element, must not be null.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* @resolves True if the keypress event was synthesized.
|
||||
*/
|
||||
sendChar(char, browser) {
|
||||
return new Promise(resolve => {
|
||||
let mm = browser.messageManager;
|
||||
mm.addMessageListener("Test:SendCharDone", function charMsg(message) {
|
||||
mm.removeMessageListener("Test:SendCharDone", charMsg);
|
||||
resolve(message.data.sendCharResult);
|
||||
});
|
||||
|
||||
mm.sendAsyncMessage("Test:SendChar", { char: char });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11,6 +11,10 @@ EventUtils.window = {};
|
|||
EventUtils.parent = EventUtils.window;
|
||||
EventUtils._EU_Ci = Components.interfaces;
|
||||
EventUtils._EU_Cc = Components.classes;
|
||||
// EventUtils' `sendChar` function relies on the navigator to synthetize events.
|
||||
EventUtils.navigator = content.document.defaultView.navigator;
|
||||
EventUtils.KeyboardEvent = content.document.defaultView.KeyboardEvent;
|
||||
|
||||
Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
|
||||
|
||||
addMessageListener("Test:SynthesizeMouse", (message) => {
|
||||
|
@ -39,3 +43,8 @@ addMessageListener("Test:SynthesizeMouse", (message) => {
|
|||
let result = EventUtils.synthesizeMouseAtPoint(left, top, data.event, content);
|
||||
sendAsyncMessage("Test:SynthesizeMouseDone", { defaultPrevented: result });
|
||||
});
|
||||
|
||||
addMessageListener("Test:SendChar", message => {
|
||||
let result = EventUtils.sendChar(message.data.char, content);
|
||||
sendAsyncMessage("Test:SendCharDone", { sendCharResult: result });
|
||||
});
|
||||
|
|
|
@ -95,6 +95,7 @@ user_pref("extensions.update.url", "http://%(server)s/extensions-dummy/updateURL
|
|||
user_pref("extensions.update.background.url", "http://%(server)s/extensions-dummy/updateBackgroundURL");
|
||||
user_pref("extensions.blocklist.url", "http://%(server)s/extensions-dummy/blocklistURL");
|
||||
user_pref("extensions.hotfix.url", "http://%(server)s/extensions-dummy/hotfixURL");
|
||||
user_pref("extensions.systemAddon.update.url", "http://%(server)s/dummy-system-addons.xml");
|
||||
// Turn off extension updates so they don't bother tests
|
||||
user_pref("extensions.update.enabled", false);
|
||||
// Make sure opening about:addons won't hit the network
|
||||
|
|
|
@ -140,6 +140,8 @@ DEFAULTS = dict(
|
|||
'http://127.0.0.1/plugins-dummy/updateCheckURL',
|
||||
'media.gmp-manager.url':
|
||||
'http://127.0.0.1/gmpmanager-dummy/update.xml',
|
||||
'extensions.systemAddon.update.url':
|
||||
'http://127.0.0.1/dummy-system-addons.xml',
|
||||
'media.navigator.enabled': True,
|
||||
'media.peerconnection.enabled': True,
|
||||
'media.navigator.permission.disabled': True,
|
||||
|
|
|
@ -1505,6 +1505,7 @@ try {
|
|||
.getService(Components.interfaces.nsIPrefBranch);
|
||||
|
||||
prefs.setCharPref("media.gmp-manager.url.override", "http://%(server)s/dummy-gmp-manager.xml");
|
||||
prefs.setCharPref("extensions.systemAddon.update.url", "http://%(server)s/dummy-system-addons.xml");
|
||||
prefs.setCharPref("browser.selfsupport.url", "https://%(server)s/selfsupport-dummy/");
|
||||
prefs.setCharPref("toolkit.telemetry.server", "https://%(server)s/telemetry-dummy");
|
||||
prefs.setCharPref("browser.search.geoip.url", "https://%(server)s/geoip-dummy");
|
||||
|
|
|
@ -5314,12 +5314,6 @@
|
|||
"kind": "flag",
|
||||
"description": "Whether enablePrivilege has ever been called during the current session"
|
||||
},
|
||||
"READ_SAVED_PING_SUCCESS": {
|
||||
"alert_emails": ["perf-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "boolean",
|
||||
"description": "Successfully reading a saved ping file"
|
||||
},
|
||||
"TOUCH_ENABLED_DEVICE": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "boolean",
|
||||
|
|
|
@ -80,8 +80,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
|
|||
"resource://gre/modules/TelemetryEnvironment.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionRecorder",
|
||||
"resource://gre/modules/SessionRecorder.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryArchive",
|
||||
"resource://gre/modules/TelemetryArchive.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySession",
|
||||
|
@ -412,7 +412,7 @@ var Impl = {
|
|||
|
||||
let updateChannel = null;
|
||||
try {
|
||||
updateChannel = UpdateChannel.get(false);
|
||||
updateChannel = UpdateUtils.getUpdateChannel(false);
|
||||
} catch (e) {
|
||||
this._log.trace("assemblePing - Unable to get update channel.", e);
|
||||
}
|
||||
|
|
|
@ -32,8 +32,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
|
|||
#endif
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge",
|
||||
"resource://gre/modules/ProfileAge.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
const CHANGE_THROTTLE_INTERVAL_MS = 5 * 60 * 1000;
|
||||
|
||||
|
@ -1007,7 +1007,7 @@ EnvironmentCache.prototype = {
|
|||
_updateSettings: function () {
|
||||
let updateChannel = null;
|
||||
try {
|
||||
updateChannel = UpdateChannel.get(false);
|
||||
updateChannel = UpdateUtils.getUpdateChannel(false);
|
||||
} catch (e) {}
|
||||
|
||||
this._currentEnvironment.settings = {
|
||||
|
|
|
@ -19,8 +19,8 @@ Cu.import("resource://services-common/observers.js", this);
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
|
||||
"resource://gre/modules/TelemetrySend.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
const LOGGER_NAME = "Toolkit.Telemetry";
|
||||
const LOGGER_PREFIX = "TelemetryReportingPolicy::";
|
||||
|
@ -251,7 +251,7 @@ var TelemetryReportingPolicyImpl = {
|
|||
// use the general minimum policy version.
|
||||
let channel = "";
|
||||
try {
|
||||
channel = UpdateChannel.get(false);
|
||||
channel = UpdateUtils.getUpdateChannel(false);
|
||||
} catch(e) {
|
||||
this._log.error("minimumPolicyVersion - Unable to retrieve the current channel.");
|
||||
return minPolicyVersion;
|
||||
|
|
|
@ -143,8 +143,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "ThirdPartyCookieProbe",
|
|||
"resource://gre/modules/ThirdPartyCookieProbe.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
|
||||
"resource://gre/modules/UITelemetry.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
|
||||
"resource://gre/modules/TelemetryEnvironment.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
|
||||
|
|
|
@ -374,19 +374,6 @@ this.TelemetryStorage = {
|
|||
return TelemetryStorageImpl.addPendingPing(pingData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a ping from an existing file to the saved pings directory so that it gets saved
|
||||
* and sent along with other pings.
|
||||
* Note: that the original ping file will not be modified.
|
||||
*
|
||||
* @param {String} pingPath The path to the ping file that needs to be added to the
|
||||
* saved pings directory.
|
||||
* @return {Promise} A promise resolved when the ping is saved to the pings directory.
|
||||
*/
|
||||
addPendingPingFromFile: function(pingPath) {
|
||||
return TelemetryStorageImpl.addPendingPingFromFile(pingPath);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the file for a ping
|
||||
*
|
||||
|
@ -397,16 +384,6 @@ this.TelemetryStorage = {
|
|||
return TelemetryStorageImpl.cleanupPingFile(ping);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the histograms from a file.
|
||||
*
|
||||
* @param {string} file The file to load.
|
||||
* @returns {promise}
|
||||
*/
|
||||
loadHistograms: function loadHistograms(file) {
|
||||
return TelemetryStorageImpl.loadHistograms(file);
|
||||
},
|
||||
|
||||
/**
|
||||
* The number of pending pings on disk.
|
||||
*/
|
||||
|
@ -414,10 +391,6 @@ this.TelemetryStorage = {
|
|||
return TelemetryStorageImpl.pendingPingCount;
|
||||
},
|
||||
|
||||
testLoadHistograms: function(file) {
|
||||
return TelemetryStorageImpl.testLoadHistograms(file);
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads a ping file.
|
||||
* @param {String} aFilePath The path of the ping file.
|
||||
|
@ -1175,26 +1148,6 @@ var TelemetryStorageImpl = {
|
|||
return file;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Add a ping from an existing file to the saved pings directory so that it gets saved
|
||||
* and sent along with other pings.
|
||||
* Note: that the original ping file will not be modified.
|
||||
*
|
||||
* @param {String} pingPath The path to the ping file that needs to be added to the
|
||||
* saved pings directory.
|
||||
* @return {Promise} A promise resolved when the ping is saved to the pings directory.
|
||||
*/
|
||||
addPendingPingFromFile: function(pingPath) {
|
||||
// Pings in the saved ping directory need to have the ping id or slug (old format) as
|
||||
// the file name. We load the ping content, check that it is valid, and use it to save
|
||||
// the ping file with the correct file name.
|
||||
return this.loadPingFile(pingPath).then(ping => {
|
||||
// Since we read a ping successfully, update the related histogram.
|
||||
Telemetry.getHistogramById("READ_SAVED_PING_SUCCESS").add(1);
|
||||
return this.addPendingPing(ping);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a ping to the saved pings directory so that it gets saved
|
||||
* and sent along with other pings.
|
||||
|
@ -1395,37 +1348,10 @@ var TelemetryStorageImpl = {
|
|||
return list;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the histograms from a file.
|
||||
*
|
||||
* Once loaded, the saved pings can be accessed (destructively only)
|
||||
* through |popPendingPings|.
|
||||
*
|
||||
* @param {string} file The file to load.
|
||||
* @returns {promise}
|
||||
*/
|
||||
loadHistograms: Task.async(function*(file) {
|
||||
let success = true;
|
||||
try {
|
||||
const ping = yield this.loadPingfile(file);
|
||||
return ping;
|
||||
} catch (ex) {
|
||||
success = false;
|
||||
yield OS.File.remove(file);
|
||||
} finally {
|
||||
const success_histogram = Telemetry.getHistogramById("READ_SAVED_PING_SUCCESS");
|
||||
success_histogram.add(success);
|
||||
}
|
||||
}),
|
||||
|
||||
get pendingPingCount() {
|
||||
return this._pendingPings.size;
|
||||
},
|
||||
|
||||
testLoadHistograms: function(file) {
|
||||
return this.loadHistograms(file.path);
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads a ping file.
|
||||
* @param {String} aFilePath The path of the ping file.
|
||||
|
|
|
@ -286,12 +286,10 @@ function checkPayload(payload, reason, successfulPings, savedPings) {
|
|||
const TELEMETRY_TEST_COUNT = "TELEMETRY_TEST_COUNT";
|
||||
const TELEMETRY_TEST_KEYED_FLAG = "TELEMETRY_TEST_KEYED_FLAG";
|
||||
const TELEMETRY_TEST_KEYED_COUNT = "TELEMETRY_TEST_KEYED_COUNT";
|
||||
const READ_SAVED_PING_SUCCESS = "READ_SAVED_PING_SUCCESS";
|
||||
|
||||
if (successfulPings > 0) {
|
||||
Assert.ok(TELEMETRY_PING in payload.histograms);
|
||||
}
|
||||
Assert.ok(READ_SAVED_PING_SUCCESS in payload.histograms);
|
||||
Assert.ok(TELEMETRY_TEST_FLAG in payload.histograms);
|
||||
Assert.ok(TELEMETRY_TEST_COUNT in payload.histograms);
|
||||
|
||||
|
@ -346,9 +344,6 @@ function checkPayload(payload, reason, successfulPings, savedPings) {
|
|||
Assert.equal(uneval(tc), uneval(expected_tc));
|
||||
}
|
||||
|
||||
let h = payload.histograms[READ_SAVED_PING_SUCCESS];
|
||||
Assert.equal(h.values[0], 1);
|
||||
|
||||
// The ping should include data from memory reporters. We can't check that
|
||||
// this data is correct, because we can't control the values returned by the
|
||||
// memory reporters. But we can at least check that the data is there.
|
||||
|
@ -487,17 +482,6 @@ add_task(function* test_expiredHistogram() {
|
|||
do_check_eq(TelemetrySession.getPayload()["histograms"]["TELEMETRY_TEST_EXPIRED"], undefined);
|
||||
});
|
||||
|
||||
// Checks that an invalid histogram file is deleted if TelemetryStorage fails to parse it.
|
||||
add_task(function* test_runInvalidJSON() {
|
||||
let pingFile = getSavedPingFile("invalid-histograms.dat");
|
||||
|
||||
writeStringToFile(pingFile, "this.is.invalid.JSON");
|
||||
do_check_true(pingFile.exists());
|
||||
|
||||
yield TelemetryStorage.testLoadHistograms(pingFile);
|
||||
do_check_false(pingFile.exists());
|
||||
});
|
||||
|
||||
// Sends a ping to a non existing server. If we remove this test, we won't get
|
||||
// all the histograms we need in the main ping.
|
||||
add_task(function* test_noServerPing() {
|
||||
|
|
|
@ -25,8 +25,8 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
const PREF_APP_DISTRIBUTION = "distribution.id";
|
||||
const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
function nsURLFormatterService() {
|
||||
XPCOMUtils.defineLazyGetter(this, "appInfo", function UFS_appInfo() {
|
||||
|
@ -111,7 +111,7 @@ nsURLFormatterService.prototype = {
|
|||
XPCOMABI: function() this.ABI,
|
||||
BUILD_TARGET: function() this.appInfo.OS + "_" + this.ABI,
|
||||
OS_VERSION: function() this.OSVersion,
|
||||
CHANNEL: function() UpdateChannel.get(),
|
||||
CHANNEL: function() UpdateUtils.UpdateChannel,
|
||||
MOZILLA_API_KEY: function() "@MOZ_MOZILLA_API_KEY@",
|
||||
GOOGLE_API_KEY: function() "@MOZ_GOOGLE_API_KEY@",
|
||||
GOOGLE_OAUTH_API_CLIENTID:function() "@MOZ_GOOGLE_OAUTH_API_CLIENTID@",
|
||||
|
|
|
@ -19,6 +19,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
|
|||
"resource://gre/modules/Deprecated.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
var gViewSourceUtils = {
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ skip-if = e10s # Bug 1064580
|
|||
[browser_f7_caret_browsing.js]
|
||||
skip-if = e10s
|
||||
[browser_findbar.js]
|
||||
skip-if = e10s # Disabled for e10s: Bug ?????? - seems to be a timing issue with RemoteFinder.jsm messages coming later than the tests expect.
|
||||
[browser_input_file_tooltips.js]
|
||||
skip-if = e10s # Bug ?????? - test directly manipulates content (TypeError: doc.createElement is not a function)
|
||||
[browser_isSynthetic.js]
|
||||
|
|
|
@ -2,6 +2,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
|||
"resource://gre/modules/Promise.jsm");
|
||||
Components.utils.import("resource://gre/modules/Timer.jsm", this);
|
||||
|
||||
const TEST_PAGE_URI = "data:text/html;charset=utf-8,The letter s.";
|
||||
|
||||
/**
|
||||
* Makes sure that the findbar hotkeys (' and /) event listeners
|
||||
* are added to the system event group and do not get blocked
|
||||
|
@ -11,7 +13,7 @@ add_task(function* test_hotkey_event_propagation() {
|
|||
info("Ensure hotkeys are not affected by stopPropagation.");
|
||||
|
||||
// Opening new tab
|
||||
let tab = yield promiseTestPageLoad();
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
let findbar = gBrowser.getFindBar();
|
||||
|
||||
|
@ -23,24 +25,29 @@ add_task(function* test_hotkey_event_propagation() {
|
|||
is(findbar.hidden, true, "Findbar is hidden now.");
|
||||
gBrowser.selectedTab = tab;
|
||||
yield promiseFocus();
|
||||
EventUtils.sendChar(key, browser.contentWindow);
|
||||
yield BrowserTestUtils.sendChar(key, browser);
|
||||
is(findbar.hidden, false, "Findbar should not be hidden.");
|
||||
yield closeFindbarAndWait(findbar);
|
||||
}
|
||||
|
||||
// Stop propagation for all keyboard events.
|
||||
let window = browser.contentWindow;
|
||||
let stopPropagation = function(e) { e.stopImmediatePropagation(); };
|
||||
window.addEventListener("keydown", stopPropagation, true);
|
||||
window.addEventListener("keypress", stopPropagation, true);
|
||||
window.addEventListener("keyup", stopPropagation, true);
|
||||
let frameScript = () => {
|
||||
const stopPropagation = e => e.stopImmediatePropagation();
|
||||
let window = content.document.defaultView;
|
||||
window.removeEventListener("keydown", stopPropagation);
|
||||
window.removeEventListener("keypress", stopPropagation);
|
||||
window.removeEventListener("keyup", stopPropagation);
|
||||
};
|
||||
|
||||
let mm = browser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + frameScript.toString() + ")();", false);
|
||||
|
||||
// Checking if findbar still appears when any hotkey is pressed.
|
||||
for (let key of HOTKEYS) {
|
||||
is(findbar.hidden, true, "Findbar is hidden now.");
|
||||
gBrowser.selectedTab = tab;
|
||||
yield promiseFocus();
|
||||
EventUtils.sendChar(key, browser.contentWindow);
|
||||
yield BrowserTestUtils.sendChar(key, browser);
|
||||
is(findbar.hidden, false, "Findbar should not be hidden.");
|
||||
yield closeFindbarAndWait(findbar);
|
||||
}
|
||||
|
@ -51,7 +58,7 @@ add_task(function* test_hotkey_event_propagation() {
|
|||
add_task(function* test_not_found() {
|
||||
info("Check correct 'Phrase not found' on new tab");
|
||||
|
||||
let tab = yield promiseTestPageLoad();
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
|
||||
// Search for the first word.
|
||||
yield promiseFindFinished("--- THIS SHOULD NEVER MATCH ---", false);
|
||||
|
@ -63,7 +70,7 @@ add_task(function* test_not_found() {
|
|||
});
|
||||
|
||||
add_task(function* test_found() {
|
||||
let tab = yield promiseTestPageLoad();
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
|
||||
// Search for a string that WILL be found, with 'Highlight All' on
|
||||
yield promiseFindFinished("S", true);
|
||||
|
@ -76,10 +83,10 @@ add_task(function* test_found() {
|
|||
// Setting first findbar to case-sensitive mode should not affect
|
||||
// new tab find bar.
|
||||
add_task(function* test_tabwise_case_sensitive() {
|
||||
let tab1 = yield promiseTestPageLoad();
|
||||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
let findbar1 = gBrowser.getFindBar();
|
||||
|
||||
let tab2 = yield promiseTestPageLoad();
|
||||
let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
let findbar2 = gBrowser.getFindBar();
|
||||
|
||||
// Toggle case sensitivity for first findbar
|
||||
|
@ -102,22 +109,33 @@ add_task(function* test_tabwise_case_sensitive() {
|
|||
gBrowser.removeTab(tab2);
|
||||
});
|
||||
|
||||
function promiseTestPageLoad() {
|
||||
let deferred = Promise.defer();
|
||||
/**
|
||||
* Navigating from a web page (for example mozilla.org) to an internal page
|
||||
* (like about:addons) might trigger a change of browser's remoteness.
|
||||
* 'Remoteness change' means that rendering page content moves from child
|
||||
* process into the parent process or the other way around.
|
||||
* This test ensures that findbar properly handles such a change.
|
||||
*/
|
||||
add_task(function * test_reinitialization_at_remoteness_change() {
|
||||
info("Ensure findbar re-initialization at remoteness change.");
|
||||
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("data:text/html;charset=utf-8,The letter s.");
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
browser.addEventListener("load", function listener() {
|
||||
if (browser.currentURI.spec == "about:blank")
|
||||
return;
|
||||
info("Page loaded: " + browser.currentURI.spec);
|
||||
browser.removeEventListener("load", listener, true);
|
||||
// Load a remote page and trigger findbar construction.
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
let findbar = gBrowser.getFindBar();
|
||||
|
||||
deferred.resolve(tab);
|
||||
}, true);
|
||||
// Findbar should operate normally.
|
||||
yield promiseFindFinished("s", false);
|
||||
ok(!findbar._findStatusDesc.textContent, "Findbar status should be empty");
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
gBrowser.updateBrowserRemoteness(browser, false);
|
||||
|
||||
// Findbar should keep operating normally.
|
||||
yield promiseFindFinished("s", false);
|
||||
ok(!findbar._findStatusDesc.textContent, "Findbar status should be empty");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
function promiseFindFinished(searchText, highlightOn) {
|
||||
let deferred = Promise.defer();
|
||||
|
@ -131,8 +149,17 @@ function promiseFindFinished(searchText, highlightOn) {
|
|||
findbar._findField.value = searchText;
|
||||
|
||||
let resultListener;
|
||||
// When highlighting is on the finder sends a second "FOUND" message after
|
||||
// the search wraps. This causes timing problems with e10s. waitMore
|
||||
// forces foundOrTimeout wait for the second "FOUND" message before
|
||||
// resolving the promise.
|
||||
let waitMore = highlightOn;
|
||||
let findTimeout = setTimeout(() => foundOrTimedout(null), 2000);
|
||||
let foundOrTimedout = function(aData) {
|
||||
if (aData !== null && waitMore) {
|
||||
waitMore = false;
|
||||
return;
|
||||
}
|
||||
if (aData === null)
|
||||
info("Result listener not called, timeout reached.");
|
||||
clearTimeout(findTimeout);
|
||||
|
|
|
@ -4,4 +4,3 @@ tail =
|
|||
skip-if = toolkit == 'gonk'
|
||||
|
||||
[test_contentAreaUtils.js]
|
||||
[test_updateChannelModule.js]
|
||||
|
|
|
@ -373,12 +373,28 @@
|
|||
// browser property
|
||||
if (this.getAttribute("browserid"))
|
||||
setTimeout(function(aSelf) { aSelf.browser = aSelf.browser; }, 0, this);
|
||||
|
||||
if (typeof gBrowser !== 'undefined')
|
||||
gBrowser.tabContainer.addEventListener("TabRemotenessChange", this);
|
||||
]]></constructor>
|
||||
|
||||
<destructor><![CDATA[
|
||||
this.destroy();
|
||||
]]></destructor>
|
||||
|
||||
<method name="handleEvent">
|
||||
<parameter name="aEvent"/>
|
||||
<body><![CDATA[
|
||||
switch(aEvent.type) {
|
||||
case "onRemotenessChange":
|
||||
// Reinitializing browser to re-attach listeners.
|
||||
this.browser._lastSearchString = this._findField.value;
|
||||
this.browser = this.browser;
|
||||
break;
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- This is necessary because the destructor isn't called when
|
||||
we are removed from a document that is not destroyed. This
|
||||
needs to be explicitly called in this case -->
|
||||
|
@ -402,6 +418,9 @@
|
|||
|
||||
// Clear all timers that might still be running.
|
||||
this._cancelTimers();
|
||||
|
||||
if (typeof gBrowser !== 'undefined')
|
||||
gBrowser.tabContainer.removeEventListener("TabRemotenessChange", this);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
|
|
|
@ -213,8 +213,10 @@ this.AppConstants = Object.freeze({
|
|||
MOZ_APP_VERSION_DISPLAY: "@MOZ_APP_VERSION_DISPLAY@",
|
||||
MOZ_BUILD_APP: "@MOZ_BUILD_APP@",
|
||||
MOZ_UPDATE_CHANNEL: "@MOZ_UPDATE_CHANNEL@",
|
||||
INSTALL_LOCALE: "@AB_CD@",
|
||||
MOZ_WIDGET_TOOLKIT: "@MOZ_WIDGET_TOOLKIT@",
|
||||
ANDROID_PACKAGE_NAME: "@ANDROID_PACKAGE_NAME@",
|
||||
|
||||
MOZ_ANDROID_APZ:
|
||||
#ifdef MOZ_ANDROID_APZ
|
||||
true,
|
||||
|
|
|
@ -8,10 +8,6 @@ this.EXPORTED_SYMBOLS = [];
|
|||
|
||||
const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu, manager: Cm} =
|
||||
Components;
|
||||
// Chunk size for the incremental downloader
|
||||
const DOWNLOAD_CHUNK_BYTES_SIZE = 300000;
|
||||
// Incremental downloader interval
|
||||
const DOWNLOAD_INTERVAL = 0;
|
||||
// 1 day default
|
||||
const DEFAULT_SECONDS_BETWEEN_CHECKS = 60 * 60 * 24;
|
||||
|
||||
|
@ -30,15 +26,12 @@ Cu.import("resource://gre/modules/Preferences.jsm");
|
|||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
Cu.import("resource://gre/modules/GMPUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["GMPInstallManager", "GMPExtractor", "GMPDownloader",
|
||||
"GMPAddon"];
|
||||
|
||||
var gLocale = null;
|
||||
|
||||
// Shared code for suppressing bad cert dialogs
|
||||
XPCOMUtils.defineLazyGetter(this, "gCertUtils", function() {
|
||||
let temp = { };
|
||||
|
@ -46,18 +39,8 @@ XPCOMUtils.defineLazyGetter(this, "gCertUtils", function() {
|
|||
return temp;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
/**
|
||||
* Number of milliseconds after which we need to cancel `checkForAddons`.
|
||||
*
|
||||
* Bug 1087674 suggests that the XHR we use in `checkForAddons` may
|
||||
* never terminate in presence of network nuisances (e.g. strange
|
||||
* antivirus behavior). This timeout is a defensive measure to ensure
|
||||
* that we fail cleanly in such case.
|
||||
*/
|
||||
const CHECK_FOR_ADDONS_TIMEOUT_DELAY_MS = 20000;
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
function getScopedLogger(prefix) {
|
||||
// `PARENT_LOGGER_ID.` being passed here effectively links this logger
|
||||
|
@ -65,138 +48,6 @@ function getScopedLogger(prefix) {
|
|||
return Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP", prefix + " ");
|
||||
}
|
||||
|
||||
// This is copied directly from nsUpdateService.js
|
||||
// It is used for calculating the URL string w/ var replacement.
|
||||
// TODO: refactor this out somewhere else
|
||||
XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() {
|
||||
let osVersion;
|
||||
try {
|
||||
osVersion = Services.sysinfo.getProperty("name") + " " +
|
||||
Services.sysinfo.getProperty("version");
|
||||
}
|
||||
catch (e) {
|
||||
LOG("gOSVersion - OS Version unknown: updates are not possible.");
|
||||
}
|
||||
|
||||
if (osVersion) {
|
||||
if (AppConstants.platform == "win") {
|
||||
const BYTE = ctypes.uint8_t;
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
const WCHAR = ctypes.char16_t;
|
||||
const BOOL = ctypes.int;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
|
||||
const SZCSDVERSIONLENGTH = 128;
|
||||
const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
|
||||
[
|
||||
{dwOSVersionInfoSize: DWORD},
|
||||
{dwMajorVersion: DWORD},
|
||||
{dwMinorVersion: DWORD},
|
||||
{dwBuildNumber: DWORD},
|
||||
{dwPlatformId: DWORD},
|
||||
{szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
|
||||
{wServicePackMajor: WORD},
|
||||
{wServicePackMinor: WORD},
|
||||
{wSuiteMask: WORD},
|
||||
{wProductType: BYTE},
|
||||
{wReserved: BYTE}
|
||||
]);
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = false;
|
||||
try {
|
||||
kernel32 = ctypes.open("Kernel32");
|
||||
} catch (e) {
|
||||
LOG("gOSVersion - Unable to open kernel32! " + e);
|
||||
osVersion += ".unknown (unknown)";
|
||||
}
|
||||
|
||||
if(kernel32) {
|
||||
try {
|
||||
// Get Service pack info
|
||||
try {
|
||||
let GetVersionEx = kernel32.declare("GetVersionExW",
|
||||
ctypes.default_abi,
|
||||
BOOL,
|
||||
OSVERSIONINFOEXW.ptr);
|
||||
let winVer = OSVERSIONINFOEXW();
|
||||
winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
|
||||
|
||||
if(0 !== GetVersionEx(winVer.address())) {
|
||||
osVersion += "." + winVer.wServicePackMajor
|
||||
+ "." + winVer.wServicePackMinor;
|
||||
} else {
|
||||
LOG("gOSVersion - Unknown failure in GetVersionEX (returned 0)");
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
} catch (e) {
|
||||
LOG("gOSVersion - error getting service pack information. Exception: " + e);
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
|
||||
// Get processor architecture
|
||||
let arch = "unknown";
|
||||
try {
|
||||
let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
|
||||
ctypes.default_abi,
|
||||
ctypes.void_t,
|
||||
SYSTEM_INFO.ptr);
|
||||
let sysInfo = SYSTEM_INFO();
|
||||
// Default to unknown
|
||||
sysInfo.wProcessorArchitecture = 0xffff;
|
||||
|
||||
GetNativeSystemInfo(sysInfo.address());
|
||||
switch(sysInfo.wProcessorArchitecture) {
|
||||
case 9:
|
||||
arch = "x64";
|
||||
break;
|
||||
case 6:
|
||||
arch = "IA64";
|
||||
break;
|
||||
case 0:
|
||||
arch = "x86";
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
LOG("gOSVersion - error getting processor architecture. Exception: " + e);
|
||||
} finally {
|
||||
osVersion += " (" + arch + ")";
|
||||
}
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
|
||||
}
|
||||
catch (e) {
|
||||
// Not all platforms have a secondary widget library, so an error is nothing to worry about.
|
||||
}
|
||||
osVersion = encodeURIComponent(osVersion);
|
||||
}
|
||||
return osVersion;
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides an easy API for downloading and installing GMP Addons
|
||||
*/
|
||||
|
@ -221,24 +72,8 @@ GMPInstallManager.prototype = {
|
|||
log.info("Using url: " + url);
|
||||
}
|
||||
|
||||
url =
|
||||
url.replace(/%PRODUCT%/g, Services.appinfo.name)
|
||||
.replace(/%VERSION%/g, Services.appinfo.version)
|
||||
.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID)
|
||||
.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + GMPUtils.ABI())
|
||||
.replace(/%OS_VERSION%/g, gOSVersion);
|
||||
if (/%LOCALE%/.test(url)) {
|
||||
// TODO: Get the real local, does it actually matter for GMP plugins?
|
||||
url = url.replace(/%LOCALE%/g, "en-US");
|
||||
}
|
||||
url =
|
||||
url.replace(/%CHANNEL%/g, UpdateChannel.get())
|
||||
.replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion)
|
||||
.replace(/%DISTRIBUTION%/g,
|
||||
GMPPrefs.get(GMPPrefs.KEY_APP_DISTRIBUTION))
|
||||
.replace(/%DISTRIBUTION_VERSION%/g,
|
||||
GMPPrefs.get(GMPPrefs.KEY_APP_DISTRIBUTION_VERSION))
|
||||
.replace(/\+/g, "%2B");
|
||||
url = UpdateUtils.formatUpdateURL(url);
|
||||
|
||||
log.info("Using url (with replacement): " + url);
|
||||
return url;
|
||||
},
|
||||
|
@ -260,38 +95,27 @@ GMPInstallManager.prototype = {
|
|||
this._deferred = Promise.defer();
|
||||
let url = this._getURL();
|
||||
|
||||
this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsISupports);
|
||||
// This is here to let unit test code override XHR
|
||||
if (this._request.wrappedJSObject) {
|
||||
this._request = this._request.wrappedJSObject;
|
||||
let allowNonBuiltIn = true;
|
||||
let certs = null;
|
||||
if (!Services.prefs.prefHasUserValue(GMPPrefs.KEY_URL_OVERRIDE)) {
|
||||
allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_REQUIREBUILTIN, true);
|
||||
if (GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS, true)) {
|
||||
certs = gCertUtils.readCertPrefs(GMPPrefs.KEY_CERTS_BRANCH);
|
||||
}
|
||||
}
|
||||
this._request.open("GET", url, true);
|
||||
let allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS, true);
|
||||
this._request.channel.notificationCallbacks =
|
||||
new gCertUtils.BadCertHandler(allowNonBuiltIn);
|
||||
// Prevent the request from reading from the cache.
|
||||
this._request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
// Prevent the request from writing to the cache.
|
||||
this._request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
|
||||
this._request.overrideMimeType("text/xml");
|
||||
// The Cache-Control header is only interpreted by proxies and the
|
||||
// final destination. It does not help if a resource is already
|
||||
// cached locally.
|
||||
this._request.setRequestHeader("Cache-Control", "no-cache");
|
||||
// HTTP/1.0 servers might not implement Cache-Control and
|
||||
// might only implement Pragma: no-cache
|
||||
this._request.setRequestHeader("Pragma", "no-cache");
|
||||
|
||||
this._request.timeout = CHECK_FOR_ADDONS_TIMEOUT_DELAY_MS;
|
||||
this._request.addEventListener("error", event => this.onFailXML("onErrorXML", event), false);
|
||||
this._request.addEventListener("abort", event => this.onFailXML("onAbortXML", event), false);
|
||||
this._request.addEventListener("timeout", event => this.onFailXML("onTimeoutXML", event), false);
|
||||
this._request.addEventListener("load", event => this.onLoadXML(event), false);
|
||||
|
||||
log.info("sending request to: " + url);
|
||||
this._request.send(null);
|
||||
ProductAddonChecker.getProductAddonList(url, allowNonBuiltIn, certs).then((addons) => {
|
||||
if (!addons) {
|
||||
this._deferred.resolve([]);
|
||||
}
|
||||
else {
|
||||
this._deferred.resolve([for (a of addons) new GMPAddon(a)]);
|
||||
}
|
||||
delete this._deferred;
|
||||
}, (ex) => {
|
||||
this._deferred.reject(ex);
|
||||
delete this._deferred;
|
||||
});
|
||||
|
||||
return this._deferred.promise;
|
||||
},
|
||||
|
@ -493,132 +317,6 @@ GMPInstallManager.prototype = {
|
|||
* This is useful for tests.
|
||||
*/
|
||||
overrideLeaveDownloadedZip: false,
|
||||
|
||||
/**
|
||||
* The XMLHttpRequest succeeded and the document was loaded.
|
||||
* @param event The nsIDOMEvent for the load
|
||||
*/
|
||||
onLoadXML: function(event) {
|
||||
let log = getScopedLogger("GMPInstallManager.onLoadXML");
|
||||
try {
|
||||
log.info("request completed downloading document");
|
||||
let certs = null;
|
||||
if (!Services.prefs.prefHasUserValue(GMPPrefs.KEY_URL_OVERRIDE) &&
|
||||
GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS, true)) {
|
||||
certs = gCertUtils.readCertPrefs(GMPPrefs.KEY_CERTS_BRANCH);
|
||||
}
|
||||
|
||||
let allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_REQUIREBUILTIN,
|
||||
true);
|
||||
log.info("allowNonBuiltIn: " + allowNonBuiltIn);
|
||||
|
||||
gCertUtils.checkCert(this._request.channel, allowNonBuiltIn, certs);
|
||||
|
||||
this.parseResponseXML();
|
||||
} catch (ex) {
|
||||
log.error("could not load xml: " + ex);
|
||||
this._deferred.reject({
|
||||
target: event.target,
|
||||
status: this._getChannelStatus(event.target),
|
||||
message: "" + ex,
|
||||
});
|
||||
delete this._deferred;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the status code for the XMLHttpRequest
|
||||
*/
|
||||
_getChannelStatus: function(request) {
|
||||
let log = getScopedLogger("GMPInstallManager._getChannelStatus");
|
||||
let status = null;
|
||||
try {
|
||||
status = request.status;
|
||||
log.info("request.status is: " + request.status);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
if (status == null) {
|
||||
status = request.channel.QueryInterface(Ci.nsIRequest).status;
|
||||
}
|
||||
return status;
|
||||
},
|
||||
|
||||
/**
|
||||
* There was an error of some kind during the XMLHttpRequest. This
|
||||
* error may have been caused by external factors (e.g. network
|
||||
* issues) or internally (by a timeout).
|
||||
*
|
||||
* @param event The nsIDOMEvent for the error
|
||||
*/
|
||||
onFailXML: function(failure, event) {
|
||||
let log = getScopedLogger("GMPInstallManager.onFailXML " + failure);
|
||||
let request = event.target;
|
||||
let status = this._getChannelStatus(request);
|
||||
let message = "request.status: " + status + " (" + event.type + ")";
|
||||
log.warn(message);
|
||||
this._deferred.reject({
|
||||
target: request,
|
||||
status: status,
|
||||
message: message
|
||||
});
|
||||
delete this._deferred;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array of GMPAddon objects discovered by the update check.
|
||||
* Or returns an empty array if there were any problems with parsing.
|
||||
* If there's an error, it will be logged if logging is enabled.
|
||||
*/
|
||||
parseResponseXML: function() {
|
||||
try {
|
||||
let log = getScopedLogger("GMPInstallManager.parseResponseXML");
|
||||
let updatesElement = this._request.responseXML.documentElement;
|
||||
if (!updatesElement) {
|
||||
let message = "empty updates document";
|
||||
log.warn(message);
|
||||
this._deferred.reject({
|
||||
target: this._request,
|
||||
message: message
|
||||
});
|
||||
delete this._deferred;
|
||||
return;
|
||||
}
|
||||
|
||||
if (updatesElement.nodeName != "updates") {
|
||||
let message = "got node name: " + updatesElement.nodeName +
|
||||
", expected: updates";
|
||||
log.warn(message);
|
||||
this._deferred.reject({
|
||||
target: this._request,
|
||||
message: message
|
||||
});
|
||||
delete this._deferred;
|
||||
return;
|
||||
}
|
||||
|
||||
const ELEMENT_NODE = Ci.nsIDOMNode.ELEMENT_NODE;
|
||||
let gmpResults = [];
|
||||
for (let i = 0; i < updatesElement.childNodes.length; ++i) {
|
||||
let updatesChildElement = updatesElement.childNodes.item(i);
|
||||
if (updatesChildElement.nodeType != ELEMENT_NODE) {
|
||||
continue;
|
||||
}
|
||||
if (updatesChildElement.localName == "addons") {
|
||||
gmpResults = GMPAddon.parseGMPAddonsNode(updatesChildElement);
|
||||
}
|
||||
}
|
||||
this._deferred.resolve(gmpResults);
|
||||
delete this._deferred;
|
||||
} catch (e) {
|
||||
this._deferred.reject({
|
||||
target: this._request,
|
||||
message: e
|
||||
});
|
||||
delete this._deferred;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -626,49 +324,16 @@ GMPInstallManager.prototype = {
|
|||
* GMPAddon objects are returns from GMPInstallManager.checkForAddons
|
||||
* GMPAddon objects can also be used in calls to GMPInstallManager.installAddon
|
||||
*
|
||||
* @param gmpAddon The AUS response XML's DOM element `addon`
|
||||
* @param addon The ProductAddonChecker `addon` object
|
||||
*/
|
||||
function GMPAddon(gmpAddon) {
|
||||
function GMPAddon(addon) {
|
||||
let log = getScopedLogger("GMPAddon.constructor");
|
||||
gmpAddon.QueryInterface(Ci.nsIDOMElement);
|
||||
["id", "URL", "hashFunction",
|
||||
"hashValue", "version", "size"].forEach(name => {
|
||||
if (gmpAddon.hasAttribute(name)) {
|
||||
this[name] = gmpAddon.getAttribute(name);
|
||||
}
|
||||
});
|
||||
this.size = Number(this.size) || undefined;
|
||||
for (let name of Object.keys(addon)) {
|
||||
this[name] = addon[name];
|
||||
}
|
||||
log.info ("Created new addon: " + this.toString());
|
||||
}
|
||||
/**
|
||||
* Parses an XML GMP addons node from AUS into an array
|
||||
* @param addonsElement An nsIDOMElement compatible node with XML from AUS
|
||||
* @return An array of GMPAddon results
|
||||
*/
|
||||
GMPAddon.parseGMPAddonsNode = function(addonsElement) {
|
||||
let log = getScopedLogger("GMPAddon.parseGMPAddonsNode");
|
||||
let gmpResults = [];
|
||||
if (addonsElement.localName !== "addons") {
|
||||
return;
|
||||
}
|
||||
|
||||
addonsElement.QueryInterface(Ci.nsIDOMElement);
|
||||
let addonCount = addonsElement.childNodes.length;
|
||||
for (let i = 0; i < addonCount; ++i) {
|
||||
let addonElement = addonsElement.childNodes.item(i);
|
||||
if (addonElement.localName !== "addon") {
|
||||
continue;
|
||||
}
|
||||
addonElement.QueryInterface(Ci.nsIDOMElement);
|
||||
try {
|
||||
gmpResults.push(new GMPAddon(addonElement));
|
||||
} catch (e) {
|
||||
log.warn("invalid addon: " + e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return gmpResults;
|
||||
};
|
||||
GMPAddon.prototype = {
|
||||
/**
|
||||
* Returns a string representation of the addon
|
||||
|
@ -799,38 +464,7 @@ function GMPDownloader(gmpAddon)
|
|||
{
|
||||
this._gmpAddon = gmpAddon;
|
||||
}
|
||||
/**
|
||||
* Computes the file hash of fileToHash with the specified hash function
|
||||
* @param hashFunctionName A hash function name such as sha512
|
||||
* @param fileToHash An nsIFile to hash
|
||||
* @return a promise which resolve to a digest in binary hex format
|
||||
*/
|
||||
GMPDownloader.computeHash = function(hashFunctionName, fileToHash) {
|
||||
let log = getScopedLogger("GMPDownloader.computeHash");
|
||||
let digest;
|
||||
let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
fileStream.init(fileToHash, FileUtils.MODE_RDONLY,
|
||||
FileUtils.PERMS_FILE, 0);
|
||||
try {
|
||||
let hash = Cc["@mozilla.org/security/hash;1"].
|
||||
createInstance(Ci.nsICryptoHash);
|
||||
let hashFunction =
|
||||
Ci.nsICryptoHash[hashFunctionName.toUpperCase()];
|
||||
if (!hashFunction) {
|
||||
log.error("could not get hash function");
|
||||
return Promise.reject();
|
||||
}
|
||||
hash.init(hashFunction);
|
||||
hash.updateFromStream(fileStream, -1);
|
||||
digest = binaryToHex(hash.finish(false));
|
||||
} catch (e) {
|
||||
log.warn("failed to compute hash: " + e);
|
||||
digest = "";
|
||||
}
|
||||
fileStream.close();
|
||||
return Promise.resolve(digest);
|
||||
},
|
||||
|
||||
GMPDownloader.prototype = {
|
||||
/**
|
||||
* Starts the download process for an addon.
|
||||
|
@ -838,9 +472,10 @@ GMPDownloader.prototype = {
|
|||
* See GMPInstallManager.installAddon for resolve/rejected info
|
||||
*/
|
||||
start: function() {
|
||||
let log = getScopedLogger("GMPDownloader.start");
|
||||
this._deferred = Promise.defer();
|
||||
if (!this._gmpAddon.isValid) {
|
||||
let log = getScopedLogger("GMPDownloader");
|
||||
let gmpAddon = this._gmpAddon;
|
||||
|
||||
if (!gmpAddon.isValid) {
|
||||
log.info("gmpAddon is not valid, will not continue");
|
||||
return Promise.reject({
|
||||
target: this,
|
||||
|
@ -849,55 +484,14 @@ GMPDownloader.prototype = {
|
|||
});
|
||||
}
|
||||
|
||||
let uri = Services.io.newURI(this._gmpAddon.URL, null, null);
|
||||
this._request = Cc["@mozilla.org/network/incremental-download;1"].
|
||||
createInstance(Ci.nsIIncrementalDownload);
|
||||
let gmpFile = FileUtils.getFile("TmpD", [this._gmpAddon.id + ".zip"]);
|
||||
if (gmpFile.exists()) {
|
||||
gmpFile.remove(false);
|
||||
}
|
||||
|
||||
log.info("downloading from " + uri.spec + " to " + gmpFile.path);
|
||||
this._request.init(uri, gmpFile, DOWNLOAD_CHUNK_BYTES_SIZE,
|
||||
DOWNLOAD_INTERVAL);
|
||||
this._request.start(this, null);
|
||||
return this._deferred.promise;
|
||||
},
|
||||
// For nsIRequestObserver
|
||||
onStartRequest: function(request, context) {
|
||||
},
|
||||
// For nsIRequestObserver
|
||||
// Called when the GMP addon zip file is downloaded
|
||||
onStopRequest: function(request, context, status) {
|
||||
let log = getScopedLogger("GMPDownloader.onStopRequest");
|
||||
log.info("onStopRequest called");
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
log.info("status failed: " + status);
|
||||
this._deferred.reject({
|
||||
target: this,
|
||||
status: status,
|
||||
type: "downloaderr"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let promise = this._verifyDownload();
|
||||
promise.then(() => {
|
||||
log.info("GMP file is ready to unzip");
|
||||
let destination = this._request.destination;
|
||||
|
||||
let zipPath = destination.path;
|
||||
let gmpAddon = this._gmpAddon;
|
||||
let installToDirPath = Cc["@mozilla.org/file/local;1"].
|
||||
createInstance(Ci.nsIFile);
|
||||
return ProductAddonChecker.downloadAddon(gmpAddon).then((zipPath) => {
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
gmpAddon.id,
|
||||
gmpAddon.version);
|
||||
installToDirPath.initWithPath(path);
|
||||
log.info("install to directory path: " + installToDirPath.path);
|
||||
let gmpInstaller = new GMPExtractor(zipPath, installToDirPath.path);
|
||||
log.info("install to directory path: " + path);
|
||||
let gmpInstaller = new GMPExtractor(zipPath, path);
|
||||
let installPromise = gmpInstaller.install();
|
||||
installPromise.then(extractedPaths => {
|
||||
return installPromise.then(extractedPaths => {
|
||||
// Success, set the prefs
|
||||
let now = Math.round(Date.now() / 1000);
|
||||
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, now, gmpAddon.id);
|
||||
|
@ -908,78 +502,13 @@ GMPDownloader.prototype = {
|
|||
// Remember our ABI, so that if the profile is migrated to another
|
||||
// platform or from 32 -> 64 bit, we notice and don't try to load the
|
||||
// unexecutable plugin library.
|
||||
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_ABI, GMPUtils.ABI(), gmpAddon.id);
|
||||
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_ABI, UpdateUtils.ABI, gmpAddon.id);
|
||||
// Setting the version pref signals installation completion to consumers,
|
||||
// if you need to set other prefs etc. do it before this.
|
||||
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_VERSION, gmpAddon.version,
|
||||
gmpAddon.id);
|
||||
this._deferred.resolve(extractedPaths);
|
||||
}, err => {
|
||||
this._deferred.reject(err);
|
||||
});
|
||||
}, err => {
|
||||
log.warn("verifyDownload check failed");
|
||||
this._deferred.reject({
|
||||
target: this,
|
||||
status: 200,
|
||||
type: "verifyerr"
|
||||
return extractedPaths;
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Verifies that the downloaded zip file's hash matches the GMPAddon hash.
|
||||
* @return a promise which resolves if the download verifies
|
||||
*/
|
||||
_verifyDownload: function() {
|
||||
let verifyDownloadDeferred = Promise.defer();
|
||||
let log = getScopedLogger("GMPDownloader._verifyDownload");
|
||||
log.info("_verifyDownload called");
|
||||
if (!this._request) {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
let destination = this._request.destination;
|
||||
log.info("for path: " + destination.path);
|
||||
|
||||
// Ensure that the file size matches the expected file size.
|
||||
if (this._gmpAddon.size !== undefined &&
|
||||
destination.fileSize != this._gmpAddon.size) {
|
||||
log.warn("Downloader:_verifyDownload downloaded size " +
|
||||
destination.fileSize + " != expected size " +
|
||||
this._gmpAddon.size + ".");
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
let promise = GMPDownloader.computeHash(this._gmpAddon.hashFunction, destination);
|
||||
promise.then(digest => {
|
||||
let expectedDigest = this._gmpAddon.hashValue.toLowerCase();
|
||||
if (digest !== expectedDigest) {
|
||||
log.warn("hashes do not match! Got: `" +
|
||||
digest + "`, expected: `" + expectedDigest + "`");
|
||||
this._deferred.reject();
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("hashes match!");
|
||||
verifyDownloadDeferred.resolve();
|
||||
}, err => {
|
||||
verifyDownloadDeferred.reject();
|
||||
});
|
||||
return verifyDownloadDeferred.promise;
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver])
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a string containing binary values to hex.
|
||||
*/
|
||||
function binaryToHex(input) {
|
||||
let result = "";
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
let hex = input.charCodeAt(i).toString(16);
|
||||
if (hex.length == 1)
|
||||
hex = "0" + hex;
|
||||
result += hex;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -126,27 +126,6 @@ this.GMPUtils = {
|
|||
hist.add(value);
|
||||
}
|
||||
},
|
||||
|
||||
ABI: function() {
|
||||
// This is copied directly from nsUpdateService.js
|
||||
let abi = null;
|
||||
try {
|
||||
abi = Services.appinfo.XPCOMABI;
|
||||
}
|
||||
catch (e) {
|
||||
return "unknown";
|
||||
}
|
||||
if (AppConstants.platform == "macosx") {
|
||||
// Mac universal build should report a different ABI than either macppc
|
||||
// or mactel.
|
||||
let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
|
||||
getService(Ci.nsIMacUtils);
|
||||
|
||||
if (macutils.isUniversalBinary)
|
||||
abi += "-u-" + macutils.architecturesInBinary;
|
||||
}
|
||||
return abi;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -181,7 +181,7 @@ var dataProviders = {
|
|||
};
|
||||
|
||||
if (AppConstants.MOZ_UPDATER)
|
||||
data.updateChannel = Cu.import("resource://gre/modules/UpdateChannel.jsm", {}).UpdateChannel.get();
|
||||
data.updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
|
||||
|
||||
try {
|
||||
data.vendor = Services.prefs.getCharPref("app.support.vendor");
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["UpdateChannel"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
this.UpdateChannel = {
|
||||
/**
|
||||
* Read the update channel from defaults only. We do this to ensure that
|
||||
* the channel is tightly coupled with the application and does not apply
|
||||
* to other instances of the application that may use the same profile.
|
||||
*
|
||||
* @param [optional] aIncludePartners
|
||||
* Whether or not to include the partner bits. Default: true.
|
||||
*/
|
||||
get: function UpdateChannel_get(aIncludePartners = true) {
|
||||
let channel = AppConstants.MOZ_UPDATE_CHANNEL;
|
||||
let defaults = Services.prefs.getDefaultBranch(null);
|
||||
try {
|
||||
channel = defaults.getCharPref("app.update.channel");
|
||||
} catch (e) {
|
||||
// use default value when pref not found
|
||||
}
|
||||
|
||||
if (aIncludePartners) {
|
||||
try {
|
||||
let partners = Services.prefs.getChildList("app.partner.").sort();
|
||||
if (partners.length) {
|
||||
channel += "-cck";
|
||||
partners.forEach(function (prefName) {
|
||||
channel += "-" + Services.prefs.getCharPref(prefName);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,347 @@
|
|||
/* 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/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["UpdateUtils"];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
|
||||
const FILE_UPDATE_LOCALE = "update.locale";
|
||||
const PREF_APP_DISTRIBUTION = "distribution.id";
|
||||
const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
|
||||
const PREF_APP_B2G_VERSION = "b2g.version";
|
||||
const PREF_APP_UPDATE_CUSTOM = "app.update.custom";
|
||||
const PREF_APP_UPDATE_IMEI_HASH = "app.update.imei_hash";
|
||||
|
||||
|
||||
this.UpdateUtils = {
|
||||
/**
|
||||
* Read the update channel from defaults only. We do this to ensure that
|
||||
* the channel is tightly coupled with the application and does not apply
|
||||
* to other instances of the application that may use the same profile.
|
||||
*
|
||||
* @param [optional] aIncludePartners
|
||||
* Whether or not to include the partner bits. Default: true.
|
||||
*/
|
||||
getUpdateChannel(aIncludePartners = true) {
|
||||
let channel = AppConstants.MOZ_UPDATE_CHANNEL;
|
||||
let defaults = Services.prefs.getDefaultBranch(null);
|
||||
try {
|
||||
channel = defaults.getCharPref("app.update.channel");
|
||||
} catch (e) {
|
||||
// use default value when pref not found
|
||||
}
|
||||
|
||||
if (aIncludePartners) {
|
||||
try {
|
||||
let partners = Services.prefs.getChildList("app.partner.").sort();
|
||||
if (partners.length) {
|
||||
channel += "-cck";
|
||||
partners.forEach(function (prefName) {
|
||||
channel += "-" + Services.prefs.getCharPref(prefName);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
},
|
||||
|
||||
get UpdateChannel() {
|
||||
return this.getUpdateChannel();
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats a URL by replacing %...% values with OS, build and locale specific
|
||||
* values.
|
||||
*
|
||||
* @param url
|
||||
* The URL to format.
|
||||
* @return The formatted URL.
|
||||
*/
|
||||
formatUpdateURL(url) {
|
||||
url = url.replace(/%PRODUCT%/g, Services.appinfo.name);
|
||||
url = url.replace(/%VERSION%/g, Services.appinfo.version);
|
||||
url = url.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID);
|
||||
url = url.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + this.ABI);
|
||||
url = url.replace(/%OS_VERSION%/g, this.OSVersion);
|
||||
if (/%LOCALE%/.test(url)) {
|
||||
url = url.replace(/%LOCALE%/g, this.Locale);
|
||||
}
|
||||
url = url.replace(/%CHANNEL%/g, this.UpdateChannel);
|
||||
url = url.replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion);
|
||||
url = url.replace(/%DISTRIBUTION%/g,
|
||||
getDistributionPrefValue(PREF_APP_DISTRIBUTION));
|
||||
url = url.replace(/%DISTRIBUTION_VERSION%/g,
|
||||
getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
|
||||
url = url.replace(/%CUSTOM%/g, Preferences.get(PREF_APP_UPDATE_CUSTOM, ""));
|
||||
url = url.replace(/\+/g, "%2B");
|
||||
|
||||
if (AppConstants.platform == "gonk") {
|
||||
let sysLibs = {};
|
||||
Cu.import("resource://gre/modules/systemlibs.js", sysLibs);
|
||||
let productDevice = sysLibs.libcutils.property_get("ro.product.device");
|
||||
let buildType = sysLibs.libcutils.property_get("ro.build.type");
|
||||
url = url.replace(/%PRODUCT_MODEL%/g,
|
||||
sysLibs.libcutils.property_get("ro.product.model"));
|
||||
if (buildType == "user" || buildType == "userdebug") {
|
||||
url = url.replace(/%PRODUCT_DEVICE%/g, productDevice);
|
||||
} else {
|
||||
url = url.replace(/%PRODUCT_DEVICE%/g, productDevice + "-" + buildType);
|
||||
}
|
||||
url = url.replace(/%B2G_VERSION%/g,
|
||||
Preferences.get(PREF_APP_B2G_VERSION, null));
|
||||
url = url.replace(/%IMEI%/g,
|
||||
Preferences.get(PREF_APP_UPDATE_IMEI_HASH, "default"));
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
};
|
||||
|
||||
/* Get the distribution pref values, from defaults only */
|
||||
function getDistributionPrefValue(aPrefName) {
|
||||
var prefValue = "default";
|
||||
|
||||
try {
|
||||
prefValue = Services.prefs.getDefaultBranch(null).getCharPref(aPrefName);
|
||||
} catch (e) {
|
||||
// use default when pref not found
|
||||
}
|
||||
|
||||
return prefValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the locale from the update.locale file for replacing %LOCALE% in the
|
||||
* update url. The update.locale file can be located in the application
|
||||
* directory or the GRE directory with preference given to it being located in
|
||||
* the application directory.
|
||||
*/
|
||||
XPCOMUtils.defineLazyGetter(UpdateUtils, "Locale", function() {
|
||||
let channel;
|
||||
let locale;
|
||||
for (let res of ['app', 'gre']) {
|
||||
channel = Services.io.newChannel2("resource://" + res + "/" + FILE_UPDATE_LOCALE,
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_NORMAL,
|
||||
Ci.nsIContentPolicy.TYPE_INTERNAL_XMLHTTPREQUEST);
|
||||
try {
|
||||
let inputStream = channel.open();
|
||||
locale = NetUtil.readInputStreamToString(inputStream, inputStream.available());
|
||||
} catch(e) {}
|
||||
if (locale)
|
||||
return locale.trim();
|
||||
}
|
||||
|
||||
Cu.reportError(FILE_UPDATE_LOCALE + " file doesn't exist in either the " +
|
||||
"application or GRE directories");
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
/* Windows only getter that returns the processor architecture. */
|
||||
XPCOMUtils.defineLazyGetter(this, "gWinCPUArch", function aus_gWinCPUArch() {
|
||||
// Get processor architecture
|
||||
let arch = "unknown";
|
||||
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = false;
|
||||
try {
|
||||
kernel32 = ctypes.open("Kernel32");
|
||||
} catch (e) {
|
||||
Cu.reportError("Unable to open kernel32! Exception: " + e);
|
||||
}
|
||||
|
||||
if (kernel32) {
|
||||
try {
|
||||
let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
|
||||
ctypes.default_abi,
|
||||
ctypes.void_t,
|
||||
SYSTEM_INFO.ptr);
|
||||
let winSystemInfo = SYSTEM_INFO();
|
||||
// Default to unknown
|
||||
winSystemInfo.wProcessorArchitecture = 0xffff;
|
||||
|
||||
GetNativeSystemInfo(winSystemInfo.address());
|
||||
switch (winSystemInfo.wProcessorArchitecture) {
|
||||
case 9:
|
||||
arch = "x64";
|
||||
break;
|
||||
case 6:
|
||||
arch = "IA64";
|
||||
break;
|
||||
case 0:
|
||||
arch = "x86";
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError("Error getting processor architecture. " +
|
||||
"Exception: " + e);
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
|
||||
return arch;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(UpdateUtils, "ABI", function() {
|
||||
let abi = null;
|
||||
try {
|
||||
abi = Services.appinfo.XPCOMABI;
|
||||
}
|
||||
catch (e) {
|
||||
Cu.reportError("XPCOM ABI unknown");
|
||||
}
|
||||
|
||||
if (AppConstants.platform == "macosx") {
|
||||
// Mac universal build should report a different ABI than either macppc
|
||||
// or mactel.
|
||||
let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
|
||||
getService(Ci.nsIMacUtils);
|
||||
|
||||
if (macutils.isUniversalBinary) {
|
||||
abi += "-u-" + macutils.architecturesInBinary;
|
||||
}
|
||||
} else if (AppConstants.platform == "win") {
|
||||
// Windows build should report the CPU architecture that it's running on.
|
||||
abi += "-" + gWinCPUArch;
|
||||
}
|
||||
return abi;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(UpdateUtils, "OSVersion", function() {
|
||||
let osVersion;
|
||||
try {
|
||||
osVersion = Services.sysinfo.getProperty("name") + " " +
|
||||
Services.sysinfo.getProperty("version");
|
||||
}
|
||||
catch (e) {
|
||||
Cu.reportError("OS Version unknown.");
|
||||
}
|
||||
|
||||
if (osVersion) {
|
||||
if (AppConstants.platform == "win") {
|
||||
const BYTE = ctypes.uint8_t;
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
const WCHAR = ctypes.char16_t;
|
||||
const BOOL = ctypes.int;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
|
||||
const SZCSDVERSIONLENGTH = 128;
|
||||
const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
|
||||
[
|
||||
{dwOSVersionInfoSize: DWORD},
|
||||
{dwMajorVersion: DWORD},
|
||||
{dwMinorVersion: DWORD},
|
||||
{dwBuildNumber: DWORD},
|
||||
{dwPlatformId: DWORD},
|
||||
{szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
|
||||
{wServicePackMajor: WORD},
|
||||
{wServicePackMinor: WORD},
|
||||
{wSuiteMask: WORD},
|
||||
{wProductType: BYTE},
|
||||
{wReserved: BYTE}
|
||||
]);
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = false;
|
||||
try {
|
||||
kernel32 = ctypes.open("Kernel32");
|
||||
} catch (e) {
|
||||
Cu.reportError("Unable to open kernel32! " + e);
|
||||
osVersion += ".unknown (unknown)";
|
||||
}
|
||||
|
||||
if (kernel32) {
|
||||
try {
|
||||
// Get Service pack info
|
||||
try {
|
||||
let GetVersionEx = kernel32.declare("GetVersionExW",
|
||||
ctypes.default_abi,
|
||||
BOOL,
|
||||
OSVERSIONINFOEXW.ptr);
|
||||
let winVer = OSVERSIONINFOEXW();
|
||||
winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
|
||||
|
||||
if(0 !== GetVersionEx(winVer.address())) {
|
||||
osVersion += "." + winVer.wServicePackMajor +
|
||||
"." + winVer.wServicePackMinor;
|
||||
} else {
|
||||
Cu.reportError("Unknown failure in GetVersionEX (returned 0)");
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError("Error getting service pack information. Exception: " + e);
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
|
||||
// Add processor architecture
|
||||
osVersion += " (" + gWinCPUArch + ")";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
|
||||
}
|
||||
catch (e) {
|
||||
// Not all platforms have a secondary widget library, so an error is nothing to worry about.
|
||||
}
|
||||
osVersion = encodeURIComponent(osVersion);
|
||||
}
|
||||
return osVersion;
|
||||
});
|
|
@ -75,7 +75,7 @@ EXTRA_JS_MODULES += [
|
|||
'TelemetryTimestamps.jsm',
|
||||
'Timer.jsm',
|
||||
'Troubleshoot.jsm',
|
||||
'UpdateChannel.jsm',
|
||||
'UpdateUtils.jsm',
|
||||
'WebChannel.jsm',
|
||||
'WindowDraggingUtils.jsm',
|
||||
'ZipUtils.jsm',
|
||||
|
|
|
@ -11,6 +11,9 @@ Cu.import("resource://gre/modules/FileUtils.jsm");
|
|||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm")
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
let { computeHash } = Cu.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
|
||||
|
||||
do_get_profile();
|
||||
|
||||
|
@ -430,7 +433,7 @@ function* test_checkForAddons_installAddon(id, includeSize, wantInstallReject) {
|
|||
let data = "e~=0.5772156649";
|
||||
let zipFile = createNewZipFile(zipFileName, data);
|
||||
let hashFunc = "sha256";
|
||||
let expectedDigest = yield GMPDownloader.computeHash(hashFunc, zipFile);
|
||||
let expectedDigest = yield computeHash(hashFunc, zipFile.path);
|
||||
let fileSize = zipFile.fileSize;
|
||||
if (wantInstallReject) {
|
||||
fileSize = 1;
|
||||
|
@ -456,7 +459,6 @@ function* test_checkForAddons_installAddon(id, includeSize, wantInstallReject) {
|
|||
let gmpAddon = gmpAddons[0];
|
||||
do_check_false(gmpAddon.isInstalled);
|
||||
|
||||
GMPInstallManager.overrideLeaveDownloadedZip = true;
|
||||
try {
|
||||
let extractedPaths = yield installManager.installAddon(gmpAddon);
|
||||
if (wantInstallReject) {
|
||||
|
@ -474,14 +476,6 @@ function* test_checkForAddons_installAddon(id, includeSize, wantInstallReject) {
|
|||
let readData = readStringFromFile(extractedFile);
|
||||
do_check_eq(readData, data);
|
||||
|
||||
// Check that the downloaded zip matches the offered zip exactly
|
||||
let downloadedGMPFile = FileUtils.getFile("TmpD",
|
||||
[gmpAddon.id + ".zip"]);
|
||||
do_check_true(downloadedGMPFile.exists());
|
||||
let downloadedBytes = getBinaryFileData(downloadedGMPFile);
|
||||
let sourceBytes = getBinaryFileData(zipFile);
|
||||
do_check_true(compareBinaryData(downloadedBytes, sourceBytes));
|
||||
|
||||
// Make sure the prefs are set correctly
|
||||
do_check_true(!!GMPScope.GMPPrefs.get(
|
||||
GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "", gmpAddon.id));
|
||||
|
@ -490,7 +484,7 @@ function* test_checkForAddons_installAddon(id, includeSize, wantInstallReject) {
|
|||
"1.1");
|
||||
do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, "",
|
||||
gmpAddon.id),
|
||||
GMPScope.GMPUtils.ABI());
|
||||
UpdateUtils.ABI);
|
||||
// Make sure it reports as being installed
|
||||
do_check_true(gmpAddon.isInstalled);
|
||||
|
||||
|
@ -498,16 +492,9 @@ function* test_checkForAddons_installAddon(id, includeSize, wantInstallReject) {
|
|||
extractedFile.parent.remove(true);
|
||||
zipFile.remove(false);
|
||||
httpServer.stop(function() {});
|
||||
do_print("Removing downloaded GMP file: " + downloadedGMPFile.path);
|
||||
downloadedGMPFile.remove(false);
|
||||
installManager.uninit();
|
||||
} catch(ex) {
|
||||
zipFile.remove(false);
|
||||
let downloadedGMPFile = FileUtils.getFile("TmpD",
|
||||
[gmpAddon.id + ".zip"]);
|
||||
do_print("Removing downloaded GMP file from exception handler: " +
|
||||
downloadedGMPFile.path);
|
||||
downloadedGMPFile.remove(false);
|
||||
if (!wantInstallReject) {
|
||||
do_throw("install update should not reject");
|
||||
}
|
||||
|
@ -798,45 +785,6 @@ function overrideXHR(status, response, options) {
|
|||
return overrideXHR.myxhr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares binary data of 2 arrays and returns true if they are the same
|
||||
*
|
||||
* @param arr1 The first array to compare
|
||||
* @param arr2 The second array to compare
|
||||
*/
|
||||
function compareBinaryData(arr1, arr2) {
|
||||
do_check_eq(arr1.length, arr2.length);
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
if (arr1[i] != arr2[i]) {
|
||||
do_print("Data differs at index " + i +
|
||||
", arr1: " + arr1[i] + ", arr2: " + arr2[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a file's data and returns it
|
||||
*
|
||||
* @param file The file to read the data from
|
||||
* @return array of bytes for the data in the file.
|
||||
*/
|
||||
function getBinaryFileData(file) {
|
||||
let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
// Open as RD_ONLY with default permissions.
|
||||
fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
|
||||
|
||||
// Check the returned size versus the expected size.
|
||||
let stream = Cc["@mozilla.org/binaryinputstream;1"].
|
||||
createInstance(Ci.nsIBinaryInputStream);
|
||||
stream.setInputStream(fileStream);
|
||||
let bytes = stream.readByteArray(stream.available());
|
||||
fileStream.close();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new zip file containing a file with the specified data
|
||||
* @param zipName The name of the zip file
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
* 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/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Preferences.jsm");
|
||||
Components.utils.import("resource://gre/modules/UpdateChannel.jsm");
|
||||
const { utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
|
||||
const TEST_CHANNEL = "TestChannel";
|
||||
|
@ -13,24 +15,24 @@ const TEST_PARTNER_A = "TestPartnerA";
|
|||
const PREF_PARTNER_B = "app.partner.test_partner_b";
|
||||
const TEST_PARTNER_B = "TestPartnerB";
|
||||
|
||||
function test_get() {
|
||||
add_task(function* test_updatechannel() {
|
||||
let defaultPrefs = new Preferences({ defaultBranch: true });
|
||||
let currentChannel = defaultPrefs.get(PREF_APP_UPDATE_CHANNEL);
|
||||
|
||||
do_check_eq(UpdateChannel.get(), currentChannel);
|
||||
do_check_eq(UpdateChannel.get(false), currentChannel);
|
||||
do_check_eq(UpdateUtils.UpdateChannel, currentChannel);
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(true), currentChannel);
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(false), currentChannel);
|
||||
|
||||
defaultPrefs.set(PREF_APP_UPDATE_CHANNEL, TEST_CHANNEL);
|
||||
do_check_eq(UpdateChannel.get(), TEST_CHANNEL);
|
||||
do_check_eq(UpdateChannel.get(false), TEST_CHANNEL);
|
||||
do_check_eq(UpdateUtils.UpdateChannel, TEST_CHANNEL);
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(true), TEST_CHANNEL);
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL);
|
||||
|
||||
defaultPrefs.set(PREF_PARTNER_A, TEST_PARTNER_A);
|
||||
defaultPrefs.set(PREF_PARTNER_B, TEST_PARTNER_B);
|
||||
do_check_eq(UpdateChannel.get(),
|
||||
do_check_eq(UpdateUtils.UpdateChannel,
|
||||
TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
|
||||
do_check_eq(UpdateChannel.get(false), TEST_CHANNEL);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
test_get();
|
||||
}
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(true),
|
||||
TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL);
|
||||
});
|
|
@ -0,0 +1,292 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://testing-common/AppInfo.jsm");
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
|
||||
const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
|
||||
const PREF_APP_PARTNER_BRANCH = "app.partner.";
|
||||
const PREF_DISTRIBUTION_ID = "distribution.id";
|
||||
const PREF_DISTRIBUTION_VERSION = "distribution.version";
|
||||
|
||||
const URL_PREFIX = "http://localhost/";
|
||||
|
||||
const MSG_SHOULD_EQUAL = " should equal the expected value";
|
||||
|
||||
updateAppInfo();
|
||||
const gAppInfo = getAppInfo();
|
||||
const gDefaultPrefBranch = Services.prefs.getDefaultBranch(null);
|
||||
|
||||
function setUpdateChannel(aChannel) {
|
||||
gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, aChannel);
|
||||
}
|
||||
|
||||
function getServicePack() {
|
||||
// NOTE: This function is a helper function and not a test. Thus,
|
||||
// it uses throw() instead of do_throw(). Any tests that use this function
|
||||
// should catch exceptions thrown in this function and deal with them
|
||||
// appropriately (usually by calling do_throw).
|
||||
const BYTE = ctypes.uint8_t;
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
const WCHAR = ctypes.char16_t;
|
||||
const BOOL = ctypes.int;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
|
||||
const SZCSDVERSIONLENGTH = 128;
|
||||
const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
|
||||
[
|
||||
{dwOSVersionInfoSize: DWORD},
|
||||
{dwMajorVersion: DWORD},
|
||||
{dwMinorVersion: DWORD},
|
||||
{dwBuildNumber: DWORD},
|
||||
{dwPlatformId: DWORD},
|
||||
{szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
|
||||
{wServicePackMajor: WORD},
|
||||
{wServicePackMinor: WORD},
|
||||
{wSuiteMask: WORD},
|
||||
{wProductType: BYTE},
|
||||
{wReserved: BYTE}
|
||||
]);
|
||||
|
||||
let kernel32 = ctypes.open("kernel32");
|
||||
try {
|
||||
let GetVersionEx = kernel32.declare("GetVersionExW",
|
||||
ctypes.default_abi,
|
||||
BOOL,
|
||||
OSVERSIONINFOEXW.ptr);
|
||||
let winVer = OSVERSIONINFOEXW();
|
||||
winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
|
||||
|
||||
if (0 === GetVersionEx(winVer.address())) {
|
||||
// Using "throw" instead of "do_throw" (see NOTE above)
|
||||
throw("Failure in GetVersionEx (returned 0)");
|
||||
}
|
||||
|
||||
return winVer.wServicePackMajor + "." + winVer.wServicePackMinor;
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
|
||||
function getProcArchitecture() {
|
||||
// NOTE: This function is a helper function and not a test. Thus,
|
||||
// it uses throw() instead of do_throw(). Any tests that use this function
|
||||
// should catch exceptions thrown in this function and deal with them
|
||||
// appropriately (usually by calling do_throw).
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = ctypes.open("kernel32");
|
||||
try {
|
||||
let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
|
||||
ctypes.default_abi,
|
||||
ctypes.void_t,
|
||||
SYSTEM_INFO.ptr);
|
||||
let sysInfo = SYSTEM_INFO();
|
||||
// Default to unknown
|
||||
sysInfo.wProcessorArchitecture = 0xffff;
|
||||
|
||||
GetNativeSystemInfo(sysInfo.address());
|
||||
switch(sysInfo.wProcessorArchitecture) {
|
||||
case 9:
|
||||
return "x64";
|
||||
case 6:
|
||||
return "IA64";
|
||||
case 0:
|
||||
return "x86";
|
||||
default:
|
||||
// Using "throw" instead of "do_throw" (see NOTE above)
|
||||
throw("Unknown architecture returned from GetNativeSystemInfo: " + sysInfo.wProcessorArchitecture);
|
||||
}
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function for formatting a url and getting the result we're
|
||||
// interested in
|
||||
function getResult(url) {
|
||||
url = UpdateUtils.formatUpdateURL(url);
|
||||
return url.substr(URL_PREFIX.length).split("/")[0];
|
||||
}
|
||||
|
||||
// url constructed with %PRODUCT%
|
||||
add_task(function* test_product() {
|
||||
let url = URL_PREFIX + "%PRODUCT%/";
|
||||
Assert.equal(getResult(url), gAppInfo.name,
|
||||
"the url param for %PRODUCT%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %VERSION%
|
||||
add_task(function* test_version() {
|
||||
let url = URL_PREFIX + "%VERSION%/";
|
||||
Assert.equal(getResult(url), gAppInfo.version,
|
||||
"the url param for %VERSION%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %BUILD_ID%
|
||||
add_task(function* test_build_id() {
|
||||
let url = URL_PREFIX + "%BUILD_ID%/";
|
||||
Assert.equal(getResult(url), gAppInfo.appBuildID,
|
||||
"the url param for %BUILD_ID%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %BUILD_TARGET%
|
||||
// XXX TODO - it might be nice if we tested the actual ABI
|
||||
add_task(function* test_build_target() {
|
||||
let url = URL_PREFIX + "%BUILD_TARGET%/";
|
||||
|
||||
let abi;
|
||||
try {
|
||||
abi = gAppInfo.XPCOMABI;
|
||||
} catch (e) {
|
||||
do_throw("nsIXULAppInfo:XPCOMABI not defined\n");
|
||||
}
|
||||
|
||||
if (AppConstants.platform == "macosx") {
|
||||
// Mac universal build should report a different ABI than either macppc
|
||||
// or mactel. This is necessary since nsUpdateService.js will set the ABI to
|
||||
// Universal-gcc3 for Mac universal builds.
|
||||
let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
|
||||
getService(Ci.nsIMacUtils);
|
||||
|
||||
if (macutils.isUniversalBinary) {
|
||||
abi += "-u-" + macutils.architecturesInBinary;
|
||||
}
|
||||
} else if (AppConstants.platform == "win") {
|
||||
// Windows build should report the CPU architecture that it's running on.
|
||||
abi += "-" + getProcArchitecture();
|
||||
}
|
||||
|
||||
Assert.equal(getResult(url), gAppInfo.OS + "_" + abi,
|
||||
"the url param for %BUILD_TARGET%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %LOCALE%
|
||||
// Bug 488936 added the update.locale file that stores the update locale
|
||||
add_task(function* test_locale() {
|
||||
// The code that gets the locale accesses the profile which is only available
|
||||
// after calling do_get_profile in xpcshell tests. This prevents an error from
|
||||
// being logged.
|
||||
do_get_profile();
|
||||
|
||||
let url = URL_PREFIX + "%LOCALE%/";
|
||||
Assert.equal(getResult(url), AppConstants.INSTALL_LOCALE,
|
||||
"the url param for %LOCALE%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %CHANNEL%
|
||||
add_task(function* test_channel() {
|
||||
let url = URL_PREFIX + "%CHANNEL%/";
|
||||
setUpdateChannel("test_channel");
|
||||
Assert.equal(getResult(url), "test_channel",
|
||||
"the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %CHANNEL% with distribution partners
|
||||
add_task(function* test_channel_distribution() {
|
||||
let url = URL_PREFIX + "%CHANNEL%/";
|
||||
gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner1",
|
||||
"test_partner1");
|
||||
gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner2",
|
||||
"test_partner2");
|
||||
Assert.equal(getResult(url),
|
||||
"test_channel-cck-test_partner1-test_partner2",
|
||||
"the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %PLATFORM_VERSION%
|
||||
add_task(function* test_platform_version() {
|
||||
let url = URL_PREFIX + "%PLATFORM_VERSION%/";
|
||||
Assert.equal(getResult(url), gAppInfo.platformVersion,
|
||||
"the url param for %PLATFORM_VERSION%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %OS_VERSION%
|
||||
add_task(function* test_os_version() {
|
||||
let url = URL_PREFIX + "%OS_VERSION%/";
|
||||
let osVersion;
|
||||
let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
|
||||
osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
|
||||
|
||||
if (AppConstants.platform == "win") {
|
||||
try {
|
||||
let servicePack = getServicePack();
|
||||
osVersion += "." + servicePack;
|
||||
} catch (e) {
|
||||
do_throw("Failure obtaining service pack: " + e);
|
||||
}
|
||||
|
||||
if ("5.0" === sysInfo.getProperty("version")) { // Win2K
|
||||
osVersion += " (unknown)";
|
||||
} else {
|
||||
try {
|
||||
osVersion += " (" + getProcArchitecture() + ")";
|
||||
} catch (e) {
|
||||
do_throw("Failed to obtain processor architecture: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (osVersion) {
|
||||
try {
|
||||
osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
|
||||
} catch (e) {
|
||||
// Not all platforms have a secondary widget library, so an error is
|
||||
// nothing to worry about.
|
||||
}
|
||||
osVersion = encodeURIComponent(osVersion);
|
||||
}
|
||||
|
||||
Assert.equal(getResult(url), osVersion,
|
||||
"the url param for %OS_VERSION%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %DISTRIBUTION%
|
||||
add_task(function* test_distribution() {
|
||||
let url = URL_PREFIX + "%DISTRIBUTION%/";
|
||||
gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_ID, "test_distro");
|
||||
Assert.equal(getResult(url), "test_distro",
|
||||
"the url param for %DISTRIBUTION%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %DISTRIBUTION_VERSION%
|
||||
add_task(function* test_distribution_version() {
|
||||
let url = URL_PREFIX + "%DISTRIBUTION_VERSION%/";
|
||||
gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_VERSION, "test_distro_version");
|
||||
Assert.equal(getResult(url), "test_distro_version",
|
||||
"the url param for %DISTRIBUTION_VERSION%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
add_task(function* test_custom() {
|
||||
Services.prefs.setCharPref("app.update.custom", "custom");
|
||||
let url = URL_PREFIX + "%CUSTOM%/";
|
||||
Assert.equal(getResult(url), "custom",
|
||||
"the url query string for %CUSTOM%" + MSG_SHOULD_EQUAL);
|
||||
});
|
|
@ -57,6 +57,8 @@ skip-if = toolkit == 'android'
|
|||
skip-if = toolkit == 'android'
|
||||
[test_timer.js]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_UpdateUtils_url.js]
|
||||
[test_UpdateUtils_updatechannel.js]
|
||||
[test_web_channel.js]
|
||||
[test_web_channel_broker.js]
|
||||
[test_ZipUtils.js]
|
||||
|
|
|
@ -859,6 +859,13 @@ var AddonManagerInternal = {
|
|||
logger.debug(`Provider finished startup: ${providerName(aProvider)}`);
|
||||
},
|
||||
|
||||
_getProviderByName(aName) {
|
||||
for (let provider of this.providers) {
|
||||
if (providerName(provider) == aName)
|
||||
return provider;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes the AddonManager, loading any known providers and initializing
|
||||
* them.
|
||||
|
@ -1464,9 +1471,9 @@ var AddonManagerInternal = {
|
|||
let buPromise = Task.spawn(function* backgroundUpdateTask() {
|
||||
let hotfixID = this.hotfixID;
|
||||
|
||||
let checkHotfix = hotfixID &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
|
||||
let appUpdateEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
|
||||
let checkHotfix = hotfixID && appUpdateEnabled;
|
||||
|
||||
logger.debug("Background update check beginning");
|
||||
|
||||
|
@ -1613,6 +1620,15 @@ var AddonManagerInternal = {
|
|||
}
|
||||
}
|
||||
|
||||
if (appUpdateEnabled) {
|
||||
try {
|
||||
yield AddonManagerInternal._getProviderByName("XPIProvider").updateSystemAddons();
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn("Failed to update system addons", e);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Background update check complete");
|
||||
Services.obs.notifyObservers(null,
|
||||
"addons-background-update-complete",
|
||||
|
|
|
@ -19,6 +19,7 @@ Cu.import("resource://gre/modules/Log.jsm");
|
|||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/GMPUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(
|
||||
this, "GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm");
|
||||
|
@ -490,8 +491,8 @@ GMPWrapper.prototype = {
|
|||
return { installed: false, valid: true };
|
||||
}
|
||||
|
||||
let abi = GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ABI, GMPUtils.ABI(), this._plugin.id);
|
||||
if (abi != GMPUtils.ABI()) {
|
||||
let abi = GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ABI, UpdateUtils.ABI, this._plugin.id);
|
||||
if (abi != UpdateUtils.ABI) {
|
||||
// ABI doesn't match. Possibly this is a profile migrated across platforms
|
||||
// or from 32 -> 64 bit.
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,322 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "ProductAddonChecker" ];
|
||||
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/CertUtils.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
let logger = Log.repository.getLogger("addons.productaddons");
|
||||
|
||||
/**
|
||||
* Number of milliseconds after which we need to cancel `downloadXML`.
|
||||
*
|
||||
* Bug 1087674 suggests that the XHR we use in `downloadXML` may
|
||||
* never terminate in presence of network nuisances (e.g. strange
|
||||
* antivirus behavior). This timeout is a defensive measure to ensure
|
||||
* that we fail cleanly in such case.
|
||||
*/
|
||||
const TIMEOUT_DELAY_MS = 20000;
|
||||
// Chunk size for the incremental downloader
|
||||
const DOWNLOAD_CHUNK_BYTES_SIZE = 300000;
|
||||
// Incremental downloader interval
|
||||
const DOWNLOAD_INTERVAL = 0;
|
||||
// How much of a file to read into memory at a time for hashing
|
||||
const HASH_CHUNK_SIZE = 8192;
|
||||
|
||||
/**
|
||||
* Gets the status of an XMLHttpRequest either directly or from its underlying
|
||||
* channel.
|
||||
*
|
||||
* @param request
|
||||
* The XMLHttpRequest.
|
||||
* @return an integer status value.
|
||||
*/
|
||||
function getRequestStatus(request) {
|
||||
let status = null;
|
||||
try {
|
||||
status = request.status;
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
if (status != null) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return request.channel.QueryInterface(Ci.nsIRequest).status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads an XML document from a URL optionally testing the SSL certificate
|
||||
* for certain attributes.
|
||||
*
|
||||
* @param url
|
||||
* The url to download from.
|
||||
* @param allowNonBuiltIn
|
||||
* Whether to trust SSL certificates without a built-in CA issuer.
|
||||
* @param allowedCerts
|
||||
* The list of certificate attributes to match the SSL certificate
|
||||
* against or null to skip checks.
|
||||
* @return a promise that resolves to the DOM document downloaded or rejects
|
||||
* with a JS exception in case of error.
|
||||
*/
|
||||
function downloadXML(url, allowNonBuiltIn = false, allowedCerts = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsISupports);
|
||||
// This is here to let unit test code override XHR
|
||||
if (request.wrappedJSObject) {
|
||||
request = request.wrappedJSObject;
|
||||
}
|
||||
request.open("GET", url, true);
|
||||
request.channel.notificationCallbacks = new BadCertHandler(allowNonBuiltIn);
|
||||
// Prevent the request from reading from the cache.
|
||||
request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
// Prevent the request from writing to the cache.
|
||||
request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
request.timeout = TIMEOUT_DELAY_MS;
|
||||
|
||||
request.overrideMimeType("text/xml");
|
||||
// The Cache-Control header is only interpreted by proxies and the
|
||||
// final destination. It does not help if a resource is already
|
||||
// cached locally.
|
||||
request.setRequestHeader("Cache-Control", "no-cache");
|
||||
// HTTP/1.0 servers might not implement Cache-Control and
|
||||
// might only implement Pragma: no-cache
|
||||
request.setRequestHeader("Pragma", "no-cache");
|
||||
|
||||
let fail = (event) => {
|
||||
let request = event.target;
|
||||
let status = getRequestStatus(request);
|
||||
let message = "Failed downloading XML, status: " + status + ", reason: " + event.type;
|
||||
logger.warn(message);
|
||||
let ex = new Error(message);
|
||||
ex.status = status;
|
||||
reject(ex);
|
||||
};
|
||||
|
||||
let success = (event) => {
|
||||
logger.info("Completed downloading document");
|
||||
let request = event.target;
|
||||
|
||||
try {
|
||||
checkCert(request.channel, allowNonBuiltIn, allowedCerts);
|
||||
} catch (ex) {
|
||||
logger.error("Request failed certificate checks: " + ex);
|
||||
ex.status = getRequestStatus(request);
|
||||
reject(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(request.responseXML);
|
||||
};
|
||||
|
||||
request.addEventListener("error", fail, false);
|
||||
request.addEventListener("abort", fail, false);
|
||||
request.addEventListener("timeout", fail, false);
|
||||
request.addEventListener("load", success, false);
|
||||
|
||||
logger.info("sending request to: " + url);
|
||||
request.send(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a list of add-ons from a DOM document.
|
||||
*
|
||||
* @param document
|
||||
* The DOM document to parse.
|
||||
* @return null if there is no <addons> element otherwise an array of the addons
|
||||
* listed.
|
||||
*/
|
||||
function parseXML(document) {
|
||||
// Check that the root element is correct
|
||||
if (document.documentElement.localName != "updates") {
|
||||
throw new Error("got node name: " + document.documentElement.localName +
|
||||
", expected: updates");
|
||||
}
|
||||
|
||||
// Check if there are any addons elements in the updates element
|
||||
let addons = document.querySelector("updates:root > addons");
|
||||
if (!addons) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let results = [];
|
||||
let addonList = document.querySelectorAll("updates:root > addons > addon");
|
||||
for (let addonElement of addonList) {
|
||||
let addon = {};
|
||||
|
||||
for (let name of ["id", "URL", "hashFunction", "hashValue", "version", "size"]) {
|
||||
if (addonElement.hasAttribute(name)) {
|
||||
addon[name] = addonElement.getAttribute(name);
|
||||
}
|
||||
}
|
||||
addon.size = Number(addon.size) || undefined;
|
||||
|
||||
results.push(addon);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads file from a URL using the incremental file downloader.
|
||||
*
|
||||
* @param url
|
||||
* The url to download from.
|
||||
* @return a promise that resolves to the path of a temporary file or rejects
|
||||
* with a JS exception in case of error.
|
||||
*/
|
||||
function downloadFile(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let observer = {
|
||||
onStartRequest: function() {},
|
||||
|
||||
onStopRequest: function(request, context, status) {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
logger.warn("File download failed: 0x" + status.toString(16));
|
||||
tmpFile.remove(true);
|
||||
reject(Components.Exception("File download failed", status));
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(tmpFile.path);
|
||||
}
|
||||
};
|
||||
|
||||
let uri = NetUtil.newURI(url);
|
||||
let request = Cc["@mozilla.org/network/incremental-download;1"].
|
||||
createInstance(Ci.nsIIncrementalDownload);
|
||||
let tmpFile = FileUtils.getFile("TmpD", ["tmpaddon"]);
|
||||
tmpFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
|
||||
|
||||
logger.info("Downloading from " + uri.spec + " to " + tmpFile.path);
|
||||
request.init(uri, tmpFile, DOWNLOAD_CHUNK_BYTES_SIZE, DOWNLOAD_INTERVAL);
|
||||
request.start(observer, null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string containing binary values to hex.
|
||||
*/
|
||||
function binaryToHex(input) {
|
||||
let result = "";
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
let hex = input.charCodeAt(i).toString(16);
|
||||
if (hex.length == 1) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
result += hex;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the hash of a file.
|
||||
*
|
||||
* @param hashFunction
|
||||
* The type of hash function to use, must be supported by nsICryptoHash.
|
||||
* @param path
|
||||
* The path of the file to hash.
|
||||
* @return a promise that resolves to hash of the file or rejects with a JS
|
||||
* exception in case of error.
|
||||
*/
|
||||
let computeHash = Task.async(function*(hashFunction, path) {
|
||||
let file = yield OS.File.open(path, { existing: true, read: true });
|
||||
try {
|
||||
let hasher = Cc["@mozilla.org/security/hash;1"].
|
||||
createInstance(Ci.nsICryptoHash);
|
||||
hasher.initWithString(hashFunction);
|
||||
|
||||
let bytes;
|
||||
do {
|
||||
bytes = yield file.read(HASH_CHUNK_SIZE);
|
||||
hasher.update(bytes, bytes.length);
|
||||
} while (bytes.length == HASH_CHUNK_SIZE);
|
||||
|
||||
return binaryToHex(hasher.finish(false));
|
||||
}
|
||||
finally {
|
||||
yield file.close();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Verifies that a downloaded file matches what was expected.
|
||||
*
|
||||
* @param properties
|
||||
* The properties to check, `size` and `hashFunction` with `hashValue`
|
||||
* are supported. Any properties missing won't be checked.
|
||||
* @param path
|
||||
* The path of the file to check.
|
||||
* @return a promise that resolves if the file matched or rejects with a JS
|
||||
* exception in case of error.
|
||||
*/
|
||||
let verifyFile = Task.async(function*(properties, path) {
|
||||
if (properties.size !== undefined) {
|
||||
let stat = yield OS.File.stat(path);
|
||||
if (stat.size != properties.size) {
|
||||
throw new Error("Downloaded file was " + stat.size + " bytes but expected " + properties.size + " bytes.");
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.hashFunction !== undefined) {
|
||||
let expectedDigest = properties.hashValue.toLowerCase();
|
||||
let digest = yield computeHash(properties.hashFunction, path);
|
||||
if (digest != expectedDigest) {
|
||||
throw new Error("Hash was `" + digest + "` but expected `" + expectedDigest + "`.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ProductAddonChecker = {
|
||||
/**
|
||||
* Downloads a list of add-ons from a URL optionally testing the SSL
|
||||
* certificate for certain attributes.
|
||||
*
|
||||
* @param url
|
||||
* The url to download from.
|
||||
* @param allowNonBuiltIn
|
||||
* Whether to trust SSL certificates without a built-in CA issuer.
|
||||
* @param allowedCerts
|
||||
* The list of certificate attributes to match the SSL certificate
|
||||
* against or null to skip checks.
|
||||
* @return a promise that resolves to the list of add-ons or rejects with a JS
|
||||
* exception in case of error.
|
||||
*/
|
||||
getProductAddonList: function(url, allowNonBuiltIn = false, allowedCerts = null) {
|
||||
return downloadXML(url, allowNonBuiltIn, allowedCerts).then(parseXML);
|
||||
},
|
||||
|
||||
/**
|
||||
* Downloads an add-on to a local file and checks that it matches the expected
|
||||
* file. The caller is responsible for deleting the temporary file returned.
|
||||
*
|
||||
* @param addon
|
||||
* The addon to download.
|
||||
* @return a promise that resolves to the temporary file downloaded or rejects
|
||||
* with a JS exception in case of error.
|
||||
*/
|
||||
downloadAddon: Task.async(function*(addon) {
|
||||
let path = yield downloadFile(addon.URL);
|
||||
try {
|
||||
yield verifyFile(addon, path);
|
||||
return path;
|
||||
}
|
||||
catch (e) {
|
||||
yield OS.File.remove(path);
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
}
|
|
@ -42,6 +42,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "BrowserToolboxProcess",
|
|||
"resource:///modules/devtools/client/framework/ToolboxProcess.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
|
||||
"resource://gre/modules/devtools/shared/Console.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ProductAddonChecker",
|
||||
"resource://gre/modules/addons/ProductAddonChecker.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "Blocklist",
|
||||
"@mozilla.org/extensions/blocklist;1",
|
||||
|
@ -99,6 +103,7 @@ const PREF_BRANCH_INSTALLED_ADDON = "extensions.installedDistroAddon.";
|
|||
const PREF_SHOWN_SELECTION_UI = "extensions.shownSelectionUI";
|
||||
const PREF_INTERPOSITION_ENABLED = "extensions.interposition.enabled";
|
||||
const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
|
||||
const PREF_SYSTEM_ADDON_UPDATE_URL = "extensions.systemAddon.update.url";
|
||||
|
||||
const PREF_EM_MIN_COMPAT_APP_VERSION = "extensions.minCompatibleAppVersion";
|
||||
const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
|
||||
|
@ -310,6 +315,25 @@ LAZY_OBJECTS.forEach(name => {
|
|||
});
|
||||
|
||||
|
||||
// Behaves like Promise.all except waits for all promises to resolve/reject
|
||||
// before resolving/rejecting itself
|
||||
function waitForAllPromises(promises) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let shouldReject = false;
|
||||
let rejectValue = null;
|
||||
|
||||
let newPromises = [
|
||||
for (p of promises)
|
||||
p.catch(value => {
|
||||
shouldReject = true;
|
||||
rejectValue = value;
|
||||
})
|
||||
]
|
||||
Promise.all(newPromises)
|
||||
.then((results) => shouldReject ? reject(rejectValue) : resolve(results));
|
||||
});
|
||||
}
|
||||
|
||||
function findMatchingStaticBlocklistItem(aAddon) {
|
||||
for (let item of STATIC_BLOCKLIST_PATTERNS) {
|
||||
if ("creator" in item && typeof item.creator == "string") {
|
||||
|
@ -2759,6 +2783,91 @@ this.XPIProvider = {
|
|||
!XPIDatabase.writeAddonsList());
|
||||
},
|
||||
|
||||
updateSystemAddons: Task.async(function XPI_updateSystemAddons() {
|
||||
// Download the list of system add-ons
|
||||
let url = Preferences.get(PREF_SYSTEM_ADDON_UPDATE_URL, null);
|
||||
if (!url)
|
||||
return;
|
||||
|
||||
url = UpdateUtils.formatUpdateURL(url);
|
||||
|
||||
logger.info(`Starting system add-on update check from ${url}.`);
|
||||
let addonList = yield ProductAddonChecker.getProductAddonList(url);
|
||||
|
||||
// If there was no list then do nothing.
|
||||
if (!addonList) {
|
||||
logger.info("No system add-ons list was returned.");
|
||||
return;
|
||||
}
|
||||
|
||||
addonList = [for (spec of addonList) { spec, path: null, addon: null }];
|
||||
|
||||
// Bug 1204159: If this matches the current set in the profile or app locations
|
||||
// then just switch to those
|
||||
|
||||
let systemAddonLocation = XPIProvider.installLocationsByName[KEY_APP_SYSTEM_ADDONS];
|
||||
|
||||
// Download all the add-ons
|
||||
// Bug 1204158: If we already have some of these locally then just use those
|
||||
let downloadAddon = Task.async(function*(item) {
|
||||
try {
|
||||
item.path = yield ProductAddonChecker.downloadAddon(item.spec);
|
||||
item.addon = yield loadManifestFromFile(nsIFile(item.path), systemAddonLocation);
|
||||
}
|
||||
catch (e) {
|
||||
logger.error(`Failed to download system add-on ${item.spec.id}`, e);
|
||||
}
|
||||
});
|
||||
yield Promise.all([for (item of addonList) downloadAddon(item)]);
|
||||
|
||||
// The download promises all resolve regardless, now check if they all
|
||||
// succeeded
|
||||
let validateAddon = (item) => {
|
||||
if (item.spec.id != item.addon.id) {
|
||||
logger.warn(`Downloaded system add-on expected to be ${item.spec.id} but was ${item.addon.id}.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.spec.version != item.addon.version) {
|
||||
logger.warn(`Expected system add-on ${item.spec.id} to be version ${item.version} but was ${item.addon.version}.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!systemAddonLocation.isValidAddon(item.addon))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!addonList.every(item => item.path && item.addon && validateAddon(item))) {
|
||||
throw new Error("Rejecting updated system add-on set that either could not " +
|
||||
"be downloaded or contained unusable add-ons.");
|
||||
}
|
||||
|
||||
// Install into the install location
|
||||
logger.info("Installing new system add-on set");
|
||||
yield systemAddonLocation.installAddonSet([for (item of addonList) item.addon]);
|
||||
|
||||
// Bug 1204156: Switch to the new system add-ons without requiring a restart
|
||||
}
|
||||
finally {
|
||||
// Delete the temporary files
|
||||
logger.info("Deleting temporary files");
|
||||
for (let item of addonList) {
|
||||
// If this item downloaded delete the temporary file.
|
||||
if (item.path) {
|
||||
try {
|
||||
yield OS.File.remove(item.path);
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn(`Failed to remove temporary file ${item.path}.`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Verifies that all installed add-ons are still correctly signed.
|
||||
*/
|
||||
|
@ -7356,40 +7465,110 @@ Object.assign(SystemAddonInstallLocation.prototype, {
|
|||
return this._directory != null;
|
||||
},
|
||||
|
||||
isValidAddon: function(aAddon) {
|
||||
if (aAddon.appDisabled) {
|
||||
logger.warn(`System add-on ${aAddon.id} isn't compatible with the application.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aAddon.unpack) {
|
||||
logger.warn(`System add-on ${aAddon.id} isn't a packed add-on.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aAddon.bootstrap) {
|
||||
logger.warn(`System add-on ${aAddon.id} isn't restartless.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Tests whether the loaded add-on information matches what is expected.
|
||||
*/
|
||||
isValid: function(aAddons) {
|
||||
for (let id of Object.keys(this._addonSet.addons)) {
|
||||
if (!aAddons.has(id)) {
|
||||
logger.warn("Expected add-on " + id + " is missing from the system add-on location.");
|
||||
logger.warn(`Expected add-on ${id} is missing from the system add-on location.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
let addon = aAddons.get(id);
|
||||
if (addon.appDisabled) {
|
||||
logger.warn("System add-on " + id + " isn't compatible with the application.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (addon.unpack) {
|
||||
logger.warn("System add-on " + id + " isn't a packed add-on.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!addon.bootstrap) {
|
||||
logger.warn("System add-on " + id + " isn't restartless.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (addon.version != this._addonSet.addons[id].version) {
|
||||
logger.warn("System add-on " + id + " wasn't the correct version.");
|
||||
logger.warn(`Expected system add-on ${id} to be version ${this._addonSet.addons[id].version} but was ${addon.version}.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.isValidAddon(addon))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Installs a new set of system add-ons into the location and updates the
|
||||
* add-on set in prefs. We wait to switch state until a restart.
|
||||
*/
|
||||
installAddonSet: Task.async(function(aAddons) {
|
||||
// Make sure the base dir exists
|
||||
yield OS.File.makeDir(this._baseDir.path, { ignoreExisting: true });
|
||||
|
||||
let newDir = this._baseDir.clone();
|
||||
|
||||
let uuidGen = Cc["@mozilla.org/uuid-generator;1"].
|
||||
getService(Ci.nsIUUIDGenerator);
|
||||
newDir.append("blank");
|
||||
|
||||
while (true) {
|
||||
newDir.leafName = uuidGen.generateUUID().toString();
|
||||
|
||||
try {
|
||||
yield OS.File.makeDir(newDir.path, { ignoreExisting: false });
|
||||
break;
|
||||
}
|
||||
catch (e) {
|
||||
// Directory already exists, pick another
|
||||
}
|
||||
}
|
||||
|
||||
let copyAddon = Task.async(function*(addon) {
|
||||
let target = OS.Path.join(newDir.path, addon.id + ".xpi");
|
||||
logger.info(`Copying ${addon.id} from ${addon._sourceBundle.path} to ${target}.`);
|
||||
try {
|
||||
yield OS.File.copy(addon._sourceBundle.path, target);
|
||||
}
|
||||
catch (e) {
|
||||
logger.error(`Failed to copy ${addon.id} from ${addon._sourceBundle.path} to ${target}.`, e);
|
||||
throw e;
|
||||
}
|
||||
addon._sourceBundle = new nsIFile(target);
|
||||
});
|
||||
|
||||
try {
|
||||
yield waitForAllPromises([for (addon of aAddons) copyAddon(addon)]);
|
||||
}
|
||||
catch (e) {
|
||||
try {
|
||||
yield OS.File.removeDir(newDir.path, { ignorePermissions: true });
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn(`Failed to remove new system add-on directory ${newDir.path}.`, e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
// All add-ons in position, create the new state and store it in prefs
|
||||
let state = { schema: 1, directory: newDir.leafName, addons: {} };
|
||||
for (let addon of aAddons) {
|
||||
state.addons[addon.id] = {
|
||||
version: addon.version
|
||||
}
|
||||
}
|
||||
|
||||
this._saveAddonSet(state);
|
||||
}),
|
||||
});
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
|
|
@ -12,6 +12,7 @@ EXTRA_JS_MODULES.addons += [
|
|||
'Content.js',
|
||||
'GMPProvider.jsm',
|
||||
'LightweightThemeImageOptimizer.jsm',
|
||||
'ProductAddonChecker.jsm',
|
||||
'SpellCheckDictionaryBootstrap.js',
|
||||
'WebExtensionBootstrap.js',
|
||||
]
|
||||
|
|
|
@ -23,8 +23,8 @@ try {
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
|
@ -559,7 +559,7 @@ Blocklist.prototype = {
|
|||
dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI);
|
||||
dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion);
|
||||
dsURI = dsURI.replace(/%LOCALE%/g, getLocale());
|
||||
dsURI = dsURI.replace(/%CHANNEL%/g, UpdateChannel.get());
|
||||
dsURI = dsURI.replace(/%CHANNEL%/g, UpdateUtils.UpdateChannel);
|
||||
dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion);
|
||||
dsURI = dsURI.replace(/%DISTRIBUTION%/g,
|
||||
getDistributionPrefValue(PREF_APP_DISTRIBUTION));
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Not an xml file!
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<foobar></barfoo>
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<test></test>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<updates>
|
||||
<addons></addons>
|
||||
</updates>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<updates>
|
||||
<addons>
|
||||
<addon id="test1" URL="http://example.com/test1.xpi"/>
|
||||
<addon id="test2" URL="http://example.com/test2.xpi" hashFunction="md5" hashValue="djhfgsjdhf"/>
|
||||
<addon id="test3" URL="http://example.com/test3.xpi" version="1.0" size="45"/>
|
||||
<addon id="test4"/>
|
||||
<addon URL="http://example.com/test5.xpi"/>
|
||||
</addons>
|
||||
</updates>
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<updates></updates>
|
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/productaddons/unsigned.xpi
Normal file
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/productaddons/unsigned.xpi
Normal file
Двоичный файл не отображается.
|
@ -1,18 +0,0 @@
|
|||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ID = "system1@tests.mozilla.org";
|
||||
const VERSION = "1.0";
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>system1@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>System Add-on 1</em:name>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>5</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
|
@ -1,18 +0,0 @@
|
|||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ID = "system2@tests.mozilla.org";
|
||||
const VERSION = "1.0";
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>system2@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>System Add-on 2</em:name>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>5</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
|
@ -1,18 +0,0 @@
|
|||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ID = "system1@tests.mozilla.org";
|
||||
const VERSION = "2.0";
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>system1@tests.mozilla.org</em:id>
|
||||
<em:version>2.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>System Add-on 1</em:name>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>5</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
|
@ -1,18 +0,0 @@
|
|||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ID = "system3@tests.mozilla.org";
|
||||
const VERSION = "1.0";
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>system3@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>System Add-on 3</em:name>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>5</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
Двоичный файл не отображается.
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system2_2.xpi
Normal file
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system2_2.xpi
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system2_3.xpi
Normal file
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system2_3.xpi
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system3_2.xpi
Normal file
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system3_2.xpi
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system3_3.xpi
Normal file
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system3_3.xpi
Normal file
Двоичный файл не отображается.
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче