diff options
Diffstat (limited to 'toolkit')
24 files changed, 998 insertions, 261 deletions
diff --git a/toolkit/components/alerts/nsXULAlerts.cpp b/toolkit/components/alerts/nsXULAlerts.cpp index 882617637a..d353d87140 100644 --- a/toolkit/components/alerts/nsXULAlerts.cpp +++ b/toolkit/components/alerts/nsXULAlerts.cpp @@ -18,7 +18,6 @@ #include "nsIWindowWatcher.h" using namespace mozilla; -using mozilla::dom::NotificationTelemetryService; #define ALERT_CHROME_URL "chrome://global/content/alerts/alert.xul" @@ -185,20 +184,6 @@ nsXULAlerts::ShowAlertWithIconURI(nsIAlertNotification* aAlert, NS_ENSURE_SUCCESS(rv, rv); if (mDoNotDisturb) { - if (!inPrivateBrowsing) { - RefPtr<NotificationTelemetryService> telemetry = - NotificationTelemetryService::GetInstance(); - if (telemetry) { - // Record the number of unique senders for XUL alerts. The OS X and - // libnotify backends will fire `alertshow` even if "do not disturb" - // is enabled. In that case, `NotificationObserver` will record the - // sender. - nsCOMPtr<nsIPrincipal> principal; - if (NS_SUCCEEDED(aAlert->GetPrincipal(getter_AddRefs(principal)))) { - Unused << NS_WARN_IF(NS_FAILED(telemetry->RecordSender(principal))); - } - } - } if (aAlertListener) aAlertListener->Observe(nullptr, "alertfinished", cookie.get()); return NS_OK; diff --git a/toolkit/components/downloads/ApplicationReputation.cpp b/toolkit/components/downloads/ApplicationReputation.cpp index a369ca8841..9fdc8a103e 100644 --- a/toolkit/components/downloads/ApplicationReputation.cpp +++ b/toolkit/components/downloads/ApplicationReputation.cpp @@ -117,14 +117,6 @@ private: friend class PendingDBLookup; - // Telemetry states. - // Status of the remote response (valid or not). - enum SERVER_RESPONSE_TYPES { - SERVER_RESPONSE_VALID = 0, - SERVER_RESPONSE_FAILED = 1, - SERVER_RESPONSE_INVALID = 2, - }; - // Number of blocklist and allowlist hits we have seen. uint32_t mBlocklistCount; uint32_t mAllowlistCount; diff --git a/toolkit/components/osfile/NativeOSFileInternals.cpp b/toolkit/components/osfile/NativeOSFileInternals.cpp index e4725d390d..36517d9ec7 100644 --- a/toolkit/components/osfile/NativeOSFileInternals.cpp +++ b/toolkit/components/osfile/NativeOSFileInternals.cpp @@ -232,7 +232,6 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AbstractResult) NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractResult) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbstractResult) diff --git a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp index 33aeaf7c26..59d84ced15 100644 --- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp +++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp @@ -662,9 +662,6 @@ nsPerformanceStatsService::nsPerformanceStatsService() nsPerformanceGroup::GroupScope::RUNTIME // scope )) , mIsHandlingUserInput(false) - , mProcessStayed(0) - , mProcessMoved(0) - , mProcessUpdateCounter(0) , mIsMonitoringPerCompartment(false) , mJankAlertThreshold(mozilla::MaxValue<uint64_t>::value) // By default, no alerts , mJankAlertBufferingDelay(1000 /* ms */) @@ -953,13 +950,6 @@ nsPerformanceStatsService::SetJankAlertBufferingDelay(uint32_t value) { return NS_OK; } -nsresult -nsPerformanceStatsService::UpdateTelemetry() -{ - /* STUB */ - return NS_OK; -} - /* static */ nsIPerformanceStats* nsPerformanceStatsService::GetStatsForGroup(const js::PerformanceGroup* group) @@ -992,12 +982,6 @@ nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * * } } - js::GetPerfMonitoringTestCpuRescheduling(cx, &mProcessStayed, &mProcessMoved); - - if (++mProcessUpdateCounter % 10 == 0) { - mozilla::Unused << UpdateTelemetry(); - } - snapshot.forget(aSnapshot); return NS_OK; @@ -1098,6 +1082,9 @@ nsPerformanceStatsService::GetPerformanceGroups(JSContext* cx, return false; } + // Returning a vector that is too large would cause allocations all over the + // place in the JS engine. We want to be sure that all data is stored inline. + MOZ_ASSERT(out.length() <= out.sMaxInlineStorage); return true; } @@ -1326,8 +1313,12 @@ nsPerformanceStatsService::GetResources(uint64_t* userTime, void nsPerformanceStatsService::NotifyJankObservers(const mozilla::Vector<uint64_t>& aPreviousJankLevels) { - GroupVector alerts; - mPendingAlerts.swap(alerts); + + // The move operation is generally constant time, unless + // `mPendingAlerts.length()` is very small, in which case it's fast anyway. + GroupVector alerts(Move(mPendingAlerts)); + mPendingAlerts = GroupVector(); // Reconstruct after `Move`. + if (!mPendingAlertsCollector) { // We are shutting down. return; diff --git a/toolkit/components/perfmonitoring/nsPerformanceStats.h b/toolkit/components/perfmonitoring/nsPerformanceStats.h index c82a3e92c7..661a78a1ab 100644 --- a/toolkit/components/perfmonitoring/nsPerformanceStats.h +++ b/toolkit/components/perfmonitoring/nsPerformanceStats.h @@ -19,7 +19,7 @@ class nsPerformanceGroup; class nsPerformanceGroupDetails; -typedef mozilla::Vector<RefPtr<nsPerformanceGroup>> GroupVector; +typedef mozilla::Vector<RefPtr<nsPerformanceGroup>, 8> GroupVector; /** * A data structure for registering observers interested in @@ -363,21 +363,6 @@ protected: nsPerformanceGroup* group); - - - /********************************************************** - * - * To check whether our algorithm makes sense, we keep count of the - * number of times the process has been rescheduled to another CPU - * while we were monitoring the performance of a group and we upload - * this data through Telemetry. - */ - nsresult UpdateTelemetry(); - - uint64_t mProcessStayed; - uint64_t mProcessMoved; - uint32_t mProcessUpdateCounter; - /********************************************************** * * Options controlling measurements. diff --git a/toolkit/components/places/Database.cpp b/toolkit/components/places/Database.cpp index a87c14b37a..08c382377b 100644 --- a/toolkit/components/places/Database.cpp +++ b/toolkit/components/places/Database.cpp @@ -597,7 +597,7 @@ Database::BackupAndReplaceDatabaseFile(nsCOMPtr<mozIStorageService>& aStorage) // If anything fails from this point on, we have a stale connection or // database file, and there's not much more we can do. // The only thing we can try to do is to replace the database on the next - // startup, and report the problem through telemetry. + // startup. { enum eCorruptDBReplaceStage : int8_t { stage_closing = 0, diff --git a/toolkit/components/places/Helpers.cpp b/toolkit/components/places/Helpers.cpp index dda1621979..13e040bfd5 100644 --- a/toolkit/components/places/Helpers.cpp +++ b/toolkit/components/places/Helpers.cpp @@ -382,12 +382,5 @@ AsyncStatementCallbackNotifier::HandleCompletion(uint16_t aReason) //////////////////////////////////////////////////////////////////////////////// //// AsyncStatementCallbackNotifier -NS_IMETHODIMP -AsyncStatementTelemetryTimer::HandleCompletion(uint16_t aReason) -{ - /* STUB */ - return NS_OK; -} - } // namespace places } // namespace mozilla diff --git a/toolkit/components/places/Helpers.h b/toolkit/components/places/Helpers.h index 654e425393..4e79abc759 100644 --- a/toolkit/components/places/Helpers.h +++ b/toolkit/components/places/Helpers.h @@ -15,7 +15,6 @@ #include "nsThreadUtils.h" #include "nsProxyRelease.h" #include "prtime.h" -#include "mozilla/Telemetry.h" namespace mozilla { namespace places { @@ -270,26 +269,6 @@ private: const char* mTopic; }; -/** - * Used to notify a topic to system observers on async execute completion. - */ -class AsyncStatementTelemetryTimer : public AsyncStatementCallback -{ -public: - explicit AsyncStatementTelemetryTimer(Telemetry::ID aHistogramId, - TimeStamp aStart = TimeStamp::Now()) - : mHistogramId(aHistogramId) - , mStart(aStart) - { - } - - NS_IMETHOD HandleCompletion(uint16_t aReason); - -private: - const Telemetry::ID mHistogramId; - const TimeStamp mStart; -}; - } // namespace places } // namespace mozilla diff --git a/toolkit/components/places/nsNavHistory.cpp b/toolkit/components/places/nsNavHistory.cpp index 49d911d655..e725260226 100644 --- a/toolkit/components/places/nsNavHistory.cpp +++ b/toolkit/components/places/nsNavHistory.cpp @@ -3063,17 +3063,15 @@ nsNavHistory::Observe(nsISupports *aSubject, const char *aTopic, namespace { -class DecayFrecencyCallback : public AsyncStatementTelemetryTimer +class DecayFrecencyCallback : public AsyncStatementCallback { public: DecayFrecencyCallback() - : AsyncStatementTelemetryTimer(Telemetry::PLACES_IDLE_FRECENCY_DECAY_TIME_MS) { } NS_IMETHOD HandleCompletion(uint16_t aReason) { - (void)AsyncStatementTelemetryTimer::HandleCompletion(aReason); if (aReason == REASON_FINISHED) { nsNavHistory *navHistory = nsNavHistory::GetHistoryService(); NS_ENSURE_STATE(navHistory); diff --git a/toolkit/components/telemetry/TelemetryHistogram.cpp b/toolkit/components/telemetry/TelemetryHistogram.cpp index ba0288979d..30fcc05eed 100644 --- a/toolkit/components/telemetry/TelemetryHistogram.cpp +++ b/toolkit/components/telemetry/TelemetryHistogram.cpp @@ -2090,11 +2090,6 @@ void TelemetryHistogram::InitializeGlobalState(bool canRecordBase, // don't go unnoticed. // TODO: Compare explicitly with gHistograms[<histogram id>].bucketCount here // once we can make gHistograms constexpr (requires VS2015). - static_assert((JS::gcreason::NUM_TELEMETRY_REASONS == 100), - "NUM_TELEMETRY_REASONS is assumed to be a fixed value in Histograms.json." - " If this was an intentional change, update this assert with its value " - "and update the n_values for the following in Histograms.json: " - "GC_MINOR_REASON, GC_MINOR_REASON_LONG, GC_REASON_2"); static_assert((mozilla::StartupTimeline::MAX_EVENT_ID == 16), "MAX_EVENT_ID is assumed to be a fixed value in Histograms.json. If this" " was an intentional change, update this assert with its value and update" @@ -2689,37 +2684,5 @@ TelemetryHistogram::IPCTimerFired(nsITimer* aTimer, void* aClosure) } } - switch (XRE_GetProcessType()) { - case GeckoProcessType_Content: { - mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton(); - mozilla::Unused << NS_WARN_IF(!contentChild); - if (contentChild) { - if (accumulationsToSend.Length()) { - mozilla::Unused << - NS_WARN_IF(!contentChild->SendAccumulateChildHistogram(accumulationsToSend)); - } - if (keyedAccumulationsToSend.Length()) { - mozilla::Unused << - NS_WARN_IF(!contentChild->SendAccumulateChildKeyedHistogram(keyedAccumulationsToSend)); - } - } - break; - } - case GeckoProcessType_GPU: { - if (mozilla::gfx::GPUParent* gpu = mozilla::gfx::GPUParent::GetSingleton()) { - if (accumulationsToSend.Length()) { - mozilla::Unused << gpu->SendAccumulateChildHistogram(accumulationsToSend); - } - if (keyedAccumulationsToSend.Length()) { - mozilla::Unused << gpu->SendAccumulateChildKeyedHistogram(keyedAccumulationsToSend); - } - } - break; - } - default: - MOZ_ASSERT_UNREACHABLE("Unsupported process type"); - break; - } - gIPCTimerArmed = false; } diff --git a/toolkit/components/thumbnails/BackgroundPageThumbs.jsm b/toolkit/components/thumbnails/BackgroundPageThumbs.jsm index 3eec9827d2..3ba0c346c3 100644 --- a/toolkit/components/thumbnails/BackgroundPageThumbs.jsm +++ b/toolkit/components/thumbnails/BackgroundPageThumbs.jsm @@ -41,6 +41,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService", "resource://gre/modules/ContextualIdentityService.jsm"); const global = this; +// contains base64 version of a placeholder thumbnail +#include blankthumb.inc + const BackgroundPageThumbs = { /** diff --git a/toolkit/components/thumbnails/PageThumbs.jsm b/toolkit/components/thumbnails/PageThumbs.jsm index 5c7754b318..b0affee928 100644 --- a/toolkit/components/thumbnails/PageThumbs.jsm +++ b/toolkit/components/thumbnails/PageThumbs.jsm @@ -28,9 +28,6 @@ const MAX_THUMBNAIL_AGE_SECS = 172800; // 2 days == 60*60*24*2 == 172800 secs. */ const THUMBNAIL_DIRECTORY = "thumbnails"; -// contains base64 version of a placeholder thumbnail -#include blankthumb.inc - Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); Cu.import("resource://gre/modules/PromiseWorker.jsm", this); Cu.import("resource://gre/modules/Promise.jsm", this); diff --git a/toolkit/components/thumbnails/moz.build b/toolkit/components/thumbnails/moz.build index d4003c6355..e4a178998f 100644 --- a/toolkit/components/thumbnails/moz.build +++ b/toolkit/components/thumbnails/moz.build @@ -13,13 +13,13 @@ EXTRA_COMPONENTS += [ ] EXTRA_JS_MODULES += [ - 'BackgroundPageThumbs.jsm', + 'PageThumbs.jsm', 'PageThumbsWorker.js', 'PageThumbUtils.jsm', ] EXTRA_PP_JS_MODULES += [ - 'PageThumbs.jsm', + 'BackgroundPageThumbs.jsm', ] diff --git a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp index e230f69518..9319822d59 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp @@ -20,7 +20,6 @@ #include "mozilla/Logging.h" #include "nsIInterfaceRequestor.h" #include "mozilla/LoadContext.h" -#include "mozilla/Telemetry.h" #include "nsContentUtils.h" #include "nsIURLFormatter.h" @@ -503,115 +502,6 @@ nsUrlClassifierStreamUpdater::AddRequestBody(const nsACString &aRequestBody) return NS_OK; } -// Map the HTTP response code to a Telemetry bucket -static uint32_t HTTPStatusToBucket(uint32_t status) -{ - uint32_t statusBucket; - switch (status) { - case 100: - case 101: - // Unexpected 1xx return code - statusBucket = 0; - break; - case 200: - // OK - Data is available in the HTTP response body. - statusBucket = 1; - break; - case 201: - case 202: - case 203: - case 205: - case 206: - // Unexpected 2xx return code - statusBucket = 2; - break; - case 204: - // No Content - statusBucket = 3; - break; - case 300: - case 301: - case 302: - case 303: - case 304: - case 305: - case 307: - case 308: - // Unexpected 3xx return code - statusBucket = 4; - break; - case 400: - // Bad Request - The HTTP request was not correctly formed. - // The client did not provide all required CGI parameters. - statusBucket = 5; - break; - case 401: - case 402: - case 405: - case 406: - case 407: - case 409: - case 410: - case 411: - case 412: - case 414: - case 415: - case 416: - case 417: - case 421: - case 426: - case 428: - case 429: - case 431: - case 451: - // Unexpected 4xx return code - statusBucket = 6; - break; - case 403: - // Forbidden - The client id is invalid. - statusBucket = 7; - break; - case 404: - // Not Found - statusBucket = 8; - break; - case 408: - // Request Timeout - statusBucket = 9; - break; - case 413: - // Request Entity Too Large - Bug 1150334 - statusBucket = 10; - break; - case 500: - case 501: - case 510: - // Unexpected 5xx return code - statusBucket = 11; - break; - case 502: - case 504: - case 511: - // Local network errors, we'll ignore these. - statusBucket = 12; - break; - case 503: - // Service Unavailable - The server cannot handle the request. - // Clients MUST follow the backoff behavior specified in the - // Request Frequency section. - statusBucket = 13; - break; - case 505: - // HTTP Version Not Supported - The server CANNOT handle the requested - // protocol major version. - statusBucket = 14; - break; - default: - statusBucket = 15; - }; - return statusBucket; -} - /////////////////////////////////////////////////////////////////////////////// // nsIStreamListenerObserver implementation diff --git a/toolkit/components/webextensions/ExtensionUtils.jsm b/toolkit/components/webextensions/ExtensionUtils.jsm index e7f768c07c..04e767cb5c 100644 --- a/toolkit/components/webextensions/ExtensionUtils.jsm +++ b/toolkit/components/webextensions/ExtensionUtils.jsm @@ -990,7 +990,8 @@ function findPathInObject(obj, path, printErrors = true) { for (let elt of path.split(".")) { if (!obj || !(elt in obj)) { if (printErrors) { - Cu.reportError(`WebExtension API ${path} not found (it may be unimplemented by Firefox).`); + let appname = Services.appinfo.name; + Cu.reportError(`WebExtension API ${path} not found (it may be unimplemented by ${appname}).`); } return null; } diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build index e288d11c86..948d8d2c9b 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -46,8 +46,6 @@ EXTRA_JS_MODULES += [ 'FinderIterator.jsm', 'FormLikeFactory.jsm', 'Geometry.jsm', - 'GMPInstallManager.jsm', - 'GMPUtils.jsm', 'Http.jsm', 'InlineSpellChecker.jsm', 'InlineSpellCheckerContent.jsm', diff --git a/toolkit/mozapps/extensions/GMPInstallManager.jsm b/toolkit/mozapps/extensions/GMPInstallManager.jsm new file mode 100644 index 0000000000..b9ebe5d7e0 --- /dev/null +++ b/toolkit/mozapps/extensions/GMPInstallManager.jsm @@ -0,0 +1,961 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +this.EXPORTED_SYMBOLS = []; + +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; + +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"); + +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; + +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; + let sysInfo = Cc["@mozilla.org/system-info;1"]. + getService(Ci.nsIPropertyBag2); + try { + osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version"); + } + catch (e) { + LOG("gOSVersion - OS Version unknown: updates are not possible."); + } + + if (osVersion) { +#ifdef XP_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(); + } + } +#endif + + 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; +}); + +// 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, "gABI", function aus_gABI() { + let abi = null; + try { + abi = Services.appinfo.XPCOMABI; + } + catch (e) { + LOG("gABI - XPCOM ABI unknown: updates are not possible."); + } +#ifdef XP_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; +#ifdef MOZ_SHARK + // Disambiguate optimised and shark nightlies + abi += "-shark" +#endif +#endif + return abi; +}); + +/** + * Provides an easy API for downloading and installing GMP Addons + */ +function GMPInstallManager() { +} +/** + * Temp file name used for downloading + */ +GMPInstallManager.prototype = { + /** + * Obtains a URL with replacement of vars + */ + _getURL: function() { + let log = getScopedLogger("GMPInstallManager._getURL"); + // Use the override URL if it is specified. The override URL is just like + // the normal URL but it does not check the cert. + 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 + "_" + gABI) + .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"); + 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: + * target: The XHR request object + * status: The HTTP status code + * type: Sometimes specifies type of rejection + */ + checkForAddons: function() { + 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; + } + 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); + + 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 + * The promise will reject with an error object: + * target: The XHR request object + * status: The HTTP status code + * type: A string to represent the type of error + * downloaderr, verifyerr or previouserrorencountered + */ + installAddon: function(gmpAddon) { + if (this._deferred) { + log.error("previous error encountered"); + return Promise.reject({type: "previouserrorencountered"}); + } + this.gmpDownloader = new GMPDownloader(gmpAddon); + return this.gmpDownloader.start(); + }, + _getTimeSinceLastCheck: function() { + let now = Math.round(Date.now() / 1000); + // Default to 0 here because `now - 0` will be returned later if that case + // is hit. We want a large value so a check will occur. + let lastCheck = GMPPrefs.get(GMPPrefs.KEY_UPDATE_LAST_CHECK, 0); + // Handle clock jumps, return now since we want it to represent + // a lot of time has passed since the last check. + if (now < lastCheck) { + return now; + } + return now - lastCheck; + }, + get _isEMEEnabled() { + return GMPPrefs.get(GMPPrefs.KEY_EME_ENABLED, true); + }, + _isAddonUpdateEnabled: function(aAddon) { + return GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ENABLED, true, aAddon) && + GMPPrefs.get(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, aAddon); + }, + _updateLastCheck: function() { + let now = Math.round(Date.now() / 1000); + GMPPrefs.set(GMPPrefs.KEY_UPDATE_LAST_CHECK, now); + }, + _versionchangeOccurred: function() { + let savedBuildID = GMPPrefs.get(GMPPrefs.KEY_BUILDID, null); + let buildID = Services.appinfo.platformBuildID; + if (savedBuildID == buildID) { + return false; + } + GMPPrefs.set(GMPPrefs.KEY_BUILDID, buildID); + return true; + }, + /** + * Wrapper for checkForAddons and installAddon. + * Will only install if not already installed and will log the results. + * This will only install/update the OpenH264 and EME plugins + * @return a promise which will be resolved if all addons could be installed + * successfully, rejected otherwise. + */ + simpleCheckAndInstall: Task.async(function*() { + let log = getScopedLogger("GMPInstallManager.simpleCheckAndInstall"); + + if (this._versionchangeOccurred()) { + log.info("A version change occurred. Ignoring " + + "media.gmp-manager.lastCheck to check immediately for " + + "new or updated GMPs."); + } else { + let secondsBetweenChecks = + GMPPrefs.get(GMPPrefs.KEY_SECONDS_BETWEEN_CHECKS, + DEFAULT_SECONDS_BETWEEN_CHECKS) + let secondsSinceLast = this._getTimeSinceLastCheck(); + log.info("Last check was: " + secondsSinceLast + + " seconds ago, minimum seconds: " + secondsBetweenChecks); + if (secondsBetweenChecks > secondsSinceLast) { + log.info("Will not check for updates."); + return {status: "too-frequent-no-check"}; + } + } + + try { + let gmpAddons = yield this.checkForAddons(); + this._updateLastCheck(); + log.info("Found " + gmpAddons.length + " addons advertised."); + let addonsToInstall = gmpAddons.filter(function(gmpAddon) { + log.info("Found addon: " + gmpAddon.toString()); + + if (!gmpAddon.isValid || GMPUtils.isPluginHidden(gmpAddon) || + gmpAddon.isInstalled) { + log.info("Addon invalid, hidden or already installed."); + return false; + } + + let addonUpdateEnabled = false; + if (GMP_PLUGIN_IDS.indexOf(gmpAddon.id) >= 0) { + addonUpdateEnabled = this._isAddonUpdateEnabled(gmpAddon.id); + if (!addonUpdateEnabled) { + log.info("Auto-update is off for " + gmpAddon.id + + ", skipping check."); + } + } else { + // Currently, we only support installs of OpenH264 and EME plugins. + log.info("Auto-update is off for unknown plugin '" + gmpAddon.id + + "', skipping check."); + } + + return addonUpdateEnabled; + }, this); + + if (!addonsToInstall.length) { + log.info("No new addons to install, returning"); + return {status: "nothing-new-to-install"}; + } + + let installResults = []; + let failureEncountered = false; + for (let addon of addonsToInstall) { + try { + yield this.installAddon(addon); + installResults.push({ + id: addon.id, + result: "succeeded", + }); + } catch (e) { + failureEncountered = true; + installResults.push({ + id: addon.id, + result: "failed", + }); + } + } + if (failureEncountered) { + throw {status: "failed", + results: installResults}; + } + return {status: "succeeded", + results: installResults}; + } catch(e) { + log.error("Could not check for addons", e); + throw e; + } + }), + + /** + * Makes sure everything is cleaned up + */ + uninit: function() { + let log = getScopedLogger("GMPInstallManager.uninit"); + if (this._request) { + log.info("Aborting request"); + this._request.abort(); + } + if (this._deferred) { + log.info("Rejecting deferred"); + this._deferred.reject({type: "uninitialized"}); + } + 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` + */ +function GMPAddon(gmpAddon) { + 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; + 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 + + ", hashFunction: " + this.hashFunction+ + ", hashValue: " + this.hashValue + + (this.size !== undefined ? ", size: " + this.size : "" ) + + ")"; + }, + /** + * If all the fields aren't specified don't consider this addon valid + * @return true if the addon is parsed and valid + */ + get isValid() { + return this.id && this.URL && this.version && + this.hashFunction && !!this.hashValue; + }, + get isInstalled() { + return this.version && + GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION, "", this.id) === this.version; + }, + get isEME() { + return this.id == "gmp-widevinecdm" || this.id.indexOf("gmp-eme-") == 0; + }, +}; +/** + * Constructs a GMPExtractor object which is used to extract a GMP zip + * into the specified location. (Which typically leties per platform) + * @param zipPath The path on disk of the zip file to extract + */ +function GMPExtractor(zipPath, installToDirPath) { + this.zipPath = zipPath; + this.installToDirPath = installToDirPath; +} +GMPExtractor.prototype = { + /** + * Obtains a list of all the entries in a zipfile in the format of *.*. + * This also includes files inside directories. + * + * @param zipReader the nsIZipReader to check + * @return An array of string name entries which can be used + * in nsIZipReader.extract + */ + _getZipEntries: function(zipReader) { + let entries = []; + let enumerator = zipReader.findEntries("*.*"); + while (enumerator.hasMore()) { + entries.push(enumerator.getNext()); + } + return entries; + }, + /** + * Installs the this.zipPath contents into the directory used to store GMP + * addons for the current platform. + * + * @return a promise which will be resolved or rejected + * See GMPInstallManager.installAddon for resolve/rejected info + */ + install: function() { + try { + let log = getScopedLogger("GMPExtractor.install"); + this._deferred = Promise.defer(); + log.info("Installing " + this.zipPath + "..."); + // Get the input zip file + let zipFile = Cc["@mozilla.org/file/local;1"]. + createInstance(Ci.nsIFile); + zipFile.initWithPath(this.zipPath); + + // Initialize a zipReader and obtain the entries + var zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]. + createInstance(Ci.nsIZipReader); + zipReader.open(zipFile) + let entries = this._getZipEntries(zipReader); + let extractedPaths = []; + + // Extract each of the entries + entries.forEach(entry => { + // We don't need these types of files + if (entry.includes("__MACOSX")) { + return; + } + let outFile = Cc["@mozilla.org/file/local;1"]. + createInstance(Ci.nsILocalFile); + outFile.initWithPath(this.installToDirPath); + outFile.appendRelativePath(entry); + + // Make sure the directory hierarchy exists + if(!outFile.parent.exists()) { + outFile.parent.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8)); + } + zipReader.extract(entry, outFile); + extractedPaths.push(outFile.path); + log.info(entry + " was successfully extracted to: " + + outFile.path); + }); + zipReader.close(); + if (!GMPInstallManager.overrideLeaveDownloadedZip) { + zipFile.remove(false); + } + + log.info(this.zipPath + " was installed successfully"); + this._deferred.resolve(extractedPaths); + } catch (e) { + if (zipReader) { + zipReader.close(); + } + this._deferred.reject({ + target: this, + status: e, + type: "exception" + }); + } + return this._deferred.promise; + } +}; + + +/** + * 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) { + 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); + 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); + let installPromise = gmpInstaller.install(); + installPromise.then(extractedPaths => { + // Success, set the prefs + let now = Math.round(Date.now() / 1000); + GMPPrefs.set(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, now, 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" + }); + }); + }, + /** + * 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; +} diff --git a/toolkit/modules/GMPUtils.jsm b/toolkit/mozapps/extensions/GMPUtils.jsm index 9e41a7a614..9e41a7a614 100644 --- a/toolkit/modules/GMPUtils.jsm +++ b/toolkit/mozapps/extensions/GMPUtils.jsm diff --git a/toolkit/mozapps/extensions/content/extensions.xml b/toolkit/mozapps/extensions/content/extensions.xml index cbd05bfa95..9c8fda8ed9 100644 --- a/toolkit/mozapps/extensions/content/extensions.xml +++ b/toolkit/mozapps/extensions/content/extensions.xml @@ -941,10 +941,6 @@ #endif oncommand="document.getBindingParent(this).showPreferences();"/> <!-- label="&cmd.debugAddon.label;" --> - <xul:button anonid="debug-btn" class="addon-control debug" - label="&cmd.debugAddon.label;" - oncommand="document.getBindingParent(this).debug();"/> - <xul:button anonid="enable-btn" class="addon-control enable" label="&cmd.enableAddon.label;" oncommand="document.getBindingParent(this).userDisabled = false;"/> @@ -1087,10 +1083,6 @@ document.getAnonymousElementByAttribute(this, "anonid", "enable-btn"); </field> - <field name="_debugBtn"> - document.getAnonymousElementByAttribute(this, "anonid", - "debug-btn"); - </field> <field name="_disableBtn"> document.getAnonymousElementByAttribute(this, "anonid", "disable-btn"); @@ -1430,12 +1422,6 @@ this.mAddon.install.state != AddonManager.STATE_INSTALLED); this._showStatus(showProgress ? "progress" : "none"); - let debuggable = this.mAddon.isDebuggable && - Services.prefs.getBoolPref('devtools.chrome.enabled') && - Services.prefs.getBoolPref('devtools.debugger.remote-enabled'); - - this._debugBtn.disabled = this._debugBtn.hidden = !debuggable - if (this.mAddon.type == "experiment") { this.removeAttribute("notification"); let prefix = "experiment."; diff --git a/toolkit/mozapps/extensions/internal/GMPProvider.jsm b/toolkit/mozapps/extensions/internal/GMPProvider.jsm index 25651f1b82..131db7249d 100644 --- a/toolkit/mozapps/extensions/internal/GMPProvider.jsm +++ b/toolkit/mozapps/extensions/internal/GMPProvider.jsm @@ -49,6 +49,7 @@ const GMP_PLUGINS = [ homepageURL: "http://www.openh264.org/", optionsURL: "chrome://mozapps/content/extensions/gmpPrefs.xul" }, +/* { id: EME_ADOBE_ID, name: "eme-adobe_name", @@ -57,6 +58,17 @@ const GMP_PLUGINS = [ homepageURL: "http://help.adobe.com/en_US/primetime/drm/HTML5_CDM", optionsURL: "chrome://mozapps/content/extensions/gmpPrefs.xul", isEME: true + }, +*/ + { + id: WIDEVINE_ID, + name: "widevine_description", + // Describe the purpose of both CDMs in the same way. + description: "eme-adobe_description", + licenseURL: "https://www.google.com/policies/privacy/", + homepageURL: "https://www.widevine.com/", + optionsURL: "chrome://mozapps/content/extensions/gmpPrefs.xul", + isEME: true }]; XPCOMUtils.defineConstant(this, "GMP_PLUGINS", GMP_PLUGINS); diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index 9ea876f6c9..99a121da4b 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -2584,7 +2584,7 @@ this.XPIProvider = { } } catch (e) { - logger.warn("Failed to call uninstall for " + id, e); + // If called on startup this may fail due to staged folder still existing. } try { diff --git a/toolkit/mozapps/extensions/moz.build b/toolkit/mozapps/extensions/moz.build index 3988cc27a5..104e8d734d 100644 --- a/toolkit/mozapps/extensions/moz.build +++ b/toolkit/mozapps/extensions/moz.build @@ -29,11 +29,13 @@ EXTRA_PP_COMPONENTS += [ EXTRA_JS_MODULES += [ 'ChromeManifestParser.jsm', 'DeferredSave.jsm', + 'GMPUtils.jsm', 'LightweightThemeManager.jsm', ] EXTRA_PP_JS_MODULES += [ - 'AddonManager.jsm' + 'AddonManager.jsm', + 'GMPInstallManager.jsm', ] # Additional debugging info is exposed in debug builds diff --git a/toolkit/modules/GMPInstallManager.jsm b/toolkit/mozapps/webextensions/GMPInstallManager.jsm index b5987ca55c..b5987ca55c 100644 --- a/toolkit/modules/GMPInstallManager.jsm +++ b/toolkit/mozapps/webextensions/GMPInstallManager.jsm diff --git a/toolkit/mozapps/webextensions/moz.build b/toolkit/mozapps/webextensions/moz.build index e703125e6f..f6e83a355b 100644 --- a/toolkit/mozapps/webextensions/moz.build +++ b/toolkit/mozapps/webextensions/moz.build @@ -30,8 +30,10 @@ EXTRA_PP_COMPONENTS += [ EXTRA_JS_MODULES += [ '../extensions/ChromeManifestParser.jsm', '../extensions/DeferredSave.jsm', + '../extensions/GMPUtils.jsm', 'AddonManager.jsm', 'LightweightThemeManager.jsm', + 'GMPInstallManager.jsm', ] JAR_MANIFESTS += ['jar.mn'] |