author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Thu, 24 Sep 2015 12:01:40 +0200 | |
changeset 264198 | bf2bc1aa78c0b72d9b6b13f7a8c6ae61c60a51dc |
parent 264180 | 0f498d5adbf959b6743f76acc849d87ecb26f19f (current diff) |
parent 264197 | 19b55237cb368ce684bc8626cf9e8abe37ff1d0a (diff) |
child 264199 | 001942e4617b2324bfa6cdfb1155581cbc3f0cc4 |
push id | 65548 |
push user | cbook@mozilla.com |
push date | Thu, 24 Sep 2015 10:06:18 +0000 |
treeherder | mozilla-inbound@0d7793440e17 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 44.0a1 |
first release with | nightly linux32
bf2bc1aa78c0
/
44.0a1
/
20150924030231
/
files
nightly linux64
bf2bc1aa78c0
/
44.0a1
/
20150924030231
/
files
nightly mac
bf2bc1aa78c0
/
44.0a1
/
20150924030231
/
files
nightly win32
bf2bc1aa78c0
/
44.0a1
/
20150924030231
/
files
nightly win64
bf2bc1aa78c0
/
44.0a1
/
20150924030231
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
44.0a1
/
20150924030231
/
pushlog to previous
nightly linux64
44.0a1
/
20150924030231
/
pushlog to previous
nightly mac
44.0a1
/
20150924030231
/
pushlog to previous
nightly win32
44.0a1
/
20150924030231
/
pushlog to previous
nightly win64
44.0a1
/
20150924030231
/
pushlog to previous
|
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1362,18 +1362,16 @@ pref("devtools.command-button-eyedropper pref("devtools.command-button-screenshot.enabled", false); pref("devtools.command-button-rulers.enabled", false); // Inspector preferences // Enable the Inspector 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); // The default size for image preview tooltips in the rule-view/computed-view/markup-view pref("devtools.inspector.imagePreviewTooltipSize", 300); // Enable user agent style inspection in rule-view pref("devtools.inspector.showUserAgentStyles", false); // Show all native anonymous content (like controls in <video> tags)
--- a/browser/base/content/aboutDialog.js +++ b/browser/base/content/aboutDialog.js @@ -67,35 +67,35 @@ function init(aEvent) } #ifdef MOZ_UPDATER gAppUpdater = new appUpdater(); 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 #ifdef XP_MACOSX // it may not be sized at this point, and we need its width to calculate its position window.sizeToContent(); window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5); #endif } #ifdef MOZ_UPDATER 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; function onUnload(aEvent) { if (gAppUpdater.isChecking) gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK); // Safe to call even when there isn't a download in progress. gAppUpdater.removeDownloadListener();
--- a/browser/base/content/browser-feeds.js +++ b/browser/base/content/browser-feeds.js @@ -162,10 +162,22 @@ var FeedHandler = { // background browsers, we'll update on tab switch. if (browserForLink == gBrowser.selectedBrowser) { // Batch updates to avoid updating the UI for multiple onLinkAdded events // fired within 100ms of each other. if (this._updateFeedTimeout) 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; + } + }, };
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -37,18 +37,18 @@ XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch", "resource:///modules/ContentSearch.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AboutHome", "resource:///modules/AboutHome.jsm"); 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"); XPCOMUtils.defineLazyServiceGetter(this, "gDNSService", "@mozilla.org/network/dns-service;1", "nsIDNSService"); XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"); @@ -960,16 +960,17 @@ var gBrowserInit = { // These routines add message listeners. They must run before // loading the frame script to ensure that we don't miss any // message sent between when the frame script is loaded and when // the listener is registered. DOMLinkHandler.init(); gPageStyleMenu.init(); LanguageDetectionListener.init(); BrowserOnClick.init(); + FeedHandler.init(); DevEdition.init(); AboutPrivateBrowsingListener.init(); TrackingProtection.init(); let mm = window.getGroupMessageManager("browsers"); mm.loadFrameScript("chrome://browser/content/tab-content.js", true); mm.loadFrameScript("chrome://browser/content/content.js", true); mm.loadFrameScript("chrome://browser/content/content-UITour.js", true); @@ -2913,17 +2914,17 @@ var BrowserOnClick = { port: location.port, timestamp: Math.round(Date.now() / 1000), errorCode: transportSecurityInfo.errorCode, failedCertChain: asciiCertChain, userAgent: window.navigator.userAgent, version: 1, build: gAppInfo.appBuildID, product: gAppInfo.name, - channel: UpdateChannel.get() + channel: UpdateUtils.UpdateChannel } let reportURL = Services.prefs.getCharPref("security.ssl.errorReporting.url"); let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] .createInstance(Ci.nsIXMLHttpRequest); try { xhr.open("POST", reportURL);
--- a/browser/base/content/newtab/newTab.js +++ b/browser/base/content/newtab/newTab.js @@ -13,18 +13,16 @@ Cu.import("resource://gre/modules/PageTh Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm"); Cu.import("resource:///modules/DirectoryLinksProvider.jsm"); Cu.import("resource://gre/modules/NewTabUtils.jsm"); 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, allPages: gAllPages, linkChecker: gLinkChecker, pinnedLinks: gPinnedLinks, blockedLinks: gBlockedLinks, gridPrefs: gGridPrefs
--- a/browser/base/content/test/general/browser_remoteTroubleshoot.js +++ b/browser/base/content/test/general/browser_remoteTroubleshoot.js @@ -55,17 +55,17 @@ add_task(function*() { Assert.ok(got.message.graphics, "should have graphics"); // Check we have channel and build ID info: Assert.equal(got.message.application.buildID, Services.appinfo.appBuildID, "should have correct build ID"); 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), "should not have update channel where not available."); } else { Assert.equal(got.message.application.updateChannel, updateChannel, "should have correct update channel."); }
--- a/browser/components/feeds/FeedWriter.js +++ b/browser/components/feeds/FeedWriter.js @@ -4,16 +4,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. const Cc = Components.classes; const Ci = Components.interfaces; 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"; function LOG(str) { var prefB = Cc["@mozilla.org/preferences-service;1"]. getService(Ci.nsIPrefBranch); @@ -708,19 +709,17 @@ FeedWriter.prototype = { } }, _setAlwaysUseCheckedState: function FW__setAlwaysUseCheckedState(feedType) { var checkbox = this._getUIElement("alwaysUse"); 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) { } this._setCheckboxCheckedState(checkbox, alwaysUse); } }, _setSubscribeUsingLabel: function FW__setSubscribeUsingLabel() { @@ -798,20 +797,17 @@ FeedWriter.prototype = { break; default: this._setAlwaysUseLabel(); } } }, _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)); } catch (ex) { } switch (handler) { case "web": { @@ -894,20 +890,18 @@ FeedWriter.prototype = { // Last-selected application var menuItem = liveBookmarksMenuItem.cloneNode(false); menuItem.removeAttribute("selected"); menuItem.setAttribute("anonid", "selectedAppMenuItem"); 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); else { // Hide the menuitem if the last selected application doesn't exist menuItem.setAttribute("hidden", true); } } @@ -953,35 +947,39 @@ FeedWriter.prototype = { menuItem.setAttribute("label", this._getString("chooseApplicationMenuItem")); handlersMenuPopup.appendChild(menuItem); // separator 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; + // 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); } - 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._setSelectedHandler(feedType); // "Subscribe using..." this._setSubscribeUsingLabel(); @@ -995,17 +993,17 @@ FeedWriter.prototype = { // Set up the "Subscribe Now" button this._getUIElement("subscribeButton") .addEventListener("command", this, false); // 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) { var textfeedinfo1, textfeedinfo2; switch (feedType) { case Ci.nsIFeed.TYPE_VIDEO: textfeedinfo1 = "feedSubscriptionVideoPodcast1"; textfeedinfo2 = "feedSubscriptionVideoPodcast2"; @@ -1024,17 +1022,17 @@ FeedWriter.prototype = { var feedinfo2 = this._document.getElementById("feedSubscriptionInfo2"); var feedinfo2Str = this._getString(textfeedinfo2); feedinfo1.textContent = feedinfo1Str; feedinfo2.textContent = feedinfo2Str; header.setAttribute('firstrun', 'true'); - prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false); + this._mm.sendAsyncMessage("FeedWriter:ShownFirstRun"); } }, /** * Returns the original URI object of the feed and ensures that this * component is only ever invoked from the preview document. * @param aWindow * The window of the document invoking the BrowserFeedWriter @@ -1085,18 +1083,17 @@ FeedWriter.prototype = { var secman = Cc["@mozilla.org/scriptsecuritymanager;1"]. getService(Ci.nsIScriptSecurityManager); this._feedPrincipal = secman.getSimpleCodebasePrincipal(this._feedURI); LOG("Subscribe Preview: feed uri = " + this._window.location.href); // 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); prefs.addObserver(PREF_SELECTED_APP, this, false); prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this, false); prefs.addObserver(PREF_VIDEO_SELECTED_READER, this, false); prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this, false); prefs.addObserver(PREF_VIDEO_SELECTED_APP, this, false); @@ -1128,18 +1125,17 @@ FeedWriter.prototype = { close: function FW_close() { this._getUIElement("handlersMenuPopup") .removeEventListener("command", this, false); this._getUIElement("subscribeButton") .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); prefs.removeObserver(PREF_SELECTED_APP, this); prefs.removeObserver(PREF_VIDEO_SELECTED_ACTION, this); prefs.removeObserver(PREF_VIDEO_SELECTED_READER, this); prefs.removeObserver(PREF_VIDEO_SELECTED_WEB, this); prefs.removeObserver(PREF_VIDEO_SELECTED_APP, this); @@ -1167,18 +1163,17 @@ FeedWriter.prototype = { this._feedURI = null; } }, subscribe: function FW_subscribe() { 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"); var selectedItem = this._getSelectedItemFromMenulist(this._handlersMenuList); let subscribeCallback = function() { if (selectedItem.hasAttribute("webhandlerurl")) { var webURI = selectedItem.getAttribute("webhandlerurl"); prefs.setCharPref(getPrefReaderForType(feedType), "web"); @@ -1315,15 +1310,24 @@ FeedWriter.prototype = { if (aDataLen > 0) { var dataURL = "data:" + aMimeType + ";base64," + btoa(String.fromCharCode.apply(null, aData)); aMenuItem.setAttribute('image', dataURL); } }); }, + 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, Ci.nsIDOMGlobalPropertyInitializer]) }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FeedWriter]);
--- a/browser/components/feeds/test/mochitest.ini +++ b/browser/components/feeds/test/mochitest.ini @@ -5,14 +5,11 @@ support-files = bug408328-data.xml bug436801-data.xml bug494328-data.xml bug589543-data.xml valid-feed.xml 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]
--- a/browser/components/loop/modules/MozLoopAPI.jsm +++ b/browser/components/loop/modules/MozLoopAPI.jsm @@ -20,18 +20,18 @@ XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage", "resource:///modules/loop/LoopStorage.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "hookWindowCloseForPanelClose", "resource://gre/modules/MozSocialAPI.jsm"); 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", "resource:///modules/Social.jsm"); XPCOMUtils.defineLazyGetter(this, "appInfo", function() { return Cc["@mozilla.org/xre/app-info;1"] .getService(Ci.nsIXULAppInfo) .QueryInterface(Ci.nsIXULRuntime); @@ -805,17 +805,17 @@ function injectLoopAPI(targetWindow) { appVersionInfo: { enumerable: true, get: function() { if (!appVersionInfo) { // If the lazy getter explodes, we're probably loaded in xpcshell, // 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); } catch (ex) { // only log outside of xpcshell to avoid extra message noise if (typeof targetWindow !== 'undefined' && "console" in targetWindow) { MozLoopService.log.error("Failed to construct appVersionInfo; if this isn't " + "an xpcshell unit test, something is wrong", ex);
--- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -119,18 +119,18 @@ XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX", "resource:///modules/SignInToWebsite.jsm"); #endif 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 XPCOMUtils.defineLazyModuleGetter(this, "TabCrashReporter", "resource:///modules/ContentCrashReporters.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter", "resource:///modules/ContentCrashReporters.jsm"); #endif @@ -2972,17 +2972,17 @@ var E10SUINotification = { // e10s testing period to Nightly users. CURRENT_NOTICE_COUNT: 4, CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1", PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt", checkStatus: function() { let skipE10sChecks = false; try { - let updateChannel = UpdateChannel.get(); + let updateChannel = UpdateUtils.UpdateChannel; let channelAuthorized = updateChannel == "nightly" || updateChannel == "aurora"; skipE10sChecks = !channelAuthorized || Services.prefs.getBoolPref("browser.tabs.remote.disabled-for-a11y"); } catch(e) {} if (skipE10sChecks) { return;
--- a/browser/components/preferences/in-content/main.js +++ b/browser/components/preferences/in-content/main.js @@ -160,18 +160,18 @@ var gMainPane = { shouldProceed = !cancelQuit.data; if (shouldProceed) { for (let prefToChange of prefsToChange) { prefToChange.value = e10sCheckbox.checked; } 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")); } Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart); } } // Revert the checkbox in case we didn't quit
--- a/browser/experiments/Experiments.jsm +++ b/browser/experiments/Experiments.jsm @@ -15,18 +15,18 @@ Cu.import("resource://gre/modules/XPCOMU Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); 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", "resource://gre/modules/AddonManager.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment", "resource://gre/modules/TelemetryEnvironment.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog", "resource://gre/modules/TelemetryLog.jsm"); @@ -270,17 +270,17 @@ Experiments.Policy.prototype = { return new Date(this.now().getTime() + offset); }, oneshotTimer: function (callback, timeout, thisObj, name) { return CommonUtils.namedTimer(callback, timeout, thisObj, name); }, updatechannel: function () { - return UpdateChannel.get(); + return UpdateUtils.UpdateChannel; }, locale: function () { let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry); return chrome.getSelectedLocale("global"); }, /**
--- a/browser/modules/DirectoryLinksProvider.jsm +++ b/browser/modules/DirectoryLinksProvider.jsm @@ -21,18 +21,18 @@ Cu.import("resource://gre/modules/Timer. XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils", "resource://gre/modules/NewTabUtils.jsm"); 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"); XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => { return new TextDecoder(); }); XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function () { return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash); @@ -275,17 +275,17 @@ var DirectoryLinksProvider = { this._setupStartEndTime(link); this._suggestedLinks.set(suggestedSite, suggestedMap); } }, _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"}); }); }, /** * Downloads a links with json content
--- a/browser/themes/windows/browser-aero.css +++ b/browser/themes/windows/browser-aero.css @@ -93,16 +93,22 @@ @media not all and (-moz-os-version: windows-win7) { @media not all and (-moz-os-version: windows-win8) { @media (-moz-windows-default-theme) { #main-window { background-color: hsl(0, 0%, 78%); } } + @media not all and (-moz-windows-default-theme) { + #main-window { + background-color: transparent; + } + } + #titlebar-buttonbox, .titlebar-button { -moz-appearance: none !important; } .titlebar-button { border: none; margin: 0 !important;
--- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -344,28 +344,23 @@ z-index: 1; } #nav-bar { background-clip: padding-box; 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; } } #personal-bookmarks { min-height: 24px; } #print-preview-toolbar:not(:-moz-lwtheme) { @@ -2000,27 +1995,34 @@ richlistitem[type~="action"][actiontype= } } /* Remove border between tab strip and navigation toolbar on Windows 10+ */ @media not all and (-moz-os-version: windows-xp) { @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; - } - - #TabsToolbar { - --tab-stroke-background-size: 0 0; - } - - :root { - --tab-toolbar-navbar-overlap: 0; + @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; + } + + :root { + --tab-toolbar-navbar-overlap: 0; + } + + #nav-bar { + border-top-style: none !important; + box-shadow: none; + } } } } } } /* Invert the unhovered close tab icons on bright-text tabs */ @media not all and (min-resolution: 1.1dppx) {
--- a/browser/themes/windows/customizableui/panelUIOverlay.css +++ b/browser/themes/windows/customizableui/panelUIOverlay.css @@ -218,17 +218,19 @@ menu.subviewbutton > .menu-right:-moz-lo color: -moz-FieldText; } #BMB_bookmarksPopup .subviewbutton[disabled=true] > .menu-text { color: GrayText; } #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; } .subviewbutton.panel-subview-footer, #BMB_bookmarksPopup .subviewbutton.panel-subview-footer { color: ButtonText; }
--- a/devtools/client/markupview/markup-view.js +++ b/devtools/client/markupview/markup-view.js @@ -3,17 +3,16 @@ /* 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 {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/; const COLLAPSE_DATA_URL_LENGTH = 60; const NEW_SELECTION_HIGHLIGHTER_TIMER = 1000; const GRAB_DELAY = 400; const DRAG_DROP_AUTOSCROLL_EDGE_DISTANCE = 50; const DRAG_DROP_MIN_AUTOSCROLL_SPEED = 5; @@ -117,17 +116,16 @@ function MarkupView(aInspector, aFrame, this._onCopy = this._onCopy.bind(this); this._frame.contentWindow.addEventListener("copy", this._onCopy); this._boundFocus = this._onFocus.bind(this); this._frame.addEventListener("focus", this._boundFocus, false); this._makeTooltipPersistent = this._makeTooltipPersistent.bind(this); - this._initPreview(); this._initTooltips(); this._initHighlighter(); EventEmitter.decorate(this); } exports.MarkupView = MarkupView; @@ -642,16 +640,21 @@ MarkupView.prototype = { } this.navigate(selection); break; } case Ci.nsIDOMKeyEvent.DOM_VK_F2: { 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(); break; } } default: handled = false; @@ -1567,102 +1570,16 @@ MarkupView.prototype = { this._lastDropTarget = null; this._lastDragTarget = null; 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 */ indicateDropTarget: function(el) { if (this._lastDropTarget) { this._lastDropTarget.classList.remove("drop-target"); }
--- a/devtools/client/markupview/markup-view.xhtml +++ b/devtools/client/markupview/markup-view.xhtml @@ -92,14 +92,10 @@ save="${elt}" class="editor comment theme-comment"><!-- --><span><!--</span><!-- --><pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="0"></pre><!-- --><span>--></span><!-- --></span> </div> - <div id="previewbar" class="disabled"> - <div id="preview"/> - <div id="viewbox"/> - </div> </body> </html>
--- a/devtools/client/styleeditor/StyleSheetEditor.jsm +++ b/devtools/client/styleeditor/StyleSheetEditor.jsm @@ -186,16 +186,25 @@ StyleSheetEditor.prototype = { this._friendlyName = decodeURI(this._friendlyName); } catch (ex) { } } 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. */ linkCSSFile: function() { if (!this.styleSheet.isOriginalSource) { return; } let relatedSheet = this.styleSheet.relatedStyleSheet; @@ -490,19 +499,17 @@ StyleSheetEditor.prototype = { // (stylesheet is enabled) so that 'missed' updates // while the stylesheet is disabled can be performed // when it is enabled back. @see enableStylesheet if (this.sourceEditor) { 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); }, /** * Handle mousemove events, calling _highlightSelectorAt after a delay only * and reseting the delay everytime. */ _onMouseMove: function(e) { @@ -678,17 +685,17 @@ StyleSheetEditor.prototype = { * file from disk and live update the stylesheet object with the contents. */ updateLinkedStyleSheet: function() { OS.File.read(this.linkedCSSFile).then((array) => { let decoder = new TextDecoder(); let text = decoder.decode(array); let relatedSheet = this.styleSheet.relatedStyleSheet; - relatedSheet.update(text, true); + relatedSheet.update(text, this.transitionsEnabled); }, this.markLinkedFileBroken); }, /** * Retrieve custom key bindings objects as expected by Editor. * Editor action names are not displayed to the user. * * @return {array} key binding objects for the source editor
--- a/devtools/client/themes/markup-view.css +++ b/devtools/client/themes/markup-view.css @@ -47,59 +47,16 @@ .not-displayed .close { opacity: .7; } .tag-line { 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; font-weight: bold; line-height: 10px; border-radius: 3px; padding: 0px 2px; -moz-margin-start: 5px;
--- a/mobile/android/chrome/content/aboutFeedback.js +++ b/mobile/android/chrome/content/aboutFeedback.js @@ -17,17 +17,17 @@ const HEARTY_ICON_XXHDPI = "chrome://bro const FLOATY_ICON_MDPI = "chrome://browser/skin/images/icon_floaty_mdpi.png"; const FLOATY_ICON_HDPI = "chrome://browser/skin/images/icon_floaty_hdpi.png"; const FLOATY_ICON_XHDPI = "chrome://browser/skin/images/icon_floaty_xhdpi.png"; 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) { Services.console.logStringMessage(a); } function init() { let anchors = document.querySelectorAll(".maybe-later"); @@ -133,17 +133,17 @@ function sendFeedback(aEvent) { data["url"] = urlElement.value; } data["device"] = Services.sysinfo.get("device"); data["manufacturer"] = Services.sysinfo.get("manufacturer"); 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("?"); if (getParam.length > 1) { let urlParam = new URLSearchParams(getParam[1]); if(urlParam.get("source")) { data["source"] = urlParam.get("source"); }
--- a/netwerk/protocol/http/UserAgentUpdates.jsm +++ b/netwerk/protocol/http/UserAgentUpdates.jsm @@ -22,17 +22,17 @@ XPCOMUtils.defineLazyModuleGetter( 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"); + this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm"); XPCOMUtils.defineLazyServiceGetter( this, "gUpdateTimer", "@mozilla.org/updates/timer-manager;1", "nsIUpdateTimerManager"); XPCOMUtils.defineLazyGetter(this, "gApp", function() { return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo) .QueryInterface(Ci.nsIXULRuntime); @@ -187,17 +187,17 @@ this.UserAgentUpdates = { _getParameters() { return { "%DATE%": function() { return Date.now().toString(); }, "%PRODUCT%": function() { return gApp.name; }, "%APP_ID%": function() { return gApp.ID; }, "%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, ""); }, }; }, _getUpdateURL: function() { let url = this._getPref(PREF_UPDATES_URL, ""); let params = this._getParameters();
--- a/services/datareporting/policy.jsm +++ b/services/datareporting/policy.jsm @@ -20,17 +20,17 @@ this.EXPORTED_SYMBOLS = [ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; #endif 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 // about the policy changes. const DATAREPORTING_POLICY_VERSION = 1; const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; @@ -383,17 +383,17 @@ this.DataReportingPolicy.prototype = Obj }, /** * The minimum policy version which for dataSubmissionPolicyAccepted to * to be valid. */ 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); }, get dataSubmissionPolicyAcceptedVersion() { return this._prefs.get("dataSubmissionPolicyAcceptedVersion", 0); },
--- a/services/datareporting/tests/xpcshell/test_policy.js +++ b/services/datareporting/tests/xpcshell/test_policy.js @@ -3,32 +3,32 @@ "use strict"; 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, aCurrentPolicyVersion = 1, aMinimumPolicyVersion = 1, aBranchMinimumVersionOverride) { let branch = "testing.datareporting." + name; // The version prefs should not be removed on reset, so set them in the // default branch. let defaultPolicyPrefs = new Preferences({ branch: branch + ".policy." , 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 defaultPolicyPrefs.reset(branchOverridePrefName); let policyPrefs = new Preferences(branch + ".policy."); let healthReportPrefs = new Preferences(branch + ".healthreport.");
--- a/services/healthreport/healthreporter.jsm +++ b/services/healthreport/healthreporter.jsm @@ -25,18 +25,18 @@ Cu.import("resource://gre/modules/osfile Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/TelemetryStopwatch.jsm"); 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. const OLDEST_ALLOWED_YEAR = 2012; const DAYS_IN_PAYLOAD = 180; const DEFAULT_DATABASE_NAME = "healthreport.sqlite"; @@ -1098,17 +1098,17 @@ AbstractHealthReporter.prototype = Objec out[k] = ai[v]; } } catch (ex) { this._log.warn("Could not obtain Services.appinfo: " + CommonUtils.exceptionStr(ex)); } try { - out["updateChannel"] = UpdateChannel.get(); + out["updateChannel"] = UpdateUtils.UpdateChannel; } catch (ex) { this._log.warn("Could not obtain update channel: " + CommonUtils.exceptionStr(ex)); } return out; }, });
--- a/services/healthreport/providers.jsm +++ b/services/healthreport/providers.jsm @@ -41,18 +41,18 @@ Cu.import("resource://gre/modules/osfile Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 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"); const LAST_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_LAST_NUMERIC}; const LAST_TEXT_FIELD = {type: Metrics.Storage.FIELD_LAST_TEXT}; const DAILY_DISCRETE_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_DAILY_DISCRETE_NUMERIC}; const DAILY_LAST_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC}; @@ -328,17 +328,17 @@ AppInfoProvider.prototype = Object.freez try { yield m.setLastText(k, ai[v]); } catch (ex) { this._log.warn("Error obtaining Services.appinfo." + v); } } 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)); } yield m.setLastText("distributionID", this._prefs.get("distribution.id", "")); yield m.setLastText("distributionVersion", this._prefs.get("distribution.version", "")); yield m.setLastText("hotfixVersion", this._prefs.get("extensions.hotfix.lastVersion", ""));
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm +++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm @@ -446,10 +446,36 @@ this.BrowserTestUtils = { resolve(); } }, true); if (!dontRemove && !tab.closing) { 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 }); + }); } };
--- a/testing/mochitest/tests/SimpleTest/AsyncUtilsContent.js +++ b/testing/mochitest/tests/SimpleTest/AsyncUtilsContent.js @@ -6,16 +6,20 @@ // Set up a dummy environment so that EventUtils works. We need to be careful to // pass a window object into each EventUtils method we call rather than having // it rely on the |window| global. var EventUtils = {}; 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) => { let data = message.data; let target = data.target; if (typeof target == "string") { target = content.document.querySelector(target); } @@ -34,8 +38,13 @@ addMessageListener("Test:SynthesizeMouse left += rect.width / 2; top += rect.height / 2; } } 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 }); +});
--- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -90,16 +90,17 @@ user_pref("browser.safebrowsing.provider user_pref("browser.safebrowsing.provider.mozilla.gethashURL", "http://%(server)s/safebrowsing-dummy/gethash"); user_pref("browser.safebrowsing.provider.mozilla.updateURL", "http://%(server)s/safebrowsing-dummy/update"); user_pref("privacy.trackingprotection.introURL", "http://%(server)s/trackingprotection/tour"); // Point update checks to the local testing server for fast failures 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 user_pref("extensions.webservice.discoverURL", "http://%(server)s/extensions-dummy/discoveryURL"); // Make sure AddonRepository won't hit the network user_pref("extensions.getAddons.maxResults", 0); user_pref("extensions.getAddons.get.url", "http://%(server)s/extensions-dummy/repositoryGetURL"); user_pref("extensions.getAddons.getWithPerformance.url", "http://%(server)s/extensions-dummy/repositoryGetWithPerformanceURL");
--- a/testing/talos/talos/config.py +++ b/testing/talos/talos/config.py @@ -135,16 +135,18 @@ DEFAULTS = dict( 'extensions.getAddons.search.browseURL': 'http://127.0.0.1/extensions-dummy/repositoryBrowseURL', 'extensions.getAddons.search.url': 'http://127.0.0.1/extensions-dummy/repositorySearchURL', 'plugins.update.url': '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, 'media.capturestream_hints.enabled': True, 'browser.contentHandlers.types.0.uri': 'http://127.0.0.1/rss?url=%s', 'browser.contentHandlers.types.1.uri': 'http://127.0.0.1/rss?url=%s', 'browser.contentHandlers.types.2.uri': 'http://127.0.0.1/rss?url=%s', 'browser.contentHandlers.types.3.uri': 'http://127.0.0.1/rss?url=%s',
--- a/testing/xpcshell/head.js +++ b/testing/xpcshell/head.js @@ -1500,16 +1500,17 @@ try { // We need to avoid hitting the network with certain components. try { if (runningInParent) { let prefs = Components.classes["@mozilla.org/preferences-service;1"] .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"); } } catch (e) { } // Make tests run consistently on DevEdition (which has a lightweight theme // selected by default).
--- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -5309,22 +5309,16 @@ "kind": "flag", "description": "Social has been enabled at least once on the current session" }, "ENABLE_PRIVILEGE_EVER_CALLED": { "expires_in_version": "never", "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", "description": "The device supports touch input", "cpp_guard": "XP_WIN" }, "COMPONENTS_SHIM_ACCESSED_BY_CONTENT": { "expires_in_version": "never",
--- a/toolkit/components/telemetry/TelemetryController.jsm +++ b/toolkit/components/telemetry/TelemetryController.jsm @@ -75,18 +75,18 @@ XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStorage", "resource://gre/modules/TelemetryStorage.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ThirdPartyCookieProbe", "resource://gre/modules/ThirdPartyCookieProbe.jsm"); 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", "resource://gre/modules/TelemetrySession.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend", "resource://gre/modules/TelemetrySend.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "TelemetryReportingPolicy", "resource://gre/modules/TelemetryReportingPolicy.jsm"); @@ -407,17 +407,17 @@ var Impl = { try { arch = Services.sysinfo.get("arch"); } catch (e) { this._log.trace("assemblePing - Unable to get system architecture.", e); } let updateChannel = null; try { - updateChannel = UpdateChannel.get(false); + updateChannel = UpdateUtils.getUpdateChannel(false); } catch (e) { this._log.trace("assemblePing - Unable to get update channel.", e); } return { architecture: arch, buildId: Services.appinfo.appBuildID, name: Services.appinfo.name,
--- a/toolkit/components/telemetry/TelemetryEnvironment.jsm +++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm @@ -27,18 +27,18 @@ XPCOMUtils.defineLazyModuleGetter(this, "resource://gre/modules/ctypes.jsm"); #ifndef MOZ_WIDGET_GONK Cu.import("resource://gre/modules/AddonManager.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"); #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; /** * This is a policy object used to override behavior for testing. */ var Policy = { now: () => new Date(), @@ -1002,17 +1002,17 @@ EnvironmentCache.prototype = { }, /** * Update the cached settings data. */ _updateSettings: function () { let updateChannel = null; try { - updateChannel = UpdateChannel.get(false); + updateChannel = UpdateUtils.getUpdateChannel(false); } catch (e) {} this._currentEnvironment.settings = { #ifndef MOZ_WIDGET_GONK addonCompatibilityCheckEnabled: AddonManager.checkCompatibility, #endif blocklistEnabled: Preferences.get(PREF_BLOCKLIST_ENABLED, true), #ifndef MOZ_WIDGET_ANDROID
--- a/toolkit/components/telemetry/TelemetryReportingPolicy.jsm +++ b/toolkit/components/telemetry/TelemetryReportingPolicy.jsm @@ -14,18 +14,18 @@ Cu.import("resource://gre/modules/Log.js Cu.import("resource://gre/modules/Preferences.jsm", this); Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/Timer.jsm", this); Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); 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::"; // Oldest year to allow in date preferences. The FHR infobar was implemented in // 2012 and no dates older than that should be encountered. const OLDEST_ALLOWED_ACCEPTANCE_YEAR = 2012; @@ -246,17 +246,17 @@ var TelemetryReportingPolicyImpl = { */ get minimumPolicyVersion() { const minPolicyVersion = Preferences.get(PREF_MINIMUM_POLICY_VERSION, 1); // First check if the current channel has a specific minimum policy version. If not, // 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; } const channelPref = PREF_MINIMUM_POLICY_VERSION + ".channel-" + channel; return Preferences.get(channelPref, minPolicyVersion); },
--- a/toolkit/components/telemetry/TelemetrySession.jsm +++ b/toolkit/components/telemetry/TelemetrySession.jsm @@ -138,18 +138,16 @@ XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStorage", "resource://gre/modules/TelemetryStorage.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog", "resource://gre/modules/TelemetryLog.jsm"); 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", "resource://services-common/utils.js"); function generateUUID() { let str = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString(); // strip {}
--- a/toolkit/components/telemetry/TelemetryStorage.jsm +++ b/toolkit/components/telemetry/TelemetryStorage.jsm @@ -370,59 +370,32 @@ this.TelemetryStorage = { * @param {Object} pingData The ping object. * @return {Promise} A promise resolved when the ping is saved to the pings directory. */ addPendingPing: function(pingData) { 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 * * @param {object} ping The ping. * @returns {promise} */ cleanupPingFile: function(ping) { 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. */ get pendingPingCount() { return TelemetryStorageImpl.pendingPingCount; }, - testLoadHistograms: function(file) { - return TelemetryStorageImpl.testLoadHistograms(file); - }, - /** * Loads a ping file. * @param {String} aFilePath The path of the ping file. * @return {Promise<Object>} A promise resolved with the ping content or rejected if the * ping contains invalid data. */ loadPingFile: Task.async(function* (aFilePath) { return TelemetryStorageImpl.loadPingFile(aFilePath); @@ -1171,36 +1144,16 @@ var TelemetryStorageImpl = { savePing: Task.async(function*(ping, overwrite) { yield getPingDirectory(); let file = pingFilePath(ping); yield this.savePingToFile(ping, file, overwrite); 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. * Note: that the original ping file will not be modified. * * @param {Object} ping The ping object. * @return {Promise} A promise resolved when the ping is saved to the pings directory. */ addPendingPing: function(ping) { @@ -1390,47 +1343,20 @@ var TelemetryStorageImpl = { id: p[0], lastModificationDate: p[1].lastModificationDate, }]; list.sort((a, b) => b.lastModificationDate - a.lastModificationDate); 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. * @param {Boolean} [aCompressed=false] If |true|, expects the file to be compressed using lz4. * @return {Promise<Object>} A promise resolved with the ping content or rejected if the * ping contains invalid data. * @throws {PingReadError} There was an error while reading the ping file from the disk. * @throws {PingParseError} There was an error while parsing the JSON content of the ping file.
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js @@ -281,22 +281,20 @@ function checkPayload(payload, reason, s } const TELEMETRY_PING = "TELEMETRY_PING"; const TELEMETRY_SUCCESS = "TELEMETRY_SUCCESS"; const TELEMETRY_TEST_FLAG = "TELEMETRY_TEST_FLAG"; 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); let rh = Telemetry.registeredHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, []); for (let name of rh) { if (/SQLITE/.test(name) && name in payload.histograms) { let histogramName = ("STARTUP_" + name); Assert.ok(histogramName in payload.histograms, histogramName + " must be available."); @@ -341,19 +339,16 @@ function checkPayload(payload, reason, s sum: successfulPings, sum_squares_lo: successfulPings, sum_squares_hi: 0 }; let tc = payload.histograms[TELEMETRY_SUCCESS]; 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. // // It's important to check for the presence of reporters with a mix of units, // because TelemetryController has separate logic for each one. But we can't // currently check UNITS_COUNT_CUMULATIVE or UNITS_PERCENTAGE because // Telemetry doesn't touch a memory reporter with these units that's @@ -482,27 +477,16 @@ add_task(function* test_expiredHistogram let dummy = Telemetry.newHistogram(histogram_id, "30", Telemetry.HISTOGRAM_EXPONENTIAL, 1, 2, 3); dummy.add(1); do_check_eq(TelemetrySession.getPayload()["histograms"][histogram_id], undefined); 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() { yield sendPing(); // We need two pings in order to make sure STARTUP_MEMORY_STORAGE_SQLIE histograms // are initialised. See bug 1131585. yield sendPing(); });
--- a/toolkit/components/urlformatter/nsURLFormatter.js +++ b/toolkit/components/urlformatter/nsURLFormatter.js @@ -20,18 +20,18 @@ const Ci = Components.interfaces; const Cu = Components.utils; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 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() { return Cc["@mozilla.org/xre/app-info;1"]. getService(Ci.nsIXULAppInfo). QueryInterface(Ci.nsIXULRuntime); }); @@ -106,17 +106,17 @@ nsURLFormatterService.prototype = { APPBUILDID: function() this.appInfo.appBuildID, PLATFORMVERSION: function() this.appInfo.platformVersion, PLATFORMBUILDID: function() this.appInfo.platformBuildID, APP: function() this.appInfo.name.toLowerCase().replace(/ /, ""), OS: function() this.appInfo.OS, 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@", GOOGLE_OAUTH_API_KEY: function() "@MOZ_GOOGLE_OAUTH_API_KEY@", BING_API_CLIENTID:function() "@MOZ_BING_API_CLIENTID@", BING_API_KEY: function() "@MOZ_BING_API_KEY@", DISTRIBUTION: function() this.distribution.id, DISTRIBUTION_VERSION: function() this.distribution.version
--- a/toolkit/components/viewsource/content/viewSourceUtils.js +++ b/toolkit/components/viewsource/content/viewSourceUtils.js @@ -14,16 +14,18 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ViewSourceBrowser", "resource://gre/modules/ViewSourceBrowser.jsm"); 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 = { mnsIWebBrowserPersist: Components.interfaces.nsIWebBrowserPersist, mnsIWebProgress: Components.interfaces.nsIWebProgress, mnsIWebPageDescriptor: Components.interfaces.nsIWebPageDescriptor, /**
--- a/toolkit/content/tests/browser/browser.ini +++ b/toolkit/content/tests/browser/browser.ini @@ -13,17 +13,16 @@ skip-if = e10s # Bug 921935 - focusmanag skip-if = e10s # Bug ?????? - intermittent crash of child process reported when run under e10s [browser_bug982298.js] skip-if = e10s # Bug 1064580 [browser_contentTitle.js] [browser_default_image_filename.js] [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] support-files = empty.png [browser_keyevents_during_autoscrolling.js] skip-if = e10s # Bug 921935 - focusmanager issues with e10s [browser_save_resend_postdata.js]
--- a/toolkit/content/tests/browser/browser_findbar.js +++ b/toolkit/content/tests/browser/browser_findbar.js @@ -1,90 +1,97 @@ 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 * by calling stopPropagation on a keypress event on a page. */ 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(); // Pressing these keys open the findbar. const HOTKEYS = ["/", "'"]; // Checking if findbar 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); } // 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); } gBrowser.removeTab(tab); }); 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); let findbar = gBrowser.getFindBar(); is(findbar._findStatusDesc.textContent, findbar._notFoundStr, "Findbar status text should be 'Phrase not found'"); gBrowser.removeTab(tab); }); 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); ok(!gBrowser.getFindBar()._findStatusDesc.textContent, "Findbar status should be empty"); gBrowser.removeTab(tab); }); // 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 findbar1.getElement("find-case-sensitive").click(); gBrowser.selectedTab = tab1; // Not found for first tab. @@ -97,47 +104,67 @@ add_task(function* test_tabwise_case_sen // But it didn't affect the second findbar. yield promiseFindFinished("S", true); ok(!findbar2._findStatusDesc.textContent, "Findbar status should be empty"); gBrowser.removeTab(tab1); 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(); + + // Findbar should operate normally. + yield promiseFindFinished("s", false); + ok(!findbar._findStatusDesc.textContent, "Findbar status should be empty"); - deferred.resolve(tab); - }, true); + gBrowser.updateBrowserRemoteness(browser, false); - return deferred.promise; -} + // 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(); let findbar = gBrowser.getFindBar(); findbar.startFind(findbar.FIND_NORMAL); let highlightElement = findbar.getElement("highlight"); if (highlightElement.checked != highlightOn) highlightElement.click(); executeSoon(() => { 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); findbar.browser.finder.removeResultListener(resultListener); deferred.resolve(); } resultListener = {
deleted file mode 100644 --- a/toolkit/content/tests/unit/test_updateChannelModule.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- 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/. */ - -Components.utils.import("resource://gre/modules/Preferences.jsm"); -Components.utils.import("resource://gre/modules/UpdateChannel.jsm"); - -const PREF_APP_UPDATE_CHANNEL = "app.update.channel"; -const TEST_CHANNEL = "TestChannel"; -const PREF_PARTNER_A = "app.partner.test_partner_a"; -const TEST_PARTNER_A = "TestPartnerA"; -const PREF_PARTNER_B = "app.partner.test_partner_b"; -const TEST_PARTNER_B = "TestPartnerB"; - -function test_get() { - 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); - - defaultPrefs.set(PREF_APP_UPDATE_CHANNEL, TEST_CHANNEL); - do_check_eq(UpdateChannel.get(), TEST_CHANNEL); - do_check_eq(UpdateChannel.get(false), TEST_CHANNEL); - - defaultPrefs.set(PREF_PARTNER_A, TEST_PARTNER_A); - defaultPrefs.set(PREF_PARTNER_B, TEST_PARTNER_B); - do_check_eq(UpdateChannel.get(), - TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B); - do_check_eq(UpdateChannel.get(false), TEST_CHANNEL); -} - -function run_test() { - test_get(); -}
--- a/toolkit/content/tests/unit/xpcshell.ini +++ b/toolkit/content/tests/unit/xpcshell.ini @@ -1,7 +1,6 @@ [DEFAULT] head = tail = skip-if = toolkit == 'gonk' [test_contentAreaUtils.js] -[test_updateChannelModule.js]
--- a/toolkit/content/widgets/findbar.xml +++ b/toolkit/content/widgets/findbar.xml @@ -368,22 +368,38 @@ this._findSelection = this.nsISelectionController.SELECTION_FIND; this._findResetTimeout = -1; // Make sure the FAYT keypress listener is attached by initializing the // 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 --> <method name="destroy"> <body><![CDATA[ if (this._destroyed) return; this._destroyed = true; @@ -397,16 +413,19 @@ this._observer); prefsvc.removeObserver("accessibility.typeaheadfind.linksonly", this._observer); prefsvc.removeObserver("accessibility.typeaheadfind.casesensitive", this._observer); // Clear all timers that might still be running. this._cancelTimers(); + + if (typeof gBrowser !== 'undefined') + gBrowser.tabContainer.removeEventListener("TabRemotenessChange", this); ]]></body> </method> <method name="_cancelTimers"> <body><![CDATA[ if (this._flashFindBarTimeout) { clearInterval(this._flashFindBarTimeout); this._flashFindBarTimeout = null;
--- a/toolkit/modules/AppConstants.jsm +++ b/toolkit/modules/AppConstants.jsm @@ -208,18 +208,20 @@ this.AppConstants = Object.freeze({ DLL_PREFIX: "@DLL_PREFIX@", DLL_SUFFIX: "@DLL_SUFFIX@", MOZ_APP_NAME: "@MOZ_APP_NAME@", MOZ_APP_VERSION: "@MOZ_APP_VERSION@", 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, #else false, #endif DEBUG_JS_MODULES: "@DEBUG_JS_MODULES@" });
--- a/toolkit/modules/GMPInstallManager.jsm +++ b/toolkit/modules/GMPInstallManager.jsm @@ -3,20 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; 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; var GMPInstallFailureReason = { GMP_INVALID: 1, GMP_HIDDEN: 2, GMP_DISABLED: 3, GMP_UPDATE_DISABLED: 4, @@ -25,183 +21,38 @@ var GMPInstallFailureReason = { Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); 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 = { }; Cu.import("resource://gre/modules/CertUtils.jsm", temp); 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 // to the parentLogger. 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 */ function GMPInstallManager() { } /** * Temp file name used for downloading */ @@ -216,34 +67,18 @@ GMPInstallManager.prototype = { let url = GMPPrefs.get(GMPPrefs.KEY_URL_OVERRIDE); if (url) { log.info("Using override url: " + url); } else { url = GMPPrefs.get(GMPPrefs.KEY_URL); 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; }, /** * Performs an addon check. * @return a promise which will be resolved or rejected. * The promise is resolved with an array of GMPAddons * The promise is rejected with an object with properties: @@ -255,48 +90,37 @@ GMPInstallManager.prototype = { let log = getScopedLogger("GMPInstallManager.checkForAddons"); if (this._deferred) { log.error("checkForAddons already called"); return Promise.reject({type: "alreadycalled"}); } 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; }, /** * Installs the specified addon and calls a callback when done. * @param gmpAddon The GMPAddon object to install * @return a promise which will be resolved or rejected * The promise will resolve with an array of paths that were extracted @@ -488,192 +312,33 @@ GMPInstallManager.prototype = { log.info("Done cleanup"); }, /** * If set to true, specifies to leave the temporary downloaded zip file. * 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; - } - }, }; /** * Used to construct a single GMP addon * 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 */ toString: function() { return this.id + " (" + "isValid: " + this.isValid + ", isInstalled: " + this.isInstalled + @@ -794,192 +459,56 @@ GMPExtractor.prototype = { * Constructs an object which downloads and initiates an install of * the specified GMPAddon object. * @param gmpAddon The addon to install. */ 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. * @return a promise which will be resolved or rejected * 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, status: status, type: "downloaderr" }); } - 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); // Reset the trial create pref, so that Gecko knows to do a test // run before reporting that the GMP works to content. GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_TRIAL_CREATE, gmpAddon.version, gmpAddon.id); // 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; -}
--- a/toolkit/modules/GMPUtils.jsm +++ b/toolkit/modules/GMPUtils.jsm @@ -121,37 +121,16 @@ this.GMPUtils = { } this.reportedKeys.push(key); let hist = Services.telemetry.getHistogramById(key); if (hist) { 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; - } }; /** * Manages preferences for GMP addons */ this.GMPPrefs = { KEY_EME_ENABLED: "media.eme.enabled", KEY_PLUGIN_ENABLED: "media.{0}.enabled",
--- a/toolkit/modules/Troubleshoot.jsm +++ b/toolkit/modules/Troubleshoot.jsm @@ -176,17 +176,17 @@ var dataProviders = { buildID: Services.appinfo.appBuildID, userAgent: Cc["@mozilla.org/network/protocol;1?name=http"]. getService(Ci.nsIHttpProtocolHandler). userAgent, safeMode: Services.appinfo.inSafeMode, }; 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"); } catch (e) {} let urlFormatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. getService(Ci.nsIURLFormatter); try {
deleted file mode 100644 --- a/toolkit/modules/UpdateChannel.jsm +++ /dev/null @@ -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; - } -};
new file mode 100644 --- /dev/null +++ b/toolkit/modules/UpdateUtils.jsm @@ -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; +});
--- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -70,17 +70,17 @@ EXTRA_JS_MODULES += [ 'ShortcutUtils.jsm', 'Sntp.jsm', 'SpatialNavigation.jsm', 'Sqlite.jsm', 'Task.jsm', 'TelemetryTimestamps.jsm', 'Timer.jsm', 'Troubleshoot.jsm', - 'UpdateChannel.jsm', + 'UpdateUtils.jsm', 'WebChannel.jsm', 'WindowDraggingUtils.jsm', 'ZipUtils.jsm', ] EXTRA_PP_JS_MODULES += [ 'AppConstants.jsm', 'SessionRecorder.jsm',
--- a/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js +++ b/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js @@ -6,16 +6,19 @@ const URL_HOST = "http://localhost"; var GMPScope = Cu.import("resource://gre/modules/GMPInstallManager.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 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(); function run_test() {Cu.import("resource://gre/modules/Preferences.jsm") Preferences.set("media.gmp.log.dump", true); Preferences.set("media.gmp.log.level", 0); run_next_test(); } @@ -425,17 +428,17 @@ function* test_checkForAddons_installAdd let zipFileName = "test_" + id + "_GMP.zip"; let zipURL = URL_HOST + ":" + testserverPort + "/" + zipFileName; do_print("zipURL: " + zipURL); 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; } let responseXML = "<?xml version=\"1.0\"?>" + "<updates>" + @@ -451,17 +454,16 @@ function* test_checkForAddons_installAdd overrideXHR(200, responseXML); let installManager = new GMPInstallManager(); let gmpAddons = yield installManager.checkForAddons(); do_check_eq(gmpAddons.length, 1); let gmpAddon = gmpAddons[0]; do_check_false(gmpAddon.isInstalled); - GMPInstallManager.overrideLeaveDownloadedZip = true; try { let extractedPaths = yield installManager.installAddon(gmpAddon); if (wantInstallReject) { do_check_true(false); // installAddon() should have thrown. } do_check_eq(extractedPaths.length, 1); let extractedPath = extractedPaths[0]; @@ -469,50 +471,35 @@ function* test_checkForAddons_installAdd let extractedFile = Cc["@mozilla.org/file/local;1"]. createInstance(Ci.nsIFile); extractedFile.initWithPath(extractedPath); do_check_true(extractedFile.exists()); 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)); do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, "", gmpAddon.id), "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); // Cleanup 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"); } } } add_task(test_checkForAddons_installAddon.bind(null, "1", true, false)); add_task(test_checkForAddons_installAddon.bind(null, "2", false, false)); @@ -794,55 +781,16 @@ function overrideXHR(status, response, o registrar.registerFactory(overrideXHR.myxhr.classID, overrideXHR.myxhr.classDescription, overrideXHR.myxhr.contractID, overrideXHR.myxhr); 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 * @param data The data to go inside the zip for the filename entry1.info */ function createNewZipFile(zipName, data) { // Create a zip file which will be used for extracting let stream = Cc["@mozilla.org/io/string-input-stream;1"]. createInstance(Ci.nsIStringInputStream);
new file mode 100644 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_UpdateUtils_updatechannel.js @@ -0,0 +1,38 @@ +/* -*- 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 { 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"; +const PREF_PARTNER_A = "app.partner.test_partner_a"; +const TEST_PARTNER_A = "TestPartnerA"; +const PREF_PARTNER_B = "app.partner.test_partner_b"; +const TEST_PARTNER_B = "TestPartnerB"; + +add_task(function* test_updatechannel() { + let defaultPrefs = new Preferences({ defaultBranch: true }); + let currentChannel = defaultPrefs.get(PREF_APP_UPDATE_CHANNEL); + + 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(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(UpdateUtils.UpdateChannel, + TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B); + do_check_eq(UpdateUtils.getUpdateChannel(true), + TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B); + do_check_eq(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL); +});
new file mode 100644 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_UpdateUtils_url.js @@ -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); +});
--- a/toolkit/modules/tests/xpcshell/xpcshell.ini +++ b/toolkit/modules/tests/xpcshell/xpcshell.ini @@ -52,12 +52,14 @@ skip-if = toolkit == 'android' [test_sqlite_shutdown.js] skip-if = toolkit == 'android' [test_task.js] skip-if = toolkit == 'android' [test_TelemetryTimestamps.js] 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] skip-if = toolkit == 'android'
--- a/toolkit/mozapps/extensions/AddonManager.jsm +++ b/toolkit/mozapps/extensions/AddonManager.jsm @@ -854,16 +854,23 @@ var AddonManagerInternal = { AddonManager.shutdown.addBlocker(name, AMProviderShutdown); } this.pendingProviders.delete(aProvider); this.providers.add(aProvider); 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. */ startup: function AMI_startup() { try { if (gStarted) return; @@ -1459,19 +1466,19 @@ var AddonManagerInternal = { backgroundUpdateCheck: function AMI_backgroundUpdateCheck() { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); 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"); Services.obs.notifyObservers(null, "addons-background-update-start", null); if (this.updateEnabled) { let scope = {}; Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope); @@ -1608,16 +1615,25 @@ var AddonManagerInternal = { } }); aInstall.install(); } } } + 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", null); }.bind(this)); // Fork the promise chain so we can log the error and let our caller see it too. buPromise.then(null, e => logger.warn("Error in background update", e)); return buPromise;
--- a/toolkit/mozapps/extensions/internal/GMPProvider.jsm +++ b/toolkit/mozapps/extensions/internal/GMPProvider.jsm @@ -14,16 +14,17 @@ Cu.import("resource://gre/modules/XPCOMU Cu.import("resource://gre/modules/AddonManager.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); 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"); XPCOMUtils.defineLazyModuleGetter( this, "setTimeout", "resource://gre/modules/Timer.jsm"); const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties"; const STRING_TYPE_NAME = "type.%ID%.name"; @@ -485,18 +486,18 @@ GMPWrapper.prototype = { }, validate: function() { if (!this.isInstalled) { // Not installed -> Valid. 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 { installed: true, mismatchedABI: true, valid: false }; }
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm @@ -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; + } + }) +}
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -37,16 +37,20 @@ XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); 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", Ci.nsIBlocklistService); XPCOMUtils.defineLazyServiceGetter(this, "ChromeRegistry", "@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry"); @@ -94,16 +98,17 @@ const PREF_XPI_PERMISSIONS_BRANCH = const PREF_XPI_UNPACK = "extensions.alwaysUnpack"; const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts"; const PREF_INSTALL_REQUIRESECUREORIGIN = "extensions.install.requireSecureOrigin"; const PREF_INSTALL_DISTRO_ADDONS = "extensions.installDistroAddons"; 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"; const PREF_CHECKCOMAT_THEMEOVERRIDE = "extensions.checkCompatibility.temporaryThemeOverride_minAppVersion"; const PREF_EM_HOTFIX_ID = "extensions.hotfix.id"; const PREF_EM_CERT_CHECKATTRIBUTES = "extensions.hotfix.cert.checkAttributes"; @@ -305,16 +310,35 @@ LAZY_OBJECTS.forEach(name => { let objs = loadLazyObjects(); return objs[name]; }, configurable: true }); }); +// 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") { if ((aAddon.defaultLocale && aAddon.defaultLocale.creator == item.creator) || (aAddon.selectedLocale && aAddon.selectedLocale.creator == item.creator)) { return item; } } @@ -2754,16 +2778,101 @@ this.XPIProvider = { getService(Ci.nsIWindowWatcher); ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, variant); // Ensure any changes to the add-ons list are flushed to disk Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, !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. */ verifySignatures: function XPI_verifySignatures() { XPIDatabase.getAddonList(a => true, (addons) => { Task.spawn(function*() { let changes = { enabled: [], @@ -7351,50 +7460,120 @@ Object.assign(SystemAddonInstallLocation /** * Tests whether updated system add-ons are expected. */ isActive: function() { 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."); + if (addon.version != this._addonSet.addons[id].version) { + logger.warn(`Expected system add-on ${id} to be version ${this._addonSet.addons[id].version} but was ${addon.version}.`); return false; } - if (!addon.bootstrap) { - logger.warn("System add-on " + id + " isn't restartless."); + if (!this.isValidAddon(addon)) return false; - } - - if (addon.version != this._addonSet.addons[id].version) { - logger.warn("System add-on " + id + " wasn't the correct version."); - 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 /** * An object that identifies a registry install location for add-ons. The location * consists of a registry key which contains string values mapping ID to the * path where an add-on is installed *
--- a/toolkit/mozapps/extensions/internal/moz.build +++ b/toolkit/mozapps/extensions/internal/moz.build @@ -7,16 +7,17 @@ EXTRA_JS_MODULES.addons += [ 'AddonLogging.jsm', 'AddonRepository.jsm', 'AddonRepository_SQLiteMigrator.jsm', 'AddonUpdateChecker.jsm', 'Content.js', 'GMPProvider.jsm', 'LightweightThemeImageOptimizer.jsm', + 'ProductAddonChecker.jsm', 'SpellCheckDictionaryBootstrap.js', 'WebExtensionBootstrap.js', ] # Don't ship unused providers on Android if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android': EXTRA_JS_MODULES.addons += [ 'PluginProvider.jsm',
--- a/toolkit/mozapps/extensions/nsBlocklistService.js +++ b/toolkit/mozapps/extensions/nsBlocklistService.js @@ -18,18 +18,18 @@ try { // process. We're used in the child process (for now), so guard against // this. Components.utils.import("resource://gre/modules/AddonManager.jsm"); } catch (e) { } 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", "resource://gre/modules/Task.jsm"); const TOOLKIT_ID = "toolkit@mozilla.org"; const KEY_PROFILEDIR = "ProfD"; const KEY_APPDIR = "XCurProcD"; @@ -554,17 +554,17 @@ Blocklist.prototype = { dsURI = dsURI.replace(/%PRODUCT%/g, gApp.name); // Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't). if (gApp.version) dsURI = dsURI.replace(/%VERSION%/g, gApp.version); dsURI = dsURI.replace(/%BUILD_ID%/g, gApp.appBuildID); 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)); dsURI = dsURI.replace(/%DISTRIBUTION_VERSION%/g, getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION)); dsURI = dsURI.replace(/%PING_COUNT%/g, pingCountVersion); dsURI = dsURI.replace(/%TOTAL_PING_COUNT%/g, pingCountTotal); dsURI = dsURI.replace(/%DAYS_SINCE_LAST_PING%/g, daysSinceLastPing);
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/bad.txt @@ -0,0 +1,1 @@ +Not an xml file! \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/bad.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<foobar></barfoo> \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/bad2.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<test></test>
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/empty.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<updates> + <addons></addons> +</updates>
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/good.xml @@ -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>
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/missing.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<updates></updates>
new file mode 100644 index 0000000000000000000000000000000000000000..51b00475a9641ea9d608874a3ab7679da3a4374b GIT binary patch literal 452 zc$^FHW@Zs#U}E54_?u^9#dA2DRf~~<A%uy6ftx{;Av3SIBrzvPuP7xgG=!6Z`R(;m zPY@2RU}5;mD8f)0<mh+UKw!^vQMJ8_JN=layLm<}3F>4Pa^m=!CAB@`#_JRJCLMTP zI9DyV<<;!;^s=<;=fu~BJ&BG`=N0*wGwVpVQP-#IS7-g;+M)E%WsUK%S<7acl?Nw( z&oP^SK626~lcn2k1%78f5zUn+<o=|h$w0Vj$<=4!T1TCmO+4Styx{&-<y%?H6YYoF z!mhnY7WkLQ7jcB0`=;kq&w>+E7tLzE!`U8JT5_uOTuP|ifs?i!UE8+CEYjcYy?ajS z8plGZ?O!gpMb-1SG`8M($7*+F>!j#D=h``2kGE-L^!%E%W6!hu!CPCtPtFy8#3UhH zdHeBp*^fM58gJ}(X{=0b>ML;8jErXXkXxv9p6O?ovig*H-&tb1i#~sHx%bD*`FKOi zcd5uS!$os`<_Ejg2Y53wi8JF0X<h~p0CE|YG=f+t;m!&P_s|Lkh5&C?Hi$|_1~(v` I0n)+%08F#C6#xJL
deleted file mode 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_1_1/bootstrap.js +++ /dev/null @@ -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) { -}
deleted file mode 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_1_1/install.rdf +++ /dev/null @@ -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>
deleted file mode 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_2_1/bootstrap.js +++ /dev/null @@ -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) { -}
deleted file mode 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_2_1/install.rdf +++ /dev/null @@ -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>
deleted file mode 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_1_2/bootstrap.js +++ /dev/null @@ -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) { -}
deleted file mode 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_1_2/install.rdf +++ /dev/null @@ -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>
deleted file mode 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_3_1/bootstrap.js +++ /dev/null @@ -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) { -}
deleted file mode 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_3_1/install.rdf +++ /dev/null @@ -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>
deleted file mode 100644 index 60bcde626ffd9307c052b3cecc45812f716c5c0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
rename from toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system1@tests.mozilla.org.xpi rename to toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_1.xpi
rename from toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app3/features/system1@tests.mozilla.org.xpi rename to toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_1_badcert.xpi
rename from toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system1@tests.mozilla.org.xpi rename to toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_2.xpi
rename from toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system2@tests.mozilla.org.xpi rename to toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system2_1.xpi
new file mode 100644 index 0000000000000000000000000000000000000000..7af871d36dec992e405752bc9db49e6dae4ea085 GIT binary patch literal 858 zc$^FHW@Zs#U}E54P|DDD&pm(r>k|eBh7F7i3_J`n3`zO<CB-F0i3NID#i1db49x$H zK8OAS;?fFk21b@|KxIJP3=C5z_<J)u@*I8tTl3JIZClQG^@};KkgLi(x+q+-{Bl`J z+9s8mqN4kEZ(i_7en~~m$^P$go0i-NKC~oh^Dl*K;%B3z-4>qFoo9A--p0-=Pa7`f zOk}^m=mvXd+NKq&rcKgVqxXQP$EGuB&U|gJkKJG1I2TN-$QOHm=k3$4Wm_ZrKiru& zrEu%%m7)HeH8*z)ygIjXHsfMpFX4)nar+Hg6VCqCXM3<Y{QM4K?G>k+AFo!*D)wKp z=*_Q<(@hOzU;7=52#;akBRyg1j@dWe_ZD5>e#QA(pWNk-H_R41mOfa|_W#7`m+#~E zFb2RwN(B^BLKZqkK<}3_F)(lgLn<?`xFj(rN3SR)4IEG`#-BqOj4=YLb<)|qLk0pZ z@9VhC=g6w(RV#2U^q3N@zxK**MK+zE>YL1cUtGQ}A>^Q7y8TSuW^3W4bG}DClz+Hq zOWL$2nbXc_x12ivl&ih-<K`6p3nEj>VyBm#Us-BB%YS+BEW=Y0g<q%rXlA@8{8*jy z&~bGs?uGe#ZvFFGS`l+#)0<YV+7kas1$>W-H2)ks$>zj!=K8kQ$V*nib7EUf<aPwP z6f18(<Xj-DKh;Tm)4D$gn=icKJ#-{KUh5^N#KYptT}vb9@V-~xuuaUy;$FyuD8ZTC zEq*tI6)T>uVf>Sq^+&Nm-?G5xL~8HDsE~L=vA8RyoNFUu#Mm>QH0+++;CfkO=R*Uv zoqrXY&Az>z-Fu&V=|4>yi!CPipU<mgS=SOh;d}GHDkl9y6T0uQc0XxWdeNts`D4A- z7xn;eMkYCCTuD@d0R(_dh9!+47HWcJg(O(CI6yWLGb)iy)B;i{5z7jRSPTPM*+80@ NfN(L8UdROE0RUxTQM3R6
new file mode 100644 index 0000000000000000000000000000000000000000..5c8570dcc4086cb1a3e9270a2f8ef64e28899c02 GIT binary patch literal 858 zc$^FHW@Zs#U}E54Fv!q$Kk&AJ=?Mb^!v;nM1|9|(hNS%blH!u0!~(sn;?NLI24)%K z&!HkfTw1}+z{v6qs0^r^fnn+de{W_-o}=%7YaW`jZOfUidpb=4)i0KIxvn=lx9shS zHz}S*+FEtx>5fP3JwE=JB>#SIies!l=j5)lQj2Z`m}KWlv>H|0<j#^!z8pAP*=w_- zT$B4o{!24Y?pbA{8W<6KKvJSo=;*s2F@at2?&lTV(mTu3{++w4x88j9>8~8~Z!9)i zx6QJWcZ$N4z1<u0=I)X-uv%!+IQ`XE?qH5<cm5|a*j0b+iQd?>>xT6{twm<vU9?WE zldq0szP$0BNKfg}(gV90BfECbti7ULUh91~J@=)dY~NQw+4~&-jDNVt`PKh_#2Ww) zDSco_nQrVTHUfITl!<|X8yHfVdBr7(IXQYoDQVz<lENNPt&`5?9WoGTd0)q6K1a6w zf?S7_hS1Y%-r8UH2{2FlBYe|a_}3THHWq=Yv!c`X-<;bSbk2J1i|qw*QO4fKGN+x< zb~@Gnw5zT2<K~q98zOVI=g!_X`N}R?dH?0X^PbwApSgPb8F^l(<2~}KE%WAO=(Ozn zob_9IYSRvd^PAH3zMQo6I%Mf}dg%vgb!i?$jWur>(@!3p`RI|>l8+o-hP-Zht;Zi& z#cH+crNsVR?7r}GIFE3B^~xvd2@U$bt378Ojr?!Nkez*Taq}{#a)F6Ey`J>g8oGs_ zlUkC$Y}tFJL{5$=8(&K?M}^Ec6pOuL%Dwi}4jsM|2NZYAJ>Ys-V<)4`q&xK!;-r6- zojvxxHR!*{JK3mN>%GqxvgEZ?3+#%kw`>kyc>bt-lk2B`r6-Mgl|R;Nec}%AW@M6M z#+5`R7(f8XWLVM&VxcBjR!D+Hivwg6F{2XML@gkN60xk1h{Z6Fl?|kc2?!Sh>4i)n F9sqbtL{b0%
rename from toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system3@tests.mozilla.org.xpi rename to toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system3_1.xpi
new file mode 100644 index 0000000000000000000000000000000000000000..5b50b8556dcf426f67b134cfc084672e08bb5b53 GIT binary patch literal 858 zc$^FHW@Zs#U}E54D9X@wPdT;n`4a{Ph7F7i3_J`n3`zO<CB-F0i3NID#i1db49qi( zKZo`MacKoN10%~fpfaFt28O8<{JohSd5*sSt$Aq9wk>D6?&&lIRKHl-<+|SJ+_JYP z-lTXMX=~M$r#l|C_xSi@lKlI<DUPxJoRhoGN-eq(V3M6H(P~s}lRHZ``EuZFWv|VS za!u|V`7g~pxo4G)YG6d{0ZEBUp`-7f!~}N5yPsEdOYbaC`*-fH-g@)Zr@wN@-(GCA zZkuH#?-Ye8d%HL0&D|wwV71Vsar&#T+`$~z?)*<;u&e&s6TPu%*A44^T8qrSyJ($S zCtn@Oe0k$Lk)G0{r3ZF1Mt1F<S$jphyw>|}dhSa@*}kuWviCXu8UJvP^Q-^=h&KQp zQf0u9ik&uPwh_?#rA!PA+`y2^%quQQ%*oL!N=X9;)O_p#)jH{H-XQ~lmiKjB=5u7* zFUWN`X$U>N=B@p8p8)f;Kf*V?g@1i9ZDSFbIx9MD|IN9bLFcU3zSv$67iH{yEOXi! zZKqTHPrKSWKW<LxzacVbd+zLQldtTOmG@sBJnyN^`I)P?pONQvI^H9%+A?omhEB`A z&so2fr#9_SIKL@f@5@PBuS1qzr<Z<^R+r{6)L8SDG5zGhnU5Z6E&0gdWytH6*LwVc zRjgL4UP|oG#qJB$*Gs7V`xbQa`~imeSz(p4jz<2sW5~|FxVU+lQ@OyzonB9RYz^JQ z&q*!GU$*Q$Qz9qFl#Q>Yn4?1G8;ZqVG38#XRv0NWq0za(y0Lp%U=g#0%Fh1^d(FPQ zoz;7vd+9&TJJwrF;-}Ah%yEa~yF$s{e?@HTo#uDjvvfa^cRj%z`{_e`=o9e(Z$>6L zW?V^Bf&m18Ook<mAQozZWrZYIv^YRE5i=^0P1FKXC=tsFiC7E+S=m6En1FCGkY30H G;sF5a)<`M<
new file mode 100644 index 0000000000000000000000000000000000000000..91a7d9ffc1e848636ef732a8864a6d4cb0ee8869 GIT binary patch literal 858 zc$^FHW@Zs#U}E54sLs%KKl$RHz!L@rh7F7i3_J`n3`zO<CB-F0i3NID#i1db49u&H zKZh;^;?fFk21b@|KxIJP3=C5z_<J)u@*I8tTl3JIZClQ8?TutvSoLD5Xm?!d9pBp% zXP%st5*G63-MOZ&`U#Ids@mVHJ~=7LpL258*;maG@g~{160JttD{h%oA5#mO&g`|> zRj#HxNnT~v$wjN8ycTWP)nIJ!Nu{f>Zk@(a{Y#tq4@N%P8@Z==`}FI#ucp}^Dy|MX zc2#eAn62uE)b~y+te5Ap_4sM{K3H1y_mD`F>G%B-4d>%*AN#FYpvT|0zH7x<+lgJ9 zULTLUcA#X9HEVKgq3nk|hiQ-U(&WFMioGAiA8B3Xb3FNWLtj42f5|_}`pfs%e`E`Q zhg20Xq*zT=ju-*GU&_S5zzqzk%)H`~#GD+xqLegnKrP1}P_2{B<~17#w7jq5nm(tO zb4$B|lT_!kYu?&l-!)8*_{6_yy3?;OrE?sFmIm!K{C)Nv&!PxkuSfZLa}BB_&puX( zn#v(6f0gZbXW8i&3>ThF`Bp3cJu>IbymR8+tiD0BmK`y(e$*#*!(q#8SC0Af63*=0 z^;zq)@{*K9$5|@pr>ve_E%vimLR_?btElb|i5a=R?59_3w%Wg=XrZiuW@l2=n;p#W ztzxxyoN^7`wU~L`rtlBK<<%<}q$f1#`<~YEJCsv1zhSN6g9DqkvE2T6AV#(1*gTGd z(%B{t%<n!Z=UbC>z(Dc3=`78-y0(alyCz4rpE|V3z(JV(%sb09x5K(-9h~yyUvtl` zE9Dv4<qv}X1y_`4EZnbW`<!uy;5$dNo?oXW_i!ca{SwfbP%q-hU*h<;K6FuhfHxzP z95b#YD!~8(KqkYIMi2`%!LmXUELt2On}`{e$R=t5DU^t1g+wfdfvjvGO-w+z7)UQ< G0`UOO<xU0w
new file mode 100644 index 0000000000000000000000000000000000000000..a11a8eb494f1cb2e25f1016a71b43bc7f9c936cd GIT binary patch literal 857 zc$^FHW@Zs#U}E54NY2o9f4d<%>Ink_!v;nM1|9|(hNS%blH!u0!~(sn;?NLI2IflR z&!I&?Tw1}+z{v6qs0^r^fnn+de{W_-o}=%7YaW`jZOa+0icK6&Zx>G265D&qV)i!W z&68D6u37Qp?p#*U{|ZMRd)MvxK8dx|R@Fz#EW0Dg`Qn<bi6W=oeB6>**5m7*+oy4s zt&&6TsGUc;+UHfDrnn@QGNvay@f6uTx!6^+PS&<vVy^1FnLn+|ckj8kM(?_EO-}!* z9k-r6F%4)6_@;MkSLv&?120-GvBa&quJ5B5QT)G~@yDC1#(77%UM0Q%v8v<N9pRN4 zyX)Sh^7<U#6>M-#^g82vwiL~GY2O0Z-}$OrI)CfZ<0Tf?+)HYdf1H0HUupUGZ?|~> zJfsqVA+`MRgVTmU@0T($FmMAyDl@ORBrzvPuP7xA98jg$1FCh>*}Ovr0xj?BxXkDD zI(zZED2j9zy<R%CDqe_r+8^PY-on4Wn6|M9Oq~^-w*Thb&LADDwJ)|8#6=nVt35V} zoYEp9zuJ^n`0?|TavM)t=HD*6W7og-_Q#VsW{)$EZSL7wb^6amg_1Kr0-avG>^v6p z!g~ES`ykB@?oB3hix&F73lusR7`3Kl{hS<wBMUZcWS^tXp8ly@d-^eDjiV+KJ4NN1 zUze;Du$o@_vR|%s-oB(6KW=-o+A}lPWJa|uD!jIz|G=@RFE18oUX*o7Stn$f?B8>7 z>t?o>vsb>{?GVApl=^1Zu?DfH={>H~OV2v3&WsUbPkA74$M3<W%qeRa+Eu>GFS*{f zFE;q+-hfy05AAjK{(SX&-+|r&ey8iN_0vCSecAcLd4tElz!sYeto;8&J?j_)ycwC~ zm~o|02?h`VG8vXMf>@~Ol@*d+(V_s^M9i2(Hc<;mp~NdIBwjHLWMu<sVgka&Kzaca Ghz9`i<5GnH
new file mode 100644 index 0000000000000000000000000000000000000000..f4e07bf300d17fb2306c1dc189b5fafea56a952a GIT binary patch literal 857 zc$^FHW@Zs#U}E54$jQ)lf3j88<0%6J!v;nM1|9|(hNS%blH!u0!~(sn;?NLI2Ig+# z&!M$ITw1}+z{v6qs0^r^fnn+de{W_-o}=%7YaW`jZOfTg2fJMbviSFgY<v-V?B*8P znR9hK(_D|ff2XtJ2fL<R{HMozr|YbEt(h3KG16H?x+`rh@8W>8{^h=#lXI-Hid{0z zPB?$i_~vM3JTrXiauu%C+!oBr{)=wRoulpb@%0PKBQJD5o;!1Y=k2Fo(@J;BJxs0+ zId;`deSM=)%cgR}YionencdW<sC@|9TbIanaMtg5o&)LY=M`#)1&CK44|l!tY@Ubf z&9BM&*$GzH<~D6yw}Y?3T*150d~<)*({=ePf3MNCU3Pe*-J-|R2kY7XpE&*Uef%EA z0C-Sk1A}UMx=)P((Ep`O3=G`BpvufEE=kPE(JM+x1BX;6_K<3wbT;phfk4aqIxh1$ zz0Og5E{Y<ZMX#4mt%?_7p7uxhrnm5~FQ#oQ0#j#2r|rKvw=+n`YVC{d1#wZv{%Vg+ zBB!*7$gei#6@L8uq};}nmif2K?%4IOz5VfIj@jePW1D++R-OKHQK96_k3gpvFFTLL zys%!s%|1x8gL{)n?L(*ecNVM6S-d5JGv4xMLWg5wGQWjC-?=B^;d;I90bQ8}McOuO z*Ix!Hmgv2{ByY=QTYJ>t<M!!Xb!==uE^Xm)eY~b#zM*ΠIrIi>y0N#Hbb>n<ue2 zJDvBEdGO`(#tlp?r*4$?GU}c>FVU^{dRA-L<sCYFCk`m?n0p}gvPL8$pXWP!kM+E@ zyO({cUU<d6wYq)Uv(@iq8>Ju0wXC~-J~O|?PQ0E&?4*6y1omAI9`4sVsUP6Y$Rx*% zD}_ohfB=xmu%r>hLQSu%ko1Zc1IQ*~MkKO{+CT~=T3I2{ieVrt8%Prq5H1GN3z$GW E01_-l`v3p{
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_ProductAddonChecker.js @@ -0,0 +1,264 @@ +"use strict"; + +Components.utils.import("resource://gre/modules/addons/ProductAddonChecker.jsm"); +Components.utils.import("resource://testing-common/httpd.js"); +Components.utils.import("resource://gre/modules/osfile.jsm"); + +const LocalFile = new Components.Constructor("@mozilla.org/file/local;1", AM_Ci.nsIFile, "initWithPath"); + +let testserver = new HttpServer(); +testserver.registerDirectory("/data/", do_get_file("data/productaddons")); +testserver.start(); +let root = testserver.identity.primaryScheme + "://" + + testserver.identity.primaryHost + ":" + + testserver.identity.primaryPort + "/data/" + +/** + * 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 = AM_Cc["@mozilla.org/network/file-input-stream;1"]. + createInstance(AM_Ci.nsIFileInputStream); + // Open as RD_ONLY with default permissions. + fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0); + + let stream = AM_Cc["@mozilla.org/binaryinputstream;1"]. + createInstance(AM_Ci.nsIBinaryInputStream); + stream.setInputStream(fileStream); + let bytes = stream.readByteArray(stream.available()); + fileStream.close(); + return bytes; +} + +/** + * Compares binary data of 2 files and returns true if they are the same + * + * @param file1 The first file to compare + * @param file2 The second file to compare +*/ +function compareFiles(file1, file2) { + return compareBinaryData(getBinaryFileData(file1), getBinaryFileData(file2)); +} + +add_task(function* test_404() { + try { + let addons = yield ProductAddonChecker.getProductAddonList(root + "404.xml"); + do_throw("Should not have returned anything"); + } + catch (e) { + do_check_true(true, "Expected to throw for a missing update file"); + } +}); + +add_task(function* test_not_xml() { + try { + let addons = yield ProductAddonChecker.getProductAddonList(root + "bad.txt"); + do_throw("Should not have returned anything"); + } + catch (e) { + do_check_true(true, "Expected to throw for a non XML result"); + } +}); + +add_task(function* test_invalid_xml() { + try { + let addons = yield ProductAddonChecker.getProductAddonList(root + "bad.xml"); + do_throw("Should not have returned anything"); + } + catch (e) { + do_check_true(true, "Expected to throw for invalid XML"); + } +}); + +add_task(function* test_wrong_xml() { + try { + let addons = yield ProductAddonChecker.getProductAddonList(root + "bad2.xml"); + do_throw("Should not have returned anything"); + } + catch (e) { + do_check_true(true, "Expected to throw for a missing <updates> tag"); + } +}); + +add_task(function* test_missing() { + let addons = yield ProductAddonChecker.getProductAddonList(root + "missing.xml"); + do_check_eq(addons, null); +}); + +add_task(function* test_empty() { + let addons = yield ProductAddonChecker.getProductAddonList(root + "empty.xml"); + do_check_true(Array.isArray(addons)); + do_check_eq(addons.length, 0); +}); + +add_task(function* test_good_xml() { + let addons = yield ProductAddonChecker.getProductAddonList(root + "good.xml"); + do_check_true(Array.isArray(addons)); + + // There are three valid entries in the XML + do_check_eq(addons.length, 5); + + let addon = addons[0]; + do_check_eq(addon.id, "test1"); + do_check_eq(addon.URL, "http://example.com/test1.xpi"); + do_check_eq(addon.hashFunction, undefined); + do_check_eq(addon.hashValue, undefined); + do_check_eq(addon.version, undefined); + do_check_eq(addon.size, undefined); + + addon = addons[1]; + do_check_eq(addon.id, "test2"); + do_check_eq(addon.URL, "http://example.com/test2.xpi"); + do_check_eq(addon.hashFunction, "md5"); + do_check_eq(addon.hashValue, "djhfgsjdhf"); + do_check_eq(addon.version, undefined); + do_check_eq(addon.size, undefined); + + addon = addons[2]; + do_check_eq(addon.id, "test3"); + do_check_eq(addon.URL, "http://example.com/test3.xpi"); + do_check_eq(addon.hashFunction, undefined); + do_check_eq(addon.hashValue, undefined); + do_check_eq(addon.version, "1.0"); + do_check_eq(addon.size, 45); + + addon = addons[3]; + do_check_eq(addon.id, "test4"); + do_check_eq(addon.URL, undefined); + do_check_eq(addon.hashFunction, undefined); + do_check_eq(addon.hashValue, undefined); + do_check_eq(addon.version, undefined); + do_check_eq(addon.size, undefined); + + addon = addons[4]; + do_check_eq(addon.id, undefined); + do_check_eq(addon.URL, "http://example.com/test5.xpi"); + do_check_eq(addon.hashFunction, undefined); + do_check_eq(addon.hashValue, undefined); + do_check_eq(addon.version, undefined); + do_check_eq(addon.size, undefined); +}); + +add_task(function* test_download_nourl() { + try { + let path = yield ProductAddonChecker.downloadAddon({}); + + yield OS.File.remove(path); + do_throw("Should not have downloaded a file with a missing url"); + } + catch (e) { + do_check_true(true, "Should have thrown when downloading a file with a missing url."); + } +}); + +add_task(function* test_download_missing() { + try { + let path = yield ProductAddonChecker.downloadAddon({ + URL: root + "nofile.xpi", + }); + + yield OS.File.remove(path); + do_throw("Should not have downloaded a missing file"); + } + catch (e) { + do_check_true(true, "Should have thrown when downloading a missing file."); + } +}); + +add_task(function* test_download_noverify() { + let path = yield ProductAddonChecker.downloadAddon({ + URL: root + "unsigned.xpi", + }); + + let stat = yield OS.File.stat(path); + do_check_false(stat.isDir); + do_check_eq(stat.size, 452) + + do_check_true(compareFiles(do_get_file("data/productaddons/unsigned.xpi"), new LocalFile(path))); + + yield OS.File.remove(path); +}); + +add_task(function* test_download_badsize() { + try { + let path = yield ProductAddonChecker.downloadAddon({ + URL: root + "unsigned.xpi", + size: 400, + }); + + yield OS.File.remove(path); + do_throw("Should not have downloaded a file with a bad size"); + } + catch (e) { + do_check_true(true, "Should have thrown when downloading a file with a bad size."); + } +}); + +add_task(function* test_download_badhashfn() { + try { + let path = yield ProductAddonChecker.downloadAddon({ + URL: root + "unsigned.xpi", + hashFunction: "sha2567", + hashValue: "9b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3", + }); + + yield OS.File.remove(path); + do_throw("Should not have downloaded a file with a bad hash function"); + } + catch (e) { + do_check_true(true, "Should have thrown when downloading a file with a bad hash function."); + } +}); + +add_task(function* test_download_badhash() { + try { + let path = yield ProductAddonChecker.downloadAddon({ + URL: root + "unsigned.xpi", + hashFunction: "sha256", + hashValue: "8b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3", + }); + + yield OS.File.remove(path); + do_throw("Should not have downloaded a file with a bad hash"); + } + catch (e) { + do_check_true(true, "Should have thrown when downloading a file with a bad hash."); + } +}); + +add_task(function* test_download_works() { + let path = yield ProductAddonChecker.downloadAddon({ + URL: root + "unsigned.xpi", + size: 452, + hashFunction: "sha256", + hashValue: "9b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3", + }); + + let stat = yield OS.File.stat(path); + do_check_false(stat.isDir); + + do_check_true(compareFiles(do_get_file("data/productaddons/unsigned.xpi"), new LocalFile(path))); + + yield OS.File.remove(path); +});
--- a/toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js @@ -1,16 +1,17 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const {classes: Cc, interfaces: Ci, utils: Cu} = Components; var GMPScope = Cu.import("resource://gre/modules/addons/GMPProvider.jsm"); Cu.import("resource://gre/modules/AppConstants.jsm"); +Cu.import("resource://gre/modules/UpdateUtils.jsm"); XPCOMUtils.defineLazyGetter(this, "pluginsBundle", () => Services.strings.createBundle("chrome://global/locale/plugins.properties")); XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm"); var gMockAddons = new Map(); @@ -333,17 +334,17 @@ add_task(function* test_pluginRegistrati yield promiseRestartManager(); Assert.equal(addedPaths.indexOf(file.path), -1); Assert.deepEqual(removedPaths, [file.path]); // Setting the ABI to expected ABI should cause registration at startup. clearPaths(); gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id), TEST_VERSION); - gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, addon.id), GMPScope.GMPUtils.ABI()); + gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, addon.id), UpdateUtils.ABI); yield promiseRestartManager(); Assert.notEqual(addedPaths.indexOf(file.path), -1); Assert.deepEqual(removedPaths, []); // Check that clearing the version doesn't trigger registration. clearPaths(); gPrefs.clearUserPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id)); Assert.deepEqual(addedPaths, []);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_system_reset.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_reset.js @@ -1,19 +1,31 @@ // Tests that we reset to the default system add-ons correctly when switching // application versions const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet"; // Enable signature checks for these tests Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true); -const featureDir = gProfD.clone(); -featureDir.append("features"); +const featureDir = FileUtils.getDir("ProfD", ["features"]); + +// Build the test sets +let dir = FileUtils.getDir("ProfD", ["sysfeatures", "app1", "features"], true); +do_get_file("data/system_addons/system1_1.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi"); +do_get_file("data/system_addons/system2_1.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi"); -const distroDir = do_get_file("data/system_addons/app0"); +dir = FileUtils.getDir("ProfD", ["sysfeatures", "app2", "features"], true); +do_get_file("data/system_addons/system1_2.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi"); +do_get_file("data/system_addons/system3_1.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi"); + +dir = FileUtils.getDir("ProfD", ["sysfeatures", "app3", "features"], true); +do_get_file("data/system_addons/system1_1_badcert.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi"); +do_get_file("data/system_addons/system3_1.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi"); + +const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "app0"], true); registerDirectory("XREAppDist", distroDir); createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "0"); function makeUUID() { let uuidGen = AM_Cc["@mozilla.org/uuid-generator;1"]. getService(AM_Ci.nsIUUIDGenerator); return uuidGen.generateUUID().toString(); @@ -131,20 +143,20 @@ add_task(function* test_downgrade() { // Fake a mid-cycle install add_task(function* test_updated() { // Create a random dir to install into let dirname = makeUUID(); FileUtils.getDir("ProfD", ["features", dirname], true); featureDir.append(dirname); // Copy in the system add-ons - let file = do_get_file("data/system_addons/app1/features/system2@tests.mozilla.org.xpi"); - file.copyTo(featureDir, file.leafName); - file = do_get_file("data/system_addons/app2/features/system3@tests.mozilla.org.xpi"); - file.copyTo(featureDir, file.leafName); + let file = do_get_file("data/system_addons/system2_1.xpi"); + file.copyTo(featureDir, "system2@tests.mozilla.org.xpi"); + file = do_get_file("data/system_addons/system3_1.xpi"); + file.copyTo(featureDir, "system3@tests.mozilla.org.xpi"); // Inject it into the system set let addonSet = { schema: 1, directory: featureDir.leafName, addons: { "system2@tests.mozilla.org": { version: "1.0" @@ -161,18 +173,18 @@ add_task(function* test_updated() { yield check_installed(true, null, "1.0", "1.0"); yield promiseShutdownManager(); }); // An additional add-on in the directory should be ignored add_task(function* test_skips_additional() { // Copy in the system add-ons - let file = do_get_file("data/system_addons/app1/features/system1@tests.mozilla.org.xpi"); - file.copyTo(featureDir, file.leafName); + let file = do_get_file("data/system_addons/system1_1.xpi"); + file.copyTo(featureDir, "system1@tests.mozilla.org.xpi"); startupManager(false); yield check_installed(true, null, "1.0", "1.0"); yield promiseShutdownManager(); }); @@ -186,18 +198,18 @@ add_task(function* test_revert() { // the default set which is system add-ons 1 and 2. yield check_installed(false, "1.0", "1.0", null); yield promiseShutdownManager(); }); // Putting it back will make the set work again add_task(function* test_reuse() { - let file = do_get_file("data/system_addons/app1/features/system2@tests.mozilla.org.xpi"); - file.copyTo(featureDir, file.leafName); + let file = do_get_file("data/system_addons/system2_1.xpi"); + file.copyTo(featureDir, "system2@tests.mozilla.org.xpi"); startupManager(false); yield check_installed(true, null, "1.0", "1.0"); yield promiseShutdownManager(); }); @@ -209,18 +221,18 @@ add_task(function* test_corrupt_pref() { yield check_installed(false, "1.0", "1.0", null); yield promiseShutdownManager(); }); // An add-on with a bad certificate should cause us to use the default set add_task(function* test_bad_profile_cert() { - let file = do_get_file("data/system_addons/app3/features/system1@tests.mozilla.org.xpi"); - file.copyTo(featureDir, file.leafName); + let file = do_get_file("data/system_addons/system1_1_badcert.xpi"); + file.copyTo(featureDir, "system1@tests.mozilla.org.xpi"); // Inject it into the system set let addonSet = { schema: 1, directory: featureDir.leafName, addons: { "system1@tests.mozilla.org": { version: "2.0"
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_update.js @@ -0,0 +1,469 @@ +// Tests that we reset to the default system add-ons correctly when switching +// application versions +const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet"; +const PREF_SYSTEM_ADDON_UPDATE_URL = "extensions.systemAddon.update.url"; +const PREF_XPI_STATE = "extensions.xpiState"; +const PREF_APP_UPDATE_ENABLED = "app.update.enabled"; + +Components.utils.import("resource://testing-common/httpd.js"); +const { computeHash } = Components.utils.import("resource://gre/modules/addons/ProductAddonChecker.jsm"); + +// Enable signature checks for these tests +//Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true); + +const featureDir = FileUtils.getDir("ProfD", ["features"]); + +// Build the test sets +let dir = FileUtils.getDir("ProfD", ["features", "prefilled"], true); +do_get_file("data/system_addons/system2_2.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi"); +do_get_file("data/system_addons/system3_2.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi"); + +// Mark these in the past so the startup file scan notices when files have changed properly +FileUtils.getFile("ProfD", ["features", "prefilled", "system2@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000; +FileUtils.getFile("ProfD", ["features", "prefilled", "system3@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000; + +const prefilledSet = { + schema: 1, + directory: dir.leafName, + addons: { + "system2@tests.mozilla.org": { + version: "2.0" + }, + "system3@tests.mozilla.org": { + version: "2.0" + }, + } +}; + +dir = FileUtils.getDir("ProfD", ["sysfeatures", "hidden", "features"], true); +do_get_file("data/system_addons/system1_1.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi"); +do_get_file("data/system_addons/system2_1.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi"); + +dir = FileUtils.getDir("ProfD", ["sysfeatures", "prefilled", "features"], true); +do_get_file("data/system_addons/system2_2.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi"); +do_get_file("data/system_addons/system3_2.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi"); + +const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "empty"], true); +registerDirectory("XREAppDist", distroDir); + +createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2"); + +let testserver = new HttpServer(); +testserver.registerDirectory("/data/", do_get_file("data/system_addons")); +testserver.start(); +let root = testserver.identity.primaryScheme + "://" + + testserver.identity.primaryHost + ":" + + testserver.identity.primaryPort + "/data/" +Services.prefs.setCharPref(PREF_SYSTEM_ADDON_UPDATE_URL, root + "update.xml"); + +function makeUUID() { + let uuidGen = AM_Cc["@mozilla.org/uuid-generator;1"]. + getService(AM_Ci.nsIUUIDGenerator); + return uuidGen.generateUUID().toString(); +} + +function* serve_update(xml, perform_update) { + testserver.registerPathHandler("/data/update.xml", (request, response) => { + response.write(xml); + }); + + try { + yield perform_update(); + } + finally { + testserver.registerPathHandler("/data/update.xml", null); + } +} + +// Runs an update check making it use the passed in xml string. Uses the direct +// call to the update function so we get rejections on failure. +function* install_system_addons(xml) { + do_print("Triggering system add-on update check."); + + yield serve_update(xml, function*() { + let { XPIProvider } = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm"); + yield XPIProvider.updateSystemAddons(); + }); +} + +// Runs a full add-on update check which will in some cases do a system add-on +// update check. Always succeeds. +function* update_all_addons(xml) { + do_print("Triggering full add-on update check."); + + yield serve_update(xml, function() { + return new Promise(resolve => { + Services.obs.addObserver(function() { + Services.obs.removeObserver(arguments.callee, "addons-background-update-complete"); + + resolve(); + }, "addons-background-update-complete", false); + + // Trigger the background update timer handler + gInternalManager.notify(null); + }); + }); +} + +// Builds an update.xml file for an update check based on the data passed. +function* build_xml(addons) { + let xml = `<?xml version="1.0" encoding="UTF-8"?>\n\n<updates>\n`; + if (addons) { + xml += ` <addons>\n`; + for (let addon of addons) { + xml += ` <addon id="${addon.id}" URL="${root + addon.path}" version="${addon.version}"`; + if (addon.size) + xml += ` size="${addon.size}"`; + if (addon.hashFunction) + xml += ` hashFunction="${addon.hashFunction}"`; + if (addon.hashValue) + xml += ` hashValue="${addon.hashValue}"`; + xml += `/>\n`; + } + xml += ` </addons>\n`; + } + xml += `</updates>\n`; + + return xml; +} + +function* check_installed(inProfile, ...versions) { + for (let i = 0; i < versions.length; i++) { + let id = "system" + (i + 1) + "@tests.mozilla.org"; + let addon = yield promiseAddonByID(id); + + if (versions[i]) { + // Add-on should be installed + do_check_neq(addon, null); + do_check_eq(addon.version, versions[i]); + do_check_true(addon.isActive); + do_check_false(addon.foreignInstall); + + // Verify the add-ons file is in the right place + let uri = addon.getResourceURI(null); + do_check_true(uri instanceof AM_Ci.nsIFileURL); + + let file = uri.file.parent; + if (inProfile) { + file = file.parent; + do_check_eq(file.leafName, "features"); + file = file.parent; + do_check_eq(file.path, gProfD.path); + } + else { + do_check_eq(file.leafName, "features"); + file = file.parent; + do_check_eq(file.path, distroDir.path); + } + + //do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_SYSTEM); + + // Verify the add-on actually started + let installed = Services.prefs.getCharPref("bootstraptest." + id + ".active_version"); + do_check_eq(installed, versions[i]); + } + else { + if (inProfile) { + // Add-on should not be installed + do_check_eq(addon, null); + } + else { + // Either add-on should not be installed or it shouldn't be active + do_check_true(!addon || !addon.isActive); + } + + try { + Services.prefs.getCharPref("bootstraptest." + id + ".active_version"); + do_throw("Expected pref to be missing"); + } + catch (e) { + } + } + } +} + + +/** + * Defines the set of initial conditions to run each test against. Each should + * define the following properties: + * + * setup: A task to setup the profile into the initial state. + * initialState: The initial expected system add-on state after setup has run. + */ +const TEST_CONDITIONS = { + // Runs tests with no updated or default system add-ons initially installed + blank: { + setup: function*() { + Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET); + distroDir.leafName = "empty"; + }, + initialState: [false, null, null, null, null, null], + }, + + // Runs tests with default system add-ons installed + withAppSet: { + setup: function*() { + Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET); + distroDir.leafName = "prefilled"; + }, + initialState: [false, null, "2.0", "2.0", null, null], + }, + + // Runs tests with updated system add-ons installed + withProfileSet: { + setup: function*() { + Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(prefilledSet)); + distroDir.leafName = "empty"; + }, + initialState: [true, null, "2.0", "2.0", null, null], + }, + + // Runs tests with both default and updated system add-ons installed + withBothSets: { + setup: function*() { + Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(prefilledSet)); + distroDir.leafName = "hidden"; + }, + initialState: [true, null, "2.0", "2.0", null, null], + }, +}; + + +/** + * The tests to run. Each test must define an updateList or test. The following + * properties are used: + * + * updateList: The set of add-ons the server should respond with. + * test: A function to run to perform the update check (replaces + * updateList) + * fails: An optional property, if true the update check is expected to + * fail. + * finalState: An optional property, the expected final state of system add-ons, + * if missing the test condition's initialState is used. + */ +const TESTS = { + // Test that an error response does nothing + error: { + test: function*() { + try { + yield install_system_addons("foobar"); + do_throw("Expected to fail the update check"); + } + catch (e) { + do_check_true(true, "Expected to fail the update check"); + } + }, + }, + + // Test that a blank response does nothing + blank: { + updateList: null, + }, + + // Test that an empty list updates to an empty set of system add-ons + empty: { + updateList: [], + finalState: [true, null, null, null, null, null] + }, + + // Tests that a new set of system add-ons gets installed + newset: { + updateList: [ + { id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" }, + { id: "system5@tests.mozilla.org", version: "1.0", path: "system5_1.xpi" } + ], + finalState: [true, null, null, null, "1.0", "1.0"] + }, + + // Tests that an upgraded set of system add-ons gets installed + upgrades: { + updateList: [ + { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi" }, + { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" } + ], + finalState: [true, null, "3.0", "3.0", null, null] + }, + + // Tests that a set of system add-ons, some new, some existing gets installed + overlapping: { + updateList: [ + { id: "system1@tests.mozilla.org", version: "1.0", path: "system1_2.xpi" }, + { id: "system2@tests.mozilla.org", version: "1.0", path: "system2_2.xpi" }, + { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_3.xpi" }, + { id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" } + ], + finalState: [true, "2.0", "2.0", "3.0", "1.0", null] + }, + + // Specifying an incorrect version should stop us updating anything + badVersion: { + fails: true, + updateList: [ + { id: "system2@tests.mozilla.org", version: "4.0", path: "system2_3.xpi" }, + { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" } + ], + }, + + // Specifying an invalid size should stop us updating anything + badSize: { + fails: true, + updateList: [ + { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi", size: 2 }, + { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" } + ], + }, + + // Specifying an incorrect hash should stop us updating anything + badHash: { + fails: true, + updateList: [ + { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi" }, + { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi", hashFunction: "sha1", hashValue: "205a4c49bd513ebd30594e380c19e86bba1f83e2" } + ], + }, + + // Correct sizes and hashes should work + checkSizeHash: { + updateList: [ + { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi", size: 858 }, + { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi", hashFunction: "sha1", hashValue: "105a4c49bd513ebd30594e380c19e86bba1f83e2" }, + { id: "system5@tests.mozilla.org", version: "1.0", path: "system5_1.xpi", size: 857, hashFunction: "sha1", hashValue: "664e9218be3c9acbb9029e715c1e5d2fbb4ea2cc" } + ], + finalState: [true, null, "3.0", "3.0", null, "1.0"] + }, +} + +add_task(function* setup() { + // Initialise the profile + startupManager(); + yield promiseShutdownManager(); +}) + +function* setup_conditions(setup) { + do_print("Setting up conditions."); + yield setup.setup(); + + // Blow away the cache to force a rescan of the filesystem + Services.prefs.clearUserPref(PREF_XPI_STATE); + startupManager(false); + + // Make sure the initial state is correct + do_print("Checking initial state."); + yield check_installed(...setup.initialState); +} + +function* verify_state(finalState) { + do_print("Checking final state."); + + // Bug 1204156: Currently switching to the new state requires a restart + // yield check_installed(...finalState); + + // Check that the new state is active after a restart + yield promiseRestartManager(); + yield check_installed(...finalState); +} + +function* exec_test(setup, test) { + yield setup_conditions(setup); + + try { + if ("test" in test) { + yield test.test(); + } + else { + yield install_system_addons(yield build_xml(test.updateList)); + } + + if (test.fails) { + do_throw("Expected this test to fail"); + } + } + catch (e) { + if (!test.fails) { + do_throw(e); + } + } + + yield verify_state(test.finalState ? test.finalState : setup.initialState); + + yield promiseShutdownManager(); +} + +for (let setup of Object.keys(TEST_CONDITIONS)) { + for (let test of Object.keys(TESTS)) { + add_task(function*() { + do_print("Running test " + setup + " " + test); + + yield exec_test(TEST_CONDITIONS[setup], TESTS[test]); + }); + } +} + +// Some custom tests + +// Test that the update check is performed as part of the regular add-on update +// check +add_task(function* test_addon_update() { + yield setup_conditions(TEST_CONDITIONS.blank); + + yield update_all_addons(yield build_xml([ + { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" }, + { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" } + ])); + + yield verify_state([true, null, "2.0", "2.0", null, null]); + + yield promiseShutdownManager(); +}); + +// Disabling app updates should block system add-on updates +add_task(function* test_app_update_disabled() { + yield setup_conditions(TEST_CONDITIONS.blank); + + Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, false); + yield update_all_addons(yield build_xml([ + { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" }, + { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" } + ])); + Services.prefs.clearUserPref(PREF_APP_UPDATE_ENABLED); + + yield verify_state(TEST_CONDITIONS.blank.initialState); + + yield promiseShutdownManager(); +}); + +// Tests that a set that matches the hidden default set works +add_task(function* test_match_default() { + yield setup_conditions(TEST_CONDITIONS.withBothSets); + + yield install_system_addons(yield build_xml([ + { id: "system1@tests.mozilla.org", version: "1.0", path: "system1_1.xpi" }, + { id: "system2@tests.mozilla.org", version: "1.0", path: "system2_1.xpi" } + ])); + + // Bug 1204159: This should revert to the default set instead of installing + // new versions into the updated set. + //yield verify_state([false, "1.0", "1.0", null, null, null]); + yield verify_state([true, "1.0", "1.0", null, null, null]); + + yield promiseShutdownManager(); +}); + +// Tests that a set that matches the current set works +add_task(function* test_match_current() { + yield setup_conditions(TEST_CONDITIONS.withBothSets); + + yield install_system_addons(yield build_xml([ + { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" }, + { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" } + ])); + + // Bug 1204159: This should remain with the current set instead of creating + // a new copy + //let set = JSON.parse(Services.prefs.getCharPref(PREF_SYSTEM_ADDON_SET)); + //do_check_eq(set.directory, "prefilled"); + + yield verify_state([true, null, "2.0", "2.0", null, null]); + + yield promiseShutdownManager(); +});
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini +++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini @@ -18,14 +18,16 @@ skip-if = appname != "firefox" [test_hotfix_cert.js] [test_isReady.js] [test_metadata_update.js] [test_pluginInfoURL.js] [test_provider_markSafe.js] [test_provider_shutdown.js] [test_provider_unsafe_access_shutdown.js] [test_provider_unsafe_access_startup.js] +[test_ProductAddonChecker.js] [test_shutdown.js] +[test_system_update.js] [test_system_reset.js] [test_XPIcancel.js] [test_XPIStates.js] [include:xpcshell-shared.ini]
--- a/toolkit/mozapps/extensions/test/xpinstall/head.js +++ b/toolkit/mozapps/extensions/test/xpinstall/head.js @@ -240,19 +240,16 @@ var Harness = { // Install blocked handling installDisabled: function(installInfo) { ok(!!this.installDisabledCallback, "Installation shouldn't have been disabled"); if (this.installDisabledCallback) this.installDisabledCallback(installInfo); this.expectingCancelled = true; - installInfo.installs.forEach(function(install) { - install.cancel(); - }); this.expectingCancelled = false; this.endTest(); }, installCancelled: function(installInfo) { if (this.expectingCancelled) return;
--- a/toolkit/mozapps/update/nsUpdateService.js +++ b/toolkit/mozapps/update/nsUpdateService.js @@ -25,18 +25,16 @@ const PREF_APP_UPDATE_BACKGROUND_INTERVA const PREF_APP_UPDATE_BACKGROUNDERRORS = "app.update.backgroundErrors"; const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors"; const PREF_APP_UPDATE_CANCELATIONS = "app.update.cancelations"; const PREF_APP_UPDATE_CERTS_BRANCH = "app.update.certs."; const PREF_APP_UPDATE_CERT_CHECKATTRS = "app.update.cert.checkAttributes"; const PREF_APP_UPDATE_CERT_ERRORS = "app.update.cert.errors"; const PREF_APP_UPDATE_CERT_MAXERRORS = "app.update.cert.maxErrors"; const PREF_APP_UPDATE_CERT_REQUIREBUILTIN = "app.update.cert.requireBuiltIn"; -const PREF_APP_UPDATE_CUSTOM = "app.update.custom"; -const PREF_APP_UPDATE_IMEI_HASH = "app.update.imei_hash"; const PREF_APP_UPDATE_ENABLED = "app.update.enabled"; const PREF_APP_UPDATE_IDLETIME = "app.update.idletime"; const PREF_APP_UPDATE_INCOMPATIBLE_MODE = "app.update.incompatible.mode"; const PREF_APP_UPDATE_INTERVAL = "app.update.interval"; const PREF_APP_UPDATE_LOG = "app.update.log"; const PREF_APP_UPDATE_MODE = "app.update.mode"; const PREF_APP_UPDATE_NEVER_BRANCH = "app.update.never."; const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported"; @@ -49,21 +47,16 @@ const PREF_APP_UPDATE_URL const PREF_APP_UPDATE_URL_DETAILS = "app.update.url.details"; const PREF_APP_UPDATE_URL_OVERRIDE = "app.update.url.override"; const PREF_APP_UPDATE_SERVICE_ENABLED = "app.update.service.enabled"; const PREF_APP_UPDATE_SERVICE_ERRORS = "app.update.service.errors"; const PREF_APP_UPDATE_SERVICE_MAX_ERRORS = "app.update.service.maxErrors"; const PREF_APP_UPDATE_SOCKET_ERRORS = "app.update.socket.maxErrors"; const PREF_APP_UPDATE_RETRY_TIMEOUT = "app.update.socket.retryTimeout"; -const PREF_APP_DISTRIBUTION = "distribution.id"; -const PREF_APP_DISTRIBUTION_VERSION = "distribution.version"; - -const PREF_APP_B2G_VERSION = "b2g.version"; - const PREF_EM_HOTFIX_ID = "extensions.hotfix.id"; const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul"; const URI_UPDATE_HISTORY_DIALOG = "chrome://mozapps/content/update/history.xul"; const URI_BRAND_PROPERTIES = "chrome://branding/locale/brand.properties"; const URI_UPDATES_PROPERTIES = "chrome://mozapps/locale/update/updates.properties"; const URI_UPDATE_NS = "http://www.mozilla.org/2005/app-update"; @@ -82,17 +75,16 @@ const FILE_UPDATE_VERSION = "update.vers const FILE_UPDATE_ARCHIVE = "update.mar"; const FILE_UPDATE_LINK = "update.link"; const FILE_UPDATE_LOG = "update.log"; const FILE_UPDATES_DB = "updates.xml"; const FILE_UPDATE_ACTIVE = "active-update.xml"; const FILE_PERMS_TEST = "update.test"; const FILE_LAST_LOG = "last-update.log"; const FILE_BACKUP_LOG = "backup-update.log"; -const FILE_UPDATE_LOCALE = "update.locale"; const STATE_NONE = "null"; const STATE_DOWNLOADING = "downloading"; const STATE_PENDING = "pending"; const STATE_PENDING_SVC = "pending-service"; const STATE_APPLYING = "applying"; const STATE_APPLIED = "applied"; const STATE_APPLIED_OS = "applied-os"; @@ -205,227 +197,34 @@ var gSDCardMountLock = null; // Gonk only XPCOMUtils.defineLazyGetter(this, "gExtStorage", function aus_gExtStorage() { if (AppConstants.platform != "gonk") { throw Cr.NS_ERROR_NOT_IMPLEMENTED; } return Services.env.get("EXTERNAL_STORAGE"); }); -XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel", - "resource://gre/modules/UpdateChannel.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils", + "resource://gre/modules/UpdateUtils.jsm"); XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function aus_gLogEnabled() { return getPref("getBoolPref", PREF_APP_UPDATE_LOG, false); }); XPCOMUtils.defineLazyGetter(this, "gUpdateBundle", function aus_gUpdateBundle() { return Services.strings.createBundle(URI_UPDATES_PROPERTIES); }); // shared code for suppressing bad cert dialogs XPCOMUtils.defineLazyGetter(this, "gCertUtils", function aus_gCertUtils() { let temp = { }; Cu.import("resource://gre/modules/CertUtils.jsm", temp); return temp; }); -XPCOMUtils.defineLazyGetter(this, "gABI", function aus_gABI() { - let abi = null; - try { - abi = Services.appinfo.XPCOMABI; - } - catch (e) { - LOG("gABI - XPCOM ABI unknown: updates are not possible."); - } - - 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(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"; - } - } 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; -}); - -/* 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) { - LOG("gWinCPUArch - 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) { - LOG("gWinCPUArch - error getting processor architecture. " + - "Exception: " + e); - } finally { - kernel32.close(); - } - } - - return arch; -}); - /** * Tests to make sure that we can write to a given directory. * * @param updateTestFile a test file in the directory that needs to be tested. * @param createDirectory whether a test directory should be created. * @throws if we don't have right access to the directory. */ function testWriteAccess(updateTestFile, createDirectory) { @@ -722,23 +521,23 @@ XPCOMUtils.defineLazyGetter(this, "gCanC var enabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true); if (!enabled && Services.prefs.prefIsLocked(PREF_APP_UPDATE_ENABLED)) { LOG("gCanCheckForUpdates - unable to automatically check for updates, " + "the preference is disabled and admistratively locked."); return false; } // If we don't know the binary platform we're updating, we can't update. - if (!gABI) { + if (!UpdateUtils.ABI) { LOG("gCanCheckForUpdates - unable to check for updates, unknown ABI"); return false; } // If we don't know the OS version we're updating, we can't update. - if (!gOSVersion) { + if (!UpdateUtils.OSVersion) { LOG("gCanCheckForUpdates - unable to check for updates, unknown OS " + "version"); return false; } LOG("gCanCheckForUpdates - able to check for updates"); return true; }); @@ -1238,68 +1037,16 @@ function cleanupActiveUpdate() { um.activeUpdate = null; um.saveUpdates(); // Now trash the updates directory, since we're done with it cleanUpUpdatesDir(); } /** - * 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. - */ -function getLocale() { - if (gLocale) { - return gLocale; - } - - let channel; - 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 { - var inputStream = channel.open(); - gLocale = readStringFromInputStream(inputStream); - } catch(e) {} - if (gLocale) - break; - } - - if (!gLocale) - throw Components.Exception(FILE_UPDATE_LOCALE + " file doesn't exist in " + - "either the application or GRE directories", - Cr.NS_ERROR_FILE_NOT_FOUND); - - LOG("getLocale - getting locale from file: " + channel.originalURI.spec + - ", locale: " + gLocale); - return gLocale; -} - -/* 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; -} - -/** * An enumeration of items in a JS array. * @constructor */ function ArrayEnumerator(aItems) { this._index = 0; if (aItems) { for (var i = 0; i < aItems.length; ++i) { if (!aItems[i]) @@ -2522,19 +2269,19 @@ UpdateService.prototype = { let validUpdateURL = true; try { this.backgroundChecker.getUpdateURL(false); } catch (e) { validUpdateURL = false; } // The following checks are done here so they can be differentiated from // foreground checks. - if (!gOSVersion) { + if (!UpdateUtils.OSVersion) { AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_VERSION); - } else if (!gABI) { + } else if (!UpdateUtils.ABI) { AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_ABI); } else if (!validUpdateURL) { if (overridePrefHasValue) { if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_URL_OVERRIDE)) { AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_INVALID_USER_OVERRIDE_URL); } else { AUSTLMY.pingCheckCode(this._pingSuffix, @@ -3266,24 +3013,24 @@ UpdateManager.prototype = { return this._updates.length; }, /** * See nsIUpdateService.idl */ get activeUpdate() { if (this._activeUpdate && - this._activeUpdate.channel != UpdateChannel.get()) { + this._activeUpdate.channel != UpdateUtils.UpdateChannel) { LOG("UpdateManager:get activeUpdate - channel has changed, " + "reloading default preferences to workaround bug 802022"); // Workaround to get distribution preferences loaded (Bug 774618). This // can be removed after bug 802022 is fixed. let prefSvc = Services.prefs.QueryInterface(Ci.nsIObserver); prefSvc.observe(null, "reload-default-prefs", null); - if (this._activeUpdate.channel != UpdateChannel.get()) { + if (this._activeUpdate.channel != UpdateUtils.UpdateChannel) { // User switched channels, clear out any old active updates and remove // partial downloads this._activeUpdate = null; this.saveUpdates(); // Destroy the updates directory, since we're done with it. cleanUpUpdatesDir(); } @@ -3511,50 +3258,17 @@ Checker.prototype = { } } if (!url || url == "") { LOG("Checker:getUpdateURL - update URL not defined"); return null; } - 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 + "_" + gABI); - url = url.replace(/%OS_VERSION%/g, gOSVersion); - if (/%LOCALE%/.test(url)) { - url = url.replace(/%LOCALE%/g, getLocale()); - } - url = url.replace(/%CHANNEL%/g, UpdateChannel.get()); - 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, getPref("getCharPref", 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, - getPref("getCharPref", PREF_APP_B2G_VERSION, null)); - url = url.replace(/%IMEI%/g, - getPref("getCharPref", PREF_APP_UPDATE_IMEI_HASH, "default")); - } + url = UpdateUtils.formatUpdateURL(url); if (force) { url += (url.indexOf("?") != -1 ? "&" : "?") + "force=1"; } LOG("Checker:getUpdateURL - update URL: " + url); return url; }, @@ -3636,17 +3350,17 @@ Checker.prototype = { let update; try { update = new Update(updateElement); } catch (e) { LOG("Checker:_updates get - invalid <update/>, ignoring..."); continue; } update.serviceURL = this.getUpdateURL(this._forced); - update.channel = UpdateChannel.get(); + update.channel = UpdateUtils.UpdateChannel; updates.push(update); } return updates; }, /** * Returns the status code for the XMLHttpRequest