diff --git a/browser/components/urlbar/UrlbarInput.jsm b/browser/components/urlbar/UrlbarInput.jsm index 5098b57b8852..fd42ec0571b0 100644 --- a/browser/components/urlbar/UrlbarInput.jsm +++ b/browser/components/urlbar/UrlbarInput.jsm @@ -1294,6 +1294,8 @@ class UrlbarInput { _on_input() { let value = this.textValue; this.valueIsTyped = true; + let valueIsPasted = this._valueIsPasted; + this._valueIsPasted = false; this._untrimmedValue = value; this.window.gBrowser.userTypedValue = value; @@ -1351,7 +1353,7 @@ class UrlbarInput { return; } - let allowAutofill = + let allowAutofill = !valueIsPasted && this._maybeAutofillOnInput(value, deletedAutofilledSubstring); this.startQuery({ @@ -1417,7 +1419,7 @@ class UrlbarInput { if (!originalPasteData) { return; } - + this._valueIsPasted = true; let oldValue = this.inputField.value; let oldStart = oldValue.substring(0, this.selectionStart); // If there is already non-whitespace content in the URL bar diff --git a/browser/components/urlbar/tests/browser/browser.ini b/browser/components/urlbar/tests/browser/browser.ini index 8aeda9461512..35243a29e1ec 100644 --- a/browser/components/urlbar/tests/browser/browser.ini +++ b/browser/components/urlbar/tests/browser/browser.ini @@ -27,6 +27,7 @@ skip-if = os != "mac" # Mac only feature [browser_autoFill_canonize.js] [browser_autoFill_caretNotAtEnd.js] [browser_autoFill_firstResult.js] +[browser_autoFill_paste.js] [browser_autoFill_placeholder.js] [browser_autoFill_preserve.js] [browser_autoFill_trimURLs.js] diff --git a/browser/components/urlbar/tests/browser/browser_autoFill_paste.js b/browser/components/urlbar/tests/browser/browser_autoFill_paste.js new file mode 100644 index 000000000000..67611b6946a5 --- /dev/null +++ b/browser/components/urlbar/tests/browser/browser_autoFill_paste.js @@ -0,0 +1,52 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// This test checks we don't autofill on paste. + +"use strict"; + +async function paste(str) { + await SimpleTest.promiseClipboardChange(str, () => { + Cc["@mozilla.org/widget/clipboardhelper;1"] + .getService(Ci.nsIClipboardHelper) + .copyString(str); + }); + gURLBar.select(); + document.commandDispatcher.getControllerForCommand("cmd_paste") + .doCommand("cmd_paste"); +} + +add_task(async function test() { + await PlacesUtils.bookmarks.eraseEverything(); + await PlacesUtils.history.clear(); + await PlacesTestUtils.addVisits([ + "http://example.com/", + ]); + registerCleanupFunction(async () => { + await PlacesUtils.history.clear(); + }); + + // Search for "e". It should autofill to example.com/. + await UrlbarTestUtils.promiseAutocompleteResultPopup({ + window, + waitForFocus, + value: "e", + fireInputEvent: true, + }); + let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0); + Assert.ok(details.autofill); + Assert.equal(gURLBar.value, "example.com/"); + Assert.equal(gURLBar.selectionStart, "e".length); + Assert.equal(gURLBar.selectionEnd, "example.com/".length); + + // Now paste. + await paste("ex"); + + // Nothing should have been autofilled. + await UrlbarTestUtils.promiseSearchComplete(window); + details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0); + Assert.ok(!details.autofill); + Assert.equal(gURLBar.value, "ex"); + Assert.equal(gURLBar.selectionStart, "ex".length); + Assert.equal(gURLBar.selectionEnd, "ex".length); +}); diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index 652289aed05d..3b85645b4afc 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -57,11 +57,12 @@ border. */ #navigator-toolbox::after { + content: ""; + display: -moz-box; -moz-appearance: toolbox; height: 1px; - /* use inset box-shadow instead of border because -moz-appearance hides the border */ - border: none; - box-shadow: inset 0 -1px var(--chrome-content-separator-color); + margin-top: -1px; + opacity: 0; } #tabbrowser-tabs { diff --git a/browser/themes/shared/browser.inc.css b/browser/themes/shared/browser.inc.css index 1e445a58db14..c290806073b0 100644 --- a/browser/themes/shared/browser.inc.css +++ b/browser/themes/shared/browser.inc.css @@ -39,13 +39,11 @@ /* Toolbar / content area border */ -#navigator-toolbox::after { - content: ""; - display: -moz-box; +#navigator-toolbox { border-bottom: 1px solid var(--chrome-content-separator-color); } -:root[customizing] #navigator-toolbox::after { +:root[customizing] #navigator-toolbox { border-bottom-style: none; } diff --git a/browser/themes/windows/browser-aero.css b/browser/themes/windows/browser-aero.css index 574388da3a4e..58b595c46fb0 100644 --- a/browser/themes/windows/browser-aero.css +++ b/browser/themes/windows/browser-aero.css @@ -308,7 +308,18 @@ background-clip: padding-box; } - #main-window[sizemode=normal] #navigator-toolbox::after { + #navigator-toolbox::after { + content: ""; + display: -moz-box; + border-bottom: 1px solid var(--chrome-content-separator-color); + } + + #navigator-toolbox, + :root[customizing] #navigator-toolbox::after { + border-bottom-style: none; + } + + :root[sizemode=normal] #navigator-toolbox::after { box-shadow: 1px 0 0 @glassShadowColor@, -1px 0 0 @glassShadowColor@; margin-left: 1px; margin-right: 1px; diff --git a/devtools/client/locales/en-US/netmonitor.properties b/devtools/client/locales/en-US/netmonitor.properties index 577cca8fbfe9..9b92eae103bb 100644 --- a/devtools/client/locales/en-US/netmonitor.properties +++ b/devtools/client/locales/en-US/netmonitor.properties @@ -959,6 +959,14 @@ netmonitor.context.copyAsCurl=Copy as cURL # for the Copy as cURL menu item displayed in the context menu for a request netmonitor.context.copyAsCurl.accesskey=C +# LOCALIZATION NOTE (netmonitor.context.copyAsFetch): This is the label displayed +# on the context menu that copies the selected request as a fetch request. +netmonitor.context.copyAsFetch=Copy as Fetch + +# LOCALIZATION NOTE (netmonitor.context.copyAsFetch.accesskey): This is the access key +# for the Copy as fetch menu item displayed in the context menu for a request +netmonitor.context.copyAsFetch.accesskey=F + # LOCALIZATION NOTE (netmonitor.context.copyRequestHeaders): This is the label displayed # on the context menu that copies the selected item's request headers netmonitor.context.copyRequestHeaders=Copy Request Headers @@ -991,6 +999,14 @@ netmonitor.context.copyImageAsDataUri=Copy Image as Data URI # for the Copy Image As Data URI menu item displayed in the context menu for a request netmonitor.context.copyImageAsDataUri.accesskey=I +# LOCALIZATION NOTE (netmonitor.context.useAsFetch): This is the label displayed +# on the context menu that copies the selected request as a fetch command. +netmonitor.context.useAsFetch=Use as Fetch in Console + +# LOCALIZATION NOTE (netmonitor.context.useAsFetch.accesskey): This is the access key +# for the Copy as fetch menu item displayed in the context menu for a request +netmonitor.context.useAsFetch.accesskey=F + # LOCALIZATION NOTE (netmonitor.context.saveImageAs): This is the label displayed # on the context menu that save the Image netmonitor.context.saveImageAs=Save Image As diff --git a/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js b/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js index 2a74bc070764..2cf2b3e23256 100644 --- a/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js +++ b/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js @@ -90,6 +90,15 @@ class RequestListContextMenu { this.copyAsCurl(id, url, method, httpVersion, requestHeaders, requestPostData), }); + copySubmenu.push({ + id: "request-list-context-copy-as-fetch", + label: L10N.getStr("netmonitor.context.copyAsFetch"), + accesskey: L10N.getStr("netmonitor.context.copyAsFetch.accesskey"), + visible: !!selectedRequest, + click: () => + this.copyAsFetch(id, url, method, requestHeaders, requestPostData), + }); + copySubmenu.push({ type: "separator", visible: copySubmenu.slice(0, 4).some((subMenu) => subMenu.visible), @@ -231,6 +240,19 @@ class RequestListContextMenu { click: () => openStatistics(true), }); + menu.push({ + type: "separator", + }); + + menu.push({ + id: "request-list-context-use-as-fetch", + label: L10N.getStr("netmonitor.context.useAsFetch"), + accesskey: L10N.getStr("netmonitor.context.useAsFetch.accesskey"), + visible: !!selectedRequest, + click: () => + this.useAsFetch(id, url, method, requestHeaders, requestPostData), + }); + showMenu(menu, { screenX: event.screenX, screenY: event.screenY, @@ -324,6 +346,107 @@ class RequestListContextMenu { copyString(Curl.generateCommand(data)); } + /** + * Generate fetch string + */ + async generateFetchString(id, url, method, requestHeaders, requestPostData) { + requestHeaders = requestHeaders || + await this.props.connector.requestData(id, "requestHeaders"); + + requestPostData = requestPostData || + await this.props.connector.requestData(id, "requestPostData"); + + // https://fetch.spec.whatwg.org/#forbidden-header-name + const forbiddenHeaders = { + "accept-charset": 1, + "accept-encoding": 1, + "access-control-request-headers": 1, + "access-control-request-method": 1, + "connection": 1, + "content-length": 1, + "cookie": 1, + "cookie2": 1, + "date": 1, + "dnt": 1, + "expect": 1, + "host": 1, + "keep-alive": 1, + "origin": 1, + "referer": 1, + "te": 1, + "trailer": 1, + "transfer-encoding": 1, + "upgrade": 1, + "via": 1, + }; + const credentialHeaders = {"cookie": 1, "authorization": 1}; + + const headers = {}; + for (const {name, value} of requestHeaders.headers) { + if (!forbiddenHeaders[name.toLowerCase()]) { + headers[name] = value; + } + } + + const referrerHeader = requestHeaders.headers.find( + ({ name }) => name.toLowerCase() === "referer" + ); + + const referrerPolicy = requestHeaders.headers.find( + ({ name }) => name.toLowerCase() === "referrer-policy" + ); + + const referrer = referrerHeader ? referrerHeader.value : undefined; + const credentials = requestHeaders.headers.some( + ({name}) => credentialHeaders[name.toLowerCase()] + ) ? "include" : "omit"; + + const fetchOptions = { + credentials, + headers, + referrer, + referrerPolicy, + body: requestPostData.postData.text, + method: method, + mode: "cors", + }; + + const options = JSON.stringify(fetchOptions, null, 4); + const fetchString = `await fetch("${url}", ${options});`; + return fetchString; + } + + /** + * Copy the currently selected item as fetch request. + */ + async copyAsFetch(id, url, method, requestHeaders, requestPostData) { + const fetchString = await this.generateFetchString( + id, + url, + method, + requestHeaders, + requestPostData + ); + copyString(fetchString); + } + + /** + * Open split console and fill it with fetch command for selected item + */ + async useAsFetch(id, url, method, requestHeaders, requestPostData) { + const fetchString = await this.generateFetchString( + id, + url, + method, + requestHeaders, + requestPostData + ); + const toolbox = gDevTools.getToolbox(this.props.connector.getTabTarget()); + await toolbox.openSplitConsole(); + const { hud } = await toolbox.getPanel("webconsole"); + hud.setInputValue(fetchString); + } + /** * Copy the raw request headers from the currently selected item. */ diff --git a/devtools/client/netmonitor/test/browser.ini b/devtools/client/netmonitor/test/browser.ini index 53254cab4fe2..5997e833f3b5 100644 --- a/devtools/client/netmonitor/test/browser.ini +++ b/devtools/client/netmonitor/test/browser.ini @@ -116,6 +116,9 @@ subsuite = clipboard skip-if = (verify && debug && os == 'win') [browser_net_copy_as_curl.js] subsuite = clipboard +[browser_net_copy_as_fetch.js] +subsuite = clipboard +[browser_net_use_as_fetch.js] [browser_net_cors_requests.js] [browser_net_cyrillic-01.js] [browser_net_cyrillic-02.js] diff --git a/devtools/client/netmonitor/test/browser_net_copy_as_fetch.js b/devtools/client/netmonitor/test/browser_net_copy_as_fetch.js new file mode 100644 index 000000000000..8a479c0ab246 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_copy_as_fetch.js @@ -0,0 +1,73 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if Copy as Fetch works. + */ + +add_task(async function() { + const { tab, monitor } = await initNetMonitor(CURL_URL); + info("Starting test... "); + + // GET request, no cookies (first request) + await performRequest("GET"); + await testClipboardContent(`await fetch("http://example.com/browser/devtools/client/netmonitor/test/sjs_simple-test-server.sjs", { + "credentials": "omit", + "headers": { + "User-Agent": "${navigator.userAgent}", + "Accept": "*/*", + "Accept-Language": "en-US", + "X-Custom-Header-1": "Custom value", + "X-Custom-Header-2": "8.8.8.8", + "X-Custom-Header-3": "Mon, 3 Mar 2014 11:11:11 GMT", + "Pragma": "no-cache", + "Cache-Control": "no-cache" + }, + "referrer": "http://example.com/browser/devtools/client/netmonitor/test/html_copy-as-curl.html", + "method": "GET", + "mode": "cors" +});`); + + await teardown(monitor); + + async function performRequest(method, payload) { + const waitRequest = waitForNetworkEvents(monitor, 1); + await ContentTask.spawn(tab.linkedBrowser, { + url: SIMPLE_SJS, + method_: method, + payload_: payload, + }, async function({url, method_, payload_}) { + content.wrappedJSObject.performRequest(url, method_, payload_); + }); + await waitRequest; + } + + async function testClipboardContent(expectedResult) { + const { document } = monitor.panelWin; + + const items = document.querySelectorAll(".request-list-item"); + EventUtils.sendMouseEvent({ type: "mousedown" }, items[items.length - 1]); + EventUtils.sendMouseEvent({ type: "contextmenu" }, + document.querySelectorAll(".request-list-item")[0]); + + /* Ensure that the copy as fetch option is always visible */ + const copyAsFetchNode = monitor.panelWin.parent.document + .querySelector("#request-list-context-copy-as-fetch"); + is(!!copyAsFetchNode, true, + "The \"Copy as Fetch\" context menu item should not be hidden."); + + await waitForClipboardPromise(function setup() { + copyAsFetchNode.click(); + }, function validate(result) { + if (typeof result !== "string") { + return false; + } + + return expectedResult === result; + }); + + info("Clipboard contains a fetch command for item " + (items.length - 1)); + } +}); diff --git a/devtools/client/netmonitor/test/browser_net_use_as_fetch.js b/devtools/client/netmonitor/test/browser_net_use_as_fetch.js new file mode 100644 index 000000000000..bbdf34b3632e --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_use_as_fetch.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if Use as Fetch works. + */ + +add_task(async function() { + const { tab, monitor, toolbox } = await initNetMonitor(CURL_URL); + info("Starting test... "); + + // GET request, no cookies (first request) + await performRequest("GET"); + await testConsoleInput(`await fetch("http://example.com/browser/devtools/client/netmonitor/test/sjs_simple-test-server.sjs", { + "credentials": "omit", + "headers": { + "User-Agent": "${navigator.userAgent}", + "Accept": "*/*", + "Accept-Language": "en-US", + "X-Custom-Header-1": "Custom value", + "X-Custom-Header-2": "8.8.8.8", + "X-Custom-Header-3": "Mon, 3 Mar 2014 11:11:11 GMT", + "Pragma": "no-cache", + "Cache-Control": "no-cache" + }, + "referrer": "http://example.com/browser/devtools/client/netmonitor/test/html_copy-as-curl.html", + "method": "GET", + "mode": "cors" +});`); + + await teardown(monitor); + + async function performRequest(method, payload) { + const waitRequest = waitForNetworkEvents(monitor, 1); + await ContentTask.spawn(tab.linkedBrowser, { + url: SIMPLE_SJS, + method_: method, + payload_: payload, + }, async function({url, method_, payload_}) { + content.wrappedJSObject.performRequest(url, method_, payload_); + }); + await waitRequest; + } + + async function testConsoleInput(expectedResult) { + const { document } = monitor.panelWin; + + const items = document.querySelectorAll(".request-list-item"); + EventUtils.sendMouseEvent({ type: "mousedown" }, items[items.length - 1]); + EventUtils.sendMouseEvent({ type: "contextmenu" }, + document.querySelectorAll(".request-list-item")[0]); + + /* Ensure that the use as fetch option is always visible */ + const useAsFetchNode = monitor.panelWin.parent.document + .querySelector("#request-list-context-use-as-fetch"); + is(!!useAsFetchNode, true, + "The \"Use as Fetch\" context menu item should not be hidden."); + + useAsFetchNode.click(); + await toolbox.once("split-console"); + const hud = toolbox.getPanel("webconsole").hud; + await hud.jsterm.once("set-input-value"); + + is(hud.getInputValue(), expectedResult, + "Console input contains fetch request for item " + (items.length - 1)); + } +}); diff --git a/dom/media/GraphDriver.cpp b/dom/media/GraphDriver.cpp index c13049d66163..ff392bec5a8c 100644 --- a/dom/media/GraphDriver.cpp +++ b/dom/media/GraphDriver.cpp @@ -419,8 +419,9 @@ AsyncCubebTask::AsyncCubebTask(AudioCallbackDriver* aDriver, mDriver(aDriver), mOperation(aOperation), mShutdownGrip(aDriver->GraphImpl()) { - NS_WARNING_ASSERTION(mDriver->mAudioStream || aOperation == INIT, - "No audio stream!"); + NS_WARNING_ASSERTION( + mDriver->mAudioStream || aOperation == AsyncCubebOperation::INIT, + "No audio stream!"); } AsyncCubebTask::~AsyncCubebTask() {} @@ -441,6 +442,22 @@ AsyncCubebTask::Run() { mDriver->CompleteAudioContextOperations(mOperation); break; } + case AsyncCubebOperation::START: { + LOG(LogLevel::Debug, ("%p: AsyncCubebOperation::START driver=%p", + mDriver->GraphImpl(), mDriver.get())); + if (!mDriver->StartStream()) { + LOG(LogLevel::Warning, + ("%p: AsyncCubebOperation couldn't start the driver=%p.", + mDriver->GraphImpl(), mDriver.get())); + } + break; + } + case AsyncCubebOperation::STOP: { + LOG(LogLevel::Debug, ("%p: AsyncCubebOperation::STOP driver=%p", + mDriver->GraphImpl(), mDriver.get())); + mDriver->Stop(); + break; + } case AsyncCubebOperation::SHUTDOWN: { LOG(LogLevel::Debug, ("%p: AsyncCubebOperation::SHUTDOWN driver=%p", mDriver->GraphImpl(), mDriver.get())); @@ -713,6 +730,7 @@ void AudioCallbackDriver::Stop() { if (cubeb_stream_stop(mAudioStream) != CUBEB_OK) { NS_WARNING("Could not stop cubeb stream for MSG."); } + mStarted = false; } void AudioCallbackDriver::Revive() { @@ -728,9 +746,16 @@ void AudioCallbackDriver::Revive() { LOG(LogLevel::Debug, ("Starting audio threads for MediaStreamGraph %p from a new thread.", mGraphImpl.get())); - RefPtr initEvent = - new AsyncCubebTask(this, AsyncCubebOperation::INIT); - initEvent->Dispatch(); + if (IsStarted()) { + RefPtr stopEvent = + new AsyncCubebTask(this, AsyncCubebOperation::STOP); + // This dispatches to a thread pool with a maximum of one thread thus it + // is guaranteed to be executed before the start event, right below. + stopEvent->Dispatch(); + } + RefPtr startEvent = + new AsyncCubebTask(this, AsyncCubebOperation::START); + startEvent->Dispatch(); } } diff --git a/dom/media/GraphDriver.h b/dom/media/GraphDriver.h index 1cac5a76a128..7bcf6acecbf9 100644 --- a/dom/media/GraphDriver.h +++ b/dom/media/GraphDriver.h @@ -322,7 +322,7 @@ struct StreamAndPromiseForOperation { dom::AudioContextOperationFlags mFlags; }; -enum AsyncCubebOperation { INIT, SHUTDOWN }; +enum class AsyncCubebOperation { INIT, START, STOP, SHUTDOWN }; enum class AudioInputType { Unknown, Voice }; /** diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp index a165e6a1075f..e7ae631e465b 100644 --- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -761,14 +761,7 @@ void MediaStreamGraphImpl::OpenAudioInputImpl(CubebUtils::AudioDeviceID aID, nsresult MediaStreamGraphImpl::OpenAudioInput(CubebUtils::AudioDeviceID aID, AudioDataListener* aListener) { - // So, so, so annoying. Can't AppendMessage except on Mainthread - if (!NS_IsMainThread()) { - RefPtr runnable = - WrapRunnable(this, &MediaStreamGraphImpl::OpenAudioInput, aID, - RefPtr(aListener)); - mAbstractMainThread->Dispatch(runnable.forget()); - return NS_OK; - } + MOZ_ASSERT(NS_IsMainThread()); class Message : public ControlMessage { public: Message(MediaStreamGraphImpl* aGraph, CubebUtils::AudioDeviceID aID, @@ -845,14 +838,7 @@ void MediaStreamGraphImpl::CloseAudioInputImpl( void MediaStreamGraphImpl::CloseAudioInput( Maybe& aID, AudioDataListener* aListener) { - // So, so, so annoying. Can't AppendMessage except on Mainthread - if (!NS_IsMainThread()) { - RefPtr runnable = - WrapRunnable(this, &MediaStreamGraphImpl::CloseAudioInput, aID, - RefPtr(aListener)); - mAbstractMainThread->Dispatch(runnable.forget()); - return; - } + MOZ_ASSERT(NS_IsMainThread()); class Message : public ControlMessage { public: Message(MediaStreamGraphImpl* aGraph, Maybe& aID, @@ -2421,25 +2407,32 @@ SourceMediaStream::SourceMediaStream() nsresult SourceMediaStream::OpenAudioInput(CubebUtils::AudioDeviceID aID, AudioDataListener* aListener) { + MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(GraphImpl()); + MOZ_ASSERT(!mInputListener); mInputListener = aListener; return GraphImpl()->OpenAudioInput(aID, aListener); } -void SourceMediaStream::CloseAudioInput(Maybe& aID, - AudioDataListener* aListener) { - MOZ_ASSERT(mInputListener == aListener); - // Destroy() may have run already and cleared this - if (GraphImpl() && mInputListener) { - GraphImpl()->CloseAudioInput(aID, aListener); +void SourceMediaStream::CloseAudioInput(Maybe& aID) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(GraphImpl()); + if (!mInputListener) { + return; } + GraphImpl()->CloseAudioInput(aID, mInputListener); mInputListener = nullptr; } -void SourceMediaStream::DestroyImpl() { +void SourceMediaStream::Destroy() { + MOZ_ASSERT(NS_IsMainThread()); Maybe id = Nothing(); - CloseAudioInput(id, mInputListener); + CloseAudioInput(id); + MediaStream::Destroy(); +} + +void SourceMediaStream::DestroyImpl() { GraphImpl()->AssertOnGraphThreadOrNotRunning(); for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) { // Disconnect before we come under mMutex's lock since it can call back diff --git a/dom/media/MediaStreamGraph.h b/dom/media/MediaStreamGraph.h index c21325615f87..fef23a14d0db 100644 --- a/dom/media/MediaStreamGraph.h +++ b/dom/media/MediaStreamGraph.h @@ -655,14 +655,14 @@ class SourceMediaStream : public MediaStream { // Users of audio inputs go through the stream so it can track when the // last stream referencing an input goes away, so it can close the cubeb - // input. Also note: callable on any thread (though it bounces through - // MainThread to set the command if needed). + // input. Main thread only. nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID, AudioDataListener* aListener); - // Note: also implied when Destroy() happens - void CloseAudioInput(Maybe& aID, - AudioDataListener* aListener); + // Main thread only. + void CloseAudioInput(Maybe& aID); + // Main thread only. + void Destroy() override; // MediaStreamGraph thread only void DestroyImpl() override; diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index 973f34c21abd..7374a558b9d6 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -601,7 +601,7 @@ nsresult MediaEngineWebRTCMicrophoneSource::Stop() { that->mInputProcessing, StartStopMessage::Stop)); CubebUtils::AudioDeviceID deviceID = that->mDeviceInfo->DeviceID(); Maybe id = Some(deviceID); - stream->CloseAudioInput(id, that->mInputProcessing); + stream->CloseAudioInput(id); })); MOZ_ASSERT(mState == kStarted, "Should be started when stopping"); diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index 4b2ce327f797..d32bd8586c36 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -212,7 +212,7 @@ pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla /* preferences for the Get Add-ons pane */ pref("extensions.getAddons.cache.enabled", true); pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/android/search?q=%TERMS%&platform=%OS%&appver=%VERSION%"); -pref("extensions.getAddons.browseAddons", "https://addons.mozilla.org/%LOCALE%/android/"); +pref("extensions.getAddons.browseAddons", "https://addons.mozilla.org/%LOCALE%/firefox/collections/4757633/mob/"); pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/api/v3/addons/search/?guid=%IDS%&lang=%LOCALE%"); pref("extensions.getAddons.compatOverides.url", "https://services.addons.mozilla.org/api/v3/addons/compat-override/?guid=%IDS%&lang=%LOCALE%"); pref("extensions.getAddons.langpacks.url", "https://services.addons.mozilla.org/api/v3/addons/language-tools/?app=android&type=language&appversion=%VERSION%"); diff --git a/mobile/android/chrome/content/aboutAddons.js b/mobile/android/chrome/content/aboutAddons.js index a754ba97925d..19701380ed34 100644 --- a/mobile/android/chrome/content/aboutAddons.js +++ b/mobile/android/chrome/content/aboutAddons.js @@ -9,6 +9,7 @@ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); const {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"); const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +const {EventDispatcher} = ChromeUtils.import("resource://gre/modules/Messaging.jsm"); const AMO_ICON = "chrome://browser/skin/images/amo-logo.png"; const UPDATE_INDICATOR = "chrome://browser/skin/images/extension-update.svg"; @@ -261,13 +262,34 @@ var Addons = { let title = document.createElement("div"); title.id = "browse-title"; title.className = "title"; - title.textContent = gStringBundle.GetStringFromName("addons.browseAll"); + title.textContent = this._getAmoTitle(); inner.appendChild(title); outer.appendChild(inner); return outer; }, + // Ensure we get a localized string by using the previous title as a fallback + // if the new one has not yet been translated. + _getAmoTitle: function _getAmoTitle() { + const initialTitleUS = "Browse all Firefox Add-ons"; + const updatedTitleUS = "Browse Firefox’s Recommended Extensions"; + const initialTitleLocalized = gStringBundle.GetStringFromName("addons.browseAll"); + const updatedTitleLocalized = gStringBundle.GetStringFromName("addons.browseRecommended"); + let title = initialTitleLocalized; + + const titleWasLocalized = updatedTitleLocalized !== updatedTitleUS; + const localeIsDefaultUS = updatedTitleLocalized === updatedTitleUS && + initialTitleLocalized === initialTitleUS; + + if (titleWasLocalized || localeIsDefaultUS) { + title = updatedTitleLocalized; + } + + EventDispatcher.instance.dispatch("about:addons", {amoTitle: title} ); + return title; + }, + _createItemForAddon: function _createItemForAddon(aAddon) { let opType = this._getOpTypeForOperations(aAddon.pendingOperations); let hasUpdate = this._addonHasUpdate(aAddon); diff --git a/mobile/android/locales/en-US/chrome/aboutAddons.properties b/mobile/android/locales/en-US/chrome/aboutAddons.properties index c9596f7f9313..f1d48a0252f7 100644 --- a/mobile/android/locales/en-US/chrome/aboutAddons.properties +++ b/mobile/android/locales/en-US/chrome/aboutAddons.properties @@ -8,6 +8,8 @@ addonType.locale=Locale addonStatus.uninstalled=%S will be uninstalled after restart. +# Will keep both strings and at runtime will fallback on the old one if the new one is not yet localized addons.browseAll=Browse all Firefox Add-ons +addons.browseRecommended=Browse Firefox’s Recommended Extensions -addon.options=Options \ No newline at end of file +addon.options=Options diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/StringHelper.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/StringHelper.java index a5e189b579ae..9bc0afe87c4a 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/StringHelper.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/StringHelper.java @@ -34,6 +34,9 @@ public class StringHelper { // About pages' titles public final String ABOUT_HOME_TITLE = ""; + // To be kept in sync with 'addons.browseRecommended' from 'aboutAddons.properties' + public final String ABOUT_ADDONS_AMO_TITLE = "Browse Firefox’s Recommended Extensions"; + // Context Menu item strings public final String CONTEXT_MENU_BOOKMARK_LINK = "Bookmark Link"; public final String CONTEXT_MENU_OPEN_LINK_IN_NEW_TAB = "Open Link in New Tab"; diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAddonManager.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAddonManager.java index 50f78f878062..4e4f7d13a467 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAddonManager.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAddonManager.java @@ -6,6 +6,7 @@ package org.mozilla.gecko.tests; import org.json.JSONObject; import org.mozilla.gecko.Actions; +import org.mozilla.gecko.util.GeckoBundle; import android.util.DisplayMetrics; @@ -21,7 +22,9 @@ public class testAddonManager extends PixelTest { public void testAddonManager() { Actions.EventExpecter tabEventExpecter; Actions.EventExpecter contentEventExpecter; + Actions.EventExpecter amoTitleExpecter; final String aboutAddonsURL = mStringHelper.ABOUT_ADDONS_URL; + final String amoTitle = mStringHelper.ABOUT_ADDONS_AMO_TITLE; blockForGeckoReady(); @@ -31,13 +34,20 @@ public class testAddonManager extends PixelTest { // Set up listeners to catch the page load we're about to do tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); contentEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Content:DOMContentLoaded"); + amoTitleExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "about:addons"); // Wait for the new tab and page to load tabEventExpecter.blockForEvent(); contentEventExpecter.blockForEvent(); + GeckoBundle addonsPageBundle = amoTitleExpecter.blockForBundle(); tabEventExpecter.unregisterListener(); contentEventExpecter.unregisterListener(); + amoTitleExpecter.unregisterListener(); + + // Verify the AMO title + final String actualAmoTitle = addonsPageBundle.getString("amoTitle"); + mAsserter.is(actualAmoTitle, amoTitle, "Incorrect AMO title"); // Verify the url verifyUrlBarTitle(aboutAddonsURL); diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp index fd0afe2ff33e..50773dddcb66 100644 --- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -120,6 +120,8 @@ nsHttpConnection::nsHttpConnection() mIdleTimeout = (k5Sec < gHttpHandler->IdleTimeout()) ? k5Sec : gHttpHandler->IdleTimeout(); + + mThroughCaptivePortal = gHttpHandler->GetThroughCaptivePortal(); } nsHttpConnection::~nsHttpConnection() { @@ -153,6 +155,20 @@ nsHttpConnection::~nsHttpConnection() { : Telemetry::HTTP_KBREAD_PER_CONN2, totalKBRead); } + + if (mThroughCaptivePortal) { + if (mTotalBytesRead || mTotalBytesWritten) { + auto total = Clamp( + (mTotalBytesRead >> 10) + (mTotalBytesWritten >> 10), 0, + std::numeric_limits::max()); + Telemetry::ScalarAdd( + Telemetry::ScalarID::NETWORKING_DATA_TRANSFERRED_CAPTIVE_PORTAL, total); + } + + Telemetry::ScalarAdd( + Telemetry::ScalarID::NETWORKING_HTTP_CONNECTIONS_CAPTIVE_PORTAL, 1); + } + if (mForceSendTimer) { mForceSendTimer->Cancel(); mForceSendTimer = nullptr; diff --git a/netwerk/protocol/http/nsHttpConnection.h b/netwerk/protocol/http/nsHttpConnection.h index 6973c6781827..54053d3cfdad 100644 --- a/netwerk/protocol/http/nsHttpConnection.h +++ b/netwerk/protocol/http/nsHttpConnection.h @@ -441,6 +441,7 @@ class nsHttpConnection final : public nsAHttpSegmentReader, bool mBootstrappedTimingsSet; nsTArray mTrafficCategory; + bool mThroughCaptivePortal; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsHttpConnection, NS_HTTPCONNECTION_IID) diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 886fbb7e7344..673984648fd2 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -307,7 +307,8 @@ nsHttpHandler::nsHttpHandler() mProcessId(0), mNextChannelId(1), mLastActiveTabLoadOptimizationLock( - "nsHttpConnectionMgr::LastActiveTabLoadOptimization") { + "nsHttpConnectionMgr::LastActiveTabLoadOptimization"), + mThroughCaptivePortal(false) { LOG(("Creating nsHttpHandler [this=%p].\n", this)); mUserAgentOverride.SetIsVoid(true); @@ -567,6 +568,7 @@ nsresult nsHttpHandler::Init() { obsService->AddObserver(this, "psm:user-certificate-deleted", true); obsService->AddObserver(this, "intl:app-locales-changed", true); obsService->AddObserver(this, "browser-delayed-startup-finished", true); + obsService->AddObserver(this, "network:captive-portal-connectivity", true); if (!IsNeckoChild()) { obsService->AddObserver( @@ -2338,6 +2340,9 @@ nsHttpHandler::Observe(nsISupports *subject, const char *topic, mAcceptLanguagesIsDirty = true; } else if (!strcmp(topic, "browser-delayed-startup-finished")) { MaybeEnableSpeculativeConnect(); + } else if (!strcmp(topic, "network:captive-portal-connectivity")) { + nsAutoCString data8 = NS_ConvertUTF16toUTF8(data); + mThroughCaptivePortal = data8.EqualsLiteral("captive"); } return NS_OK; diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index b02f7eab10e5..08d6ba4b498c 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -422,6 +422,7 @@ class nsHttpHandler final : public nsIHttpProtocolHandler, HttpTrafficAnalyzer *GetHttpTrafficAnalyzer(); + bool GetThroughCaptivePortal() { return mThroughCaptivePortal; } private: nsHttpHandler(); @@ -737,6 +738,8 @@ class nsHttpHandler final : public nsIHttpProtocolHandler, private: nsTHashtable mBlacklistedSpdyOrigins; + + bool mThroughCaptivePortal; }; extern StaticRefPtr gHttpHandler; diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp index 99516732455d..73eeda48b7c6 100644 --- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -158,6 +158,8 @@ nsHttpTransaction::nsHttpTransaction() #endif mSelfAddr.raw.family = PR_AF_UNSPEC; mPeerAddr.raw.family = PR_AF_UNSPEC; + + mThroughCaptivePortal = gHttpHandler->GetThroughCaptivePortal(); } void nsHttpTransaction::ResumeReading() { @@ -1196,6 +1198,11 @@ void nsHttpTransaction::Close(nsresult reason) { } } + if (mThroughCaptivePortal) { + Telemetry::ScalarAdd( + Telemetry::ScalarID::NETWORKING_HTTP_TRANSACTIONS_CAPTIVE_PORTAL, 1); + } + if (relConn && mConnection) { MutexAutoLock lock(mLock); mConnection = nullptr; diff --git a/netwerk/protocol/http/nsHttpTransaction.h b/netwerk/protocol/http/nsHttpTransaction.h index aa70956904aa..033e31f86cb7 100644 --- a/netwerk/protocol/http/nsHttpTransaction.h +++ b/netwerk/protocol/http/nsHttpTransaction.h @@ -484,6 +484,7 @@ class nsHttpTransaction final : public nsAHttpTransaction, RefPtr mH2WSTransaction; HttpTrafficCategory mTrafficCategory; + bool mThroughCaptivePortal; }; } // namespace net diff --git a/testing/web-platform/meta/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html.ini b/testing/web-platform/meta/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html.ini index 231c4bca522a..406ad2a4233f 100644 --- a/testing/web-platform/meta/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html.ini +++ b/testing/web-platform/meta/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html.ini @@ -1,6 +1,8 @@ [sub-sample-buffer-stitching.html] disabled: if (os == 'win' and processor == 'aarch64'): https://bugzilla.mozilla.org/show_bug.cgi?id=1533911 + if (os == 'win' and version == '6.1.7601'): https://bugzilla.mozilla.org/show_bug.cgi?id=1533762 + if (os == 'linux' and bits == 32): https://bugzilla.mozilla.org/show_bug.cgi?id=1533762 [# AUDIT TASK RUNNER FINISHED: 2 out of 2 tasks were failed.] expected: FAIL diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js b/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js index ab61ca1f23d8..36f676152820 100644 --- a/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js +++ b/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js @@ -56,19 +56,11 @@ add_task(async function test_support_separator_properties() { ); let toolbox = document.querySelector("#navigator-toolbox"); - if (AppConstants.platform == "macosx") { - Assert.ok( - window.getComputedStyle(toolbox, "::after").boxShadow - .includes(`rgb(${hexToRGB(SEPARATOR_BOTTOM_COLOR).join(", ")})`), - "Bottom separator color properly set" - ); - } else { - Assert.equal( - window.getComputedStyle(toolbox, "::after").borderBottomColor, - `rgb(${hexToRGB(SEPARATOR_BOTTOM_COLOR).join(", ")})`, - "Bottom separator color properly set" - ); - } + Assert.equal( + window.getComputedStyle(toolbox).borderBottomColor, + `rgb(${hexToRGB(SEPARATOR_BOTTOM_COLOR).join(", ")})`, + "Bottom separator color properly set" + ); await extension.unload(); }); diff --git a/toolkit/components/search/SearchService.jsm b/toolkit/components/search/SearchService.jsm index 44a5975b93cb..317b99207124 100644 --- a/toolkit/components/search/SearchService.jsm +++ b/toolkit/components/search/SearchService.jsm @@ -2862,6 +2862,8 @@ SearchService.prototype = { return val; }, + _listJSONURL: ((AppConstants.platform == "android") ? APP_SEARCH_PREFIX : EXT_SEARCH_PREFIX) + "list.json", + _engines: { }, __sortedEngines: null, _visibleDefaultEngines: [], @@ -3467,11 +3469,9 @@ SearchService.prototype = { async _findEngines() { LOG("_findEngines: looking for engines in JARs"); - let prefix = AppConstants.platform == "android" ? APP_SEARCH_PREFIX : EXT_SEARCH_PREFIX; - let listURL = prefix + "list.json"; - let chan = makeChannel(listURL); + let chan = makeChannel(this._listJSONURL); if (!chan) { - LOG("_findEngines: " + prefix + " isn't registered"); + LOG("_findEngines: " + this._listJSONURL + " isn't registered"); return []; } @@ -3485,10 +3485,10 @@ SearchService.prototype = { resolve(event.target.responseText); }; request.onerror = function(event) { - LOG("_findEngines: failed to read " + listURL); + LOG("_findEngines: failed to read " + this._listJSONURL); resolve(); }; - request.open("GET", Services.io.newURI(listURL).spec, true); + request.open("GET", Services.io.newURI(this._listJSONURL).spec, true); request.send(); }); diff --git a/toolkit/components/search/tests/xpcshell/test_validate_engines.js b/toolkit/components/search/tests/xpcshell/test_validate_engines.js new file mode 100644 index 000000000000..98a64e5d6d90 --- /dev/null +++ b/toolkit/components/search/tests/xpcshell/test_validate_engines.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Ensure all the engines defined in list.json are valid by +// creating a new list.json that contains every engine and +// loading them all. + +"use strict"; + +Cu.importGlobalProperties(["fetch"]); + +const {SearchService} = ChromeUtils.import("resource://gre/modules/SearchService.jsm"); +const LIST_JSON_URL = "resource://search-extensions/list.json"; + +function traverse(obj, fun) { + for (var i in obj) { + fun.apply(this, [i, obj[i]]); + if (obj[i] !== null && typeof(obj[i]) == "object") { + traverse(obj[i], fun); + } + } +} + +const ss = new SearchService(); + +add_task(async function test_validate_engines() { + let engines = await fetch(LIST_JSON_URL).then(req => req.json()); + + let visibleDefaultEngines = new Set(); + traverse(engines, (key, val) => { + if (key === "visibleDefaultEngines") { + val.forEach(engine => visibleDefaultEngines.add(engine)); + } + }); + + let listjson = {default: { + visibleDefaultEngines: Array.from(visibleDefaultEngines), + }}; + ss._listJSONURL = "data:application/json," + JSON.stringify(listjson); + + await AddonTestUtils.promiseStartupManager(); + await ss.init(); +}); diff --git a/toolkit/components/search/tests/xpcshell/xpcshell.ini b/toolkit/components/search/tests/xpcshell/xpcshell.ini index 9897a20192e7..32aed845cbc1 100644 --- a/toolkit/components/search/tests/xpcshell/xpcshell.ini +++ b/toolkit/components/search/tests/xpcshell/xpcshell.ini @@ -107,6 +107,7 @@ skip-if = (verify && !debug && (os == 'linux')) [test_paramSubstitution.js] [test_migrateWebExtensionEngine.js] [test_sendSubmissionURL.js] +[test_validate_engines.js] [test_validate_manifests.js] [test_webextensions_install.js] [test_purpose.js] diff --git a/toolkit/components/telemetry/Scalars.yaml b/toolkit/components/telemetry/Scalars.yaml index 186be155a031..753f2ecfc4c3 100644 --- a/toolkit/components/telemetry/Scalars.yaml +++ b/toolkit/components/telemetry/Scalars.yaml @@ -2692,6 +2692,772 @@ update: operating_systems: - windows +update.startup: + from_app_version: + bug_numbers: + - 1539154 + description: > + Records the previous application version that the update was applied to + when the update makes it to the last phase where the application has + exited and started. + expires: "72" + kind: string + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - 'main' + + mar_partial_size_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes of a partial update MAR file when + the update makes it to the last phase where the application has exited and + started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + mar_complete_size_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes of a complete update MAR file when + the update makes it to the last phase where the application has exited and + started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + +update.startup.intervals: + check: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the check phase of the update process + when an update makes it to the last phase where the application has exited + and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + download_bits_partial: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the download phase of the update + process using the BITS downloader for a partial update MAR file when the + update makes it to the last phase where the application has exited and + and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + download_bits_complete: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the download phase of the update + process using the BITS downloader for a complete update MAR file when the + update makes it to the last phase where the application has exited and + started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + download_internal_partial: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the download phase of the update + process using the internal application downloader for a partial update MAR + file when the update makes it to the last phase where the application has + exited and and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + download_internal_complete: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the download phase of the update + process using the internal application downloader for a complete update + MAR file when the update makes it to the last phase where the application + has exited and and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + stage_partial: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the stage phase of the update process + for a partial update MAR file when the update makes it to the last phase + where the application has exited and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + stage_complete: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the stage phase of the update process + for a complete update MAR file when the update makes it to the last phase + where the application has exited and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + apply_partial: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the apply phase of the update process + for a partial update MAR file when the update makes it to the last phase + where the application has exited and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + apply_complete: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the apply phase of the update process + for a complete update MAR file when the update makes it to the last phase + where the application has exited and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + +update.startup.downloads: + bits_partial_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes downloaded using the BITS downloader for + a partial update MAR file when the update makes it to the last phase where + the application has exited and started. This value can be different than + the size of the update MAR file because the value is only monitored + during the initial download while the application is running and not if + the download is resumed. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + bits_partial_seconds: + bug_numbers: + - 1539154 + description: > + Records the total number of seconds spent downloading during the initial + download without exiting the application using the BITS downloader for a + partial update MAR file when the update makes it to the last phase where + the application has exited and started. This value can be used with the + value of update.startup.downloads.bits_partial_bytes to estimate the bytes + per second for the download. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + bits_complete_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes downloaded using the BITS downloader for + a complete update MAR file when the update makes it to the last phase + where the application has exited and started. This value can be different + than the size of the update MAR file because the value is only monitored + during the initial download while the application is running and not if + the download is resumed. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + bits_complete_seconds: + bug_numbers: + - 1539154 + description: > + Records the total number of seconds spent downloading during the initial + download without exiting the application using the BITS downloader for a + complete update MAR file when the update makes it to the last phase where + the application has exited and started. This value can be used with the + value of update.startup.downloads.bits_complete_bytes to estimate the + bytes per second for the download. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + internal_partial_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes downloaded using the internal application + downloader for a partial update MAR file when the update makes it to the + last phase where the application has exited and started. This value can be + different than the size of the update MAR file because the value is only + monitored during the initial download while the application is running and + not if the download is resumed. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + internal_partial_seconds: + bug_numbers: + - 1539154 + description: > + Records the total number of seconds spent downloading during the initial + download without exiting the application using the internal application + downloader for a partial update MAR file when the update makes it to the + last phase where the application has exited and started. This value can be + used with the value of update.startup.downloads.internal_partial_bytes to + estimate the bytes per second for the download. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + internal_complete_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes downloaded by the internal + application downloader for a complete update MAR file when an update makes + it to the last phase where the application has exited and started. This + value can be different than the size of the update MAR file because the + value is only monitored during the initial download while the + application is running and not if the download is resumed. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + internal_complete_seconds: + bug_numbers: + - 1539154 + description: > + Records the total number of seconds spent downloading during the initial + download without exiting the application using the internal application + downloader for a complete update MAR file when the update makes it to the + last phase where the application has exited and started. This value can be + used with the value of update.startup.downloads.internal_complete_bytes to + estimate the bytes per second for the download. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + +update.session: + from_app_version: + bug_numbers: + - 1539154 + description: > + Records the previous application version that the update was applied to + when the update has finished due to a failure before the application has + exited and started. + expires: "72" + kind: string + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - 'main' + + mar_partial_size_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes of a partial update MAR file when + the update has finished due to a failure before the application has exited + and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + mar_complete_size_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes of a complete update MAR file when + the update has finished due to a failure before the application has exited + and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + +update.session.intervals: + check: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the check phase of the update process + when an update has finished due to a failure before the application has + exited and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + download_bits_partial: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the download phase of the update + process using the BITS downloader for a partial update MAR file when the + update has finished due to a failure before the application has exited + and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + download_bits_complete: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the download phase of the update + process using the BITS downloader for a complete update MAR file when the + update has finished due to a failure before the application has exited and + started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + download_internal_partial: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the download phase of the update + process using the internal application downloader for a partial update MAR + file when the update has finished due to a failure before the application + has exited and and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + download_internal_complete: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the download phase of the update + process using the internal application downloader for a complete update + MAR file when the update has finished due to a failure before the + application has exited and and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + stage_partial: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the stage phase of the update process + for a partial update MAR file when the update has finished due to a + failure before the application has exited and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + stage_complete: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the stage phase of the update process + for a complete update MAR file when the update has finished due to a + failure before the application has exited and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + apply_partial: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the apply phase of the update process + for a partial update MAR file when the update has finished due to a + failure before the application has exited and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + apply_complete: + bug_numbers: + - 1539154 + description: > + Records the interval in seconds of the apply phase of the update process + for a complete update MAR file when the update has finished due to a + failure before the application has exited and started. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + +update.session.downloads: + bits_partial_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes downloaded using the BITS downloader for + a partial update MAR file when the update has finished due to a failure + before the application has exited and started. This value can be + different than the size of the update MAR file because the value is only + monitored during the initial download while the application is running and + not if the download is resumed. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + bits_partial_seconds: + bug_numbers: + - 1539154 + description: > + Records the total number of seconds spent downloading during the initial + download without exiting the application using the BITS downloader for a + partial update MAR file when the update has finished due to a failure + before the application has exited and started. This value can be used with + the value of update.session.downloads.bits_partial_bytes to estimate the + bytes per second for the download. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + bits_complete_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes downloaded using the BITS downloader for + a complete update MAR file when the update has finished due to a failure + before the application has exited and started. This value can be different + than the size of the update MAR file because the value is only monitored + during the initial download while the application is running and not if + the download is resumed. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + bits_complete_seconds: + bug_numbers: + - 1539154 + description: > + Records the total number of seconds spent downloading during the initial + download without exiting the application using the BITS downloader for a + complete update MAR file when the update has finished due to a failure + before the application has exited and started. This value can be used with + the value of update.session.downloads.bits_complete_bytes to estimate the + bytes per second for the download. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + operating_systems: + - "windows" + + internal_partial_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes downloaded using the internal + application downloader for a partial update MAR file when the update has + finished due to a failure before the application has exited and started. + This value can be different than the size of the update MAR file because + the value is only monitored during the initial download while the + application is running and not if the download is resumed. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + internal_partial_seconds: + bug_numbers: + - 1539154 + description: > + Records the total number of seconds spent downloading during the initial + download without exiting the application using the internal application + downloader for a partial update MAR file when the update has finished due + to a failure before the application has exited and started. This value can + be used with the value of update.session.downloads.internal_partial_bytes + to estimate the bytes per second for the download. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + internal_complete_bytes: + bug_numbers: + - 1539154 + description: > + Records the total number of bytes downloaded by the internal + application downloader for a complete update MAR file when an update has + finished due to a failure before the application has exited and started. + This value can be different than the size of the update MAR file because + the value is only monitored during the initial download while the + application is running and not if the download is resumed. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + internal_complete_seconds: + bug_numbers: + - 1539154 + description: > + Records the total number of seconds spent downloading during the initial + download without exiting the application using the internal application + downloader for a complete update MAR file when the update has finished due + to a failure before the application has exited and started. This value can + be used with the value of update.session.downloads.internal_complete_bytes + to estimate the bytes per second for the download. + expires: "72" + kind: uint + keyed: false + notification_emails: + - application-update-telemetry-alerts@mozilla.com + - rstrong@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + # The following section contains search counters. browser.search: with_ads: @@ -2848,6 +3614,45 @@ networking: record_in_processes: - 'main' + data_transferred_captive_portal: + bug_numbers: + - 1543005 + description: > + Hom many KB has been transfer over a captive portal during a subsession. + expires: "73" + keyed: false + kind: uint + notification_emails: + - ddamjanovic@mozilla.com + record_in_processes: + - 'main' + + http_transactions_captive_portal: + bug_numbers: + - 1543005 + description: > + Number of http transactions transfer over a captive portal during a subsession. + expires: "73" + keyed: false + kind: uint + notification_emails: + - ddamjanovic@mozilla.com + record_in_processes: + - 'main' + + http_connections_captive_portal: + bug_numbers: + - 1543005 + description: > + Number of http connections transfer over a captive portal during a subsession. + expires: "73" + keyed: false + kind: uint + notification_emails: + - ddamjanovic@mozilla.com + record_in_processes: + - 'main' + # The following section is for probes testing the Telemetry system. They will not be # submitted in pings and are only used for testing. telemetry.test: diff --git a/toolkit/content/tests/chrome/chrome.ini b/toolkit/content/tests/chrome/chrome.ini index d5d287bbfb99..d238e47f6ad6 100644 --- a/toolkit/content/tests/chrome/chrome.ini +++ b/toolkit/content/tests/chrome/chrome.ini @@ -62,8 +62,6 @@ skip-if = (verify && (os == 'win')) [test_autocomplete3.xul] [test_autocomplete4.xul] [test_autocomplete5.xul] -[test_autocomplete_delayOnPaste.xul] -subsuite = clipboard [test_autocomplete_emphasis.xul] [test_autocomplete_with_composition_on_input.html] [test_autocomplete_with_composition_on_textbox.xul] diff --git a/toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul b/toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul deleted file mode 100644 index 3a2a36c7a8a0..000000000000 --- a/toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - -

-

- -
-
- - -
diff --git a/toolkit/content/widgets/autocomplete.xml b/toolkit/content/widgets/autocomplete.xml index b92f9b307cb2..806e57fb3986 100644 --- a/toolkit/content/widgets/autocomplete.xml +++ b/toolkit/content/widgets/autocomplete.xml @@ -45,15 +45,8 @@ this._searchCompleteHandler = this.initEventHandler("searchcomplete"); this._textEnteredHandler = this.initEventHandler("textentered"); this._textRevertedHandler = this.initEventHandler("textreverted"); - - // For security reasons delay searches on pasted values. - this.inputField.controllers.insertControllerAt(0, this._pasteController); ]]> - - null @@ -113,18 +106,8 @@ onget="var m = parseInt(this.getAttribute('minresultsforpopup')); return isNaN(m) ? 1 : m;"/> - - + onset="this.setAttribute('timeout', val); return val;" + onget="var t = parseInt(this.getAttribute('timeout')); return isNaN(t) ? 50 : t;"/> - false - aCommand == "cmd_paste", - doCommand(aCommand) { - this._autocomplete._valueIsPasted = true; - this._autocomplete.editor.paste(this._kGlobalClipboard); - this._autocomplete._valueIsPasted = false; - }, - isCommandEnabled(aCommand) { - return this._autocomplete.editor.isSelectionEditable && - this._autocomplete.editor.canPaste(this._kGlobalClipboard); - }, - onEvent() {}, - }) - ]]> - diff --git a/toolkit/mozapps/update/UpdateService.jsm b/toolkit/mozapps/update/UpdateService.jsm index bf384741bed7..9eb021d27ec3 100644 --- a/toolkit/mozapps/update/UpdateService.jsm +++ b/toolkit/mozapps/update/UpdateService.jsm @@ -233,6 +233,8 @@ var gLogfileWritePromise; // at once. Computers with many users (ex: a school computer), should not end // up with dozens of BITS jobs. var gBITSInUseByAnotherUser = false; +// The start time in milliseconds of the update check. +var gCheckStartMs; XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function aus_gLogEnabled() { return Services.prefs.getBoolPref(PREF_APP_UPDATE_LOG, false) || @@ -1933,8 +1935,16 @@ UpdateService.prototype = { // be resumed the next time the application starts. Downloads using // Windows BITS are not stopped since they don't require Firefox to be // running to perform the download. - if (this._downloader && !this._downloader.usingBits) { - this.stopDownload(); + if (this._downloader) { + if (!this._downloader.usingBits) { + this.stopDownload(); + } else { + // The BITS downloader isn't stopped on exit so the + // active-update.xml needs to be saved for the values sent to + // telemetry to be saved to disk. + Cc["@mozilla.org/updates/update-manager;1"]. + getService(Ci.nsIUpdateManager).saveUpdates(); + } } // Prevent leaking the downloader (bug 454964) this._downloader = null; @@ -2127,6 +2137,10 @@ UpdateService.prototype = { update.errorCode = parseInt(parts[1]); } + if (update.state == STATE_SUCCEEDED || update.patchCount == 1 || + (update.selectedPatch && update.selectedPatch.type == "complete")) { + AUSTLMY.pingUpdatePhases(update, true); + } if (status != STATE_SUCCEEDED) { // Rotate the update logs so the update log isn't removed. By passing @@ -3175,6 +3189,10 @@ UpdateManager.prototype = { if (!update) { return; } + + let patch = update.selectedPatch.QueryInterface(Ci.nsIWritablePropertyBag); + patch.setProperty("stageFinished", Math.ceil(Date.now() / 1000)); + var status = readStatusFile(getUpdatesDir()); pingStateAndStatusCodes(update, false, status); var parts = status.split(":"); @@ -3200,6 +3218,10 @@ UpdateManager.prototype = { writeStatusFile(getUpdatesDir(), update.state = STATE_APPLIED_SERVICE); } + if (update.state == STATE_FAILED) { + AUSTLMY.pingUpdatePhases(update, false); + } + // Now that the active update's properties have been updated write the // active-update.xml to disk. Since there have been no changes to the update // history the updates.xml will not be written to disk. @@ -3223,6 +3245,7 @@ UpdateManager.prototype = { update.state == STATE_PENDING || update.state == STATE_PENDING_SERVICE || update.state == STATE_PENDING_ELEVATE) { + patch.setProperty("applyStart", Math.floor(Date.now() / 1000)); // Notify the user that an update has been staged and is ready for // installation (i.e. that they should restart the application). let prompter = Cc["@mozilla.org/updates/update-prompt;1"]. @@ -3378,6 +3401,7 @@ Checker.prototype = { throw Cr.NS_ERROR_NULL_POINTER; } + gCheckStartMs = Date.now(); let UpdateServiceInstance = UpdateServiceFactory.createInstance(); // |force| can override |canCheckForUpdates| since |force| indicates a // manual update check. But nothing should override enterprise policies. @@ -3650,6 +3674,17 @@ Downloader.prototype = { */ _bitsActiveNotifications: false, + /** + * The start time of the first download attempt in milliseconds for telemetry. + */ + _startDownloadMs: null, + + /** + * The name of the downloader being used to download the update. This is used + * when setting property names on the update patch for telemetry. + */ + _downloaderName: "bits", + /** * Cancels the active download. * @@ -3878,13 +3913,28 @@ Downloader.prototype = { AUSTLMY.pingDownloadCode(undefined, AUSTLMY.DWNLD_ERR_NO_UPDATE_PATCH); return readStatusFile(updateDir); } + // QI the update and the patch to nsIWritablePropertyBag so it isn't + // necessary later in the download code. + this._update.QueryInterface(Ci.nsIWritablePropertyBag); + if (gCheckStartMs && !this._update.getProperty("checkInterval")) { + let interval = Math.max(Math.ceil((Date.now() - gCheckStartMs) / 1000), 1); + this._update.setProperty("checkInterval", interval); + } // this._patch implements nsIWritablePropertyBag. Expose that interface // immediately after a patch is assigned so that this._patch.getProperty // and this._patch.setProperty can always safely be called. this._patch.QueryInterface(Ci.nsIWritablePropertyBag); this.isCompleteUpdate = this._patch.type == "complete"; - if (!this._canUseBits(this._patch)) { + let canUseBits = this._canUseBits(this._patch); + if (!canUseBits) { + this._downloaderName = "internal"; + } + if (!this._patch.getProperty(this._downloaderName + "DownloadStart")) { + this._patch.setProperty(this._downloaderName + "DownloadStart", Math.floor(Date.now() / 1000)); + } + + if (!canUseBits) { let patchFile = getUpdatesDir().clone(); patchFile.append(FILE_UPDATE_MAR); @@ -4108,6 +4158,17 @@ Downloader.prototype = { getService(Ci.nsIUpdateManager).saveUpdates(); } } + // Only record the download bytes per second when there isn't already a + // value for the bytes per second so downloads that are already in progess + // don't have their records overwritten. When the Update Agent is + // implemented this should be reworked so that telemetry receives the bytes + // and seconds it took to complete for the entire update download instead of + // just the sample that is currently recorded. Note: this._patch has already + // been QI'd to nsIWritablePropertyBag. + if (!this._patch.getProperty("internalBytes") && + !this._patch.getProperty("bitsBytes")) { + this._startDownloadMs = Date.now(); + } // Make shallow copy in case listeners remove themselves when called. let listeners = this._listeners.concat(); @@ -4131,6 +4192,11 @@ Downloader.prototype = { onProgress: function Downloader_onProgress(request, context, progress, maxProgress) { LOG("Downloader:onProgress - progress: " + progress + "/" + maxProgress); + if (this._startDownloadMs) { + let seconds = Math.round((Date.now() - this._startDownloadMs) / 1000); + this._patch.setProperty(this._downloaderName + "Seconds", seconds); + this._patch.setProperty(this._downloaderName + "Bytes", progress); + } if (progress > this._patch.size) { LOG("Downloader:onProgress - progress: " + progress + @@ -4253,6 +4319,8 @@ Downloader.prototype = { "current fail: " + this.updateService._consecutiveSocketErrors + ", " + "max fail: " + maxFail + ", " + "retryTimeout: " + retryTimeout); + this._patch.setProperty(this._downloaderName + "DownloadFinished", + Math.floor(Date.now() / 1000)); if (Components.isSuccessCode(status)) { if (this._verifyDownload()) { if (shouldUseService()) { @@ -4456,6 +4524,7 @@ Downloader.prototype = { Services.prefs.setIntPref(PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS, downloadAttempts); let maxAttempts = Math.min(Services.prefs.getIntPref(PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS, 2), 10); + AUSTLMY.pingUpdatePhases(this._update, false); if (downloadAttempts > maxAttempts) { LOG("Downloader:onStopRequest - notifying observers of error. " + "topic: update-error, status: download-attempts-exceeded, " + @@ -4496,6 +4565,7 @@ Downloader.prototype = { LOG("Downloader:onStopRequest - attempting to stage update: " + this._update.name); gUpdateFileWriteInfo = {phase: "stage", failure: false}; + this._patch.setProperty("stageStart", Math.floor(Date.now() / 1000)); // Stage the update try { Cc["@mozilla.org/updates/update-processor;1"]. @@ -4509,6 +4579,8 @@ Downloader.prototype = { shouldShowPrompt = true; } } + } else { + this._patch.setProperty("applyStart", Math.floor(Date.now() / 1000)); } } diff --git a/toolkit/mozapps/update/UpdateTelemetry.jsm b/toolkit/mozapps/update/UpdateTelemetry.jsm index b2ecb695abd7..b6bc9ae6bed0 100644 --- a/toolkit/mozapps/update/UpdateTelemetry.jsm +++ b/toolkit/mozapps/update/UpdateTelemetry.jsm @@ -13,6 +13,14 @@ const {BitsError, BitsUnknownError} = ChromeUtils.import("resource://gre/modules/Bits.jsm"); ChromeUtils.import("resource://gre/modules/Services.jsm", this); +// It is possible for the update.session telemetry to be set more than once +// which must be prevented since they are scalars and setting them more than +// once could lead to values set in the first ping not being present in the +// next ping which would make the values incomprehensible in relation to the +// other values. This isn't needed for update.startup since this will only be +// set once during startup. +var gUpdatePhasesSetForSession = false; + var AUSTLMY = { // Telemetry for the application update background update check occurs when // the background update timer fires after the update interval which is @@ -432,6 +440,111 @@ var AUSTLMY = { } }, + /** + * Submit the update phase telemetry. These are scalars and must only be + * submitted once per sesssion. The update.startup is only submitted once + * once per session due to it only being submitted during startup and only the + * first call to pingUpdatePhases for update.session will be submitted. + * + * @param aUpdate + * The update object which contains the values to submit to telemetry. + * @param aIsStartup + * If true the telemetry will be set under update.startup and if false + * the telemetry will be set under update.session. When false + * subsequent calls will return early and not submit telemetry. + */ + pingUpdatePhases: function UT_pingUpdatePhases(aUpdate, aIsStartup) { + if (!aIsStartup && !Cu.isInAutomation) { + if (gUpdatePhasesSetForSession) { + return; + } + gUpdatePhasesSetForSession = true; + } + let basePrefix = aIsStartup ? "update.startup." : "update.session."; + // None of the calls to getProperty should fail. + try { + let update = aUpdate.QueryInterface(Ci.nsIWritablePropertyBag); + let scalarSet = Services.telemetry.scalarSet; + + // Though it is possible that the previous app version that was updated + // from could change the record is for the app version that initiated the + // update. + scalarSet(basePrefix + "from_app_version", aUpdate.previousAppVersion); + + // The check interval only happens once even if the partial patch fails + // to apply on restart and the complete patch is downloaded. + scalarSet(basePrefix + "intervals.check", + update.getProperty("checkInterval")); + + for (let i = 0; i < aUpdate.patchCount; ++i) { + let patch = + aUpdate.getPatchAt(i).QueryInterface(Ci.nsIWritablePropertyBag); + let type = patch.type; + + scalarSet(basePrefix + "mar_" + type + "_size_bytes", patch.size); + + let prefix = basePrefix + "intervals."; + let internalDownloadStart = patch.getProperty("internalDownloadStart"); + let internalDownloadFinished = + patch.getProperty("internalDownloadFinished"); + if (internalDownloadStart !== null && internalDownloadFinished !== null) { + scalarSet(prefix + "download_internal_" + type, + Math.max((internalDownloadFinished - internalDownloadStart), 1)); + } + + let bitsDownloadStart = patch.getProperty("bitsDownloadStart"); + let bitsDownloadFinished = patch.getProperty("bitsDownloadFinished"); + if (bitsDownloadStart !== null && bitsDownloadFinished !== null) { + scalarSet(prefix + "download_bits_" + type, + Math.max((bitsDownloadFinished - bitsDownloadStart), 1)); + } + + + let stageStart = patch.getProperty("stageStart"); + let stageFinished = patch.getProperty("stageFinished"); + if (stageStart !== null && stageFinished !== null) { + scalarSet(prefix + "stage_" + type, + Math.max((stageFinished - stageStart), 1)); + } + + // Both the partial and the complete patch are recorded for the apply + // interval because it is possible for a partial patch to fail when it + // is applied during a restart and then to try the complete patch. + let applyStart = patch.getProperty("applyStart"); + if (applyStart !== null) { + let applyFinished = Date.now() / 1000; + scalarSet(prefix + "apply_" + type, + Math.max((applyFinished - applyStart), 1)); + } + + prefix = basePrefix + "downloads."; + let internalBytes = patch.getProperty("internalBytes"); + if (internalBytes !== null) { + scalarSet(prefix + "internal_" + type + "_bytes", + Math.max(internalBytes, 1)); + } + let internalSeconds = patch.getProperty("internalSeconds"); + if (internalSeconds !== null) { + scalarSet(prefix + "internal_" + type + "_seconds", + Math.max(internalSeconds, 1)); + } + + let bitsBytes = patch.getProperty("bitsBytes"); + if (bitsBytes !== null) { + scalarSet(prefix + "bits_" + type + "_bytes", + Math.max(bitsBytes, 1)); + } + let bitsSeconds = patch.getProperty("bitsSeconds"); + if (bitsSeconds !== null) { + scalarSet(prefix + "bits_" + type + "_seconds", + Math.max(bitsSeconds, 1)); + } + } + } catch (e) { + Cu.reportError(e); + } + }, + /** * Submit a telemetry ping for the last page displayed by the update wizard. * diff --git a/toolkit/mozapps/update/tests/browser/browser.ini b/toolkit/mozapps/update/tests/browser/browser.ini index e505fef2f6cc..b2e11b89105b 100644 --- a/toolkit/mozapps/update/tests/browser/browser.ini +++ b/toolkit/mozapps/update/tests/browser/browser.ini @@ -74,4 +74,26 @@ reason = test must be able to prevent file deletion. [browser_doorhanger_sp_patch_partialApplyFailure_complete.js] [browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js] [browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js] + +# Telemetry Application Update Tests +[browser_telemetry_completeBadSize.js] +[browser_telemetry_partialBadSize_completeBadSize.js] +[browser_telemetry_complete_stageFailure.js] +skip-if = asan +reason = Bug 1545712 +[browser_telemetry_partial_stageFailure_complete_stageFailure.js] +skip-if = asan +reason = Bug 1545712 +[browser_telemetry_complete_applyFailure.js] +[browser_telemetry_partial_applyFailure_complete_applyFailure.js] +[browser_telemetry_partial_applyFailure_complete_stageFailure.js] +skip-if = asan +reason = Bug 1545712 +[browser_telemetry_partial_applyFailure_complete_applied.js] +[browser_telemetry_partial_applyFailure_complete_staged_applied.js] +[browser_telemetry_partialBadSize_complete_staged_applied.js] +[browser_telemetry_complete_applied.js] +[browser_telemetry_partial_applied.js] +[browser_telemetry_partial_staged_applied.js] +[browser_telemetry_complete_staged_applied.js] [browser_TelemetryUpdatePing.js] diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures.js index 89bfc9790f1a..780bc04f5499 100644 --- a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures.js +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures.js @@ -2,7 +2,6 @@ add_task(async function testDownloadFailures() { const maxBackgroundErrors = 5; SpecialPowers.pushPrefEnv({set: [ [PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors], - [PREF_APP_UPDATE_DOWNLOADPROMPT_MAXATTEMPTS, 2], ]}); let updateParams = "badURL=1"; diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures_bgWin.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures_bgWin.js index 56343511929d..1c7afb336cf6 100644 --- a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures_bgWin.js +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures_bgWin.js @@ -2,7 +2,6 @@ add_task(async function testBackgroundWindowFailures() { const maxBackgroundErrors = 5; SpecialPowers.pushPrefEnv({set: [ [PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors], - [PREF_APP_UPDATE_DOWNLOADPROMPT_MAXATTEMPTS, 2], ]}); let updateParams = "badURL=1"; diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_staged.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_staged.js index 00d0ce6a8b86..31e579b6c816 100644 --- a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_staged.js +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_staged.js @@ -4,7 +4,6 @@ add_task(async function testCompleteAndPartialPatchesWithBadCompleteSize() { ]}); let updateParams = "invalidCompleteSize=1&promptWaitTime=0"; - await runUpdateTest(updateParams, 1, [ { notificationId: "update-restart", diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_completeBadSize.js index 59c8364c2d9c..9d6fc4376e7e 100644 --- a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_completeBadSize.js +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_completeBadSize.js @@ -1,10 +1,5 @@ add_task(async function testCompletePatchWithBadCompleteSize() { - SpecialPowers.pushPrefEnv({set: [ - [PREF_APP_UPDATE_DOWNLOADPROMPT_MAXATTEMPTS, 2], - ]}); - let updateParams = "completePatchOnly=1&invalidCompleteSize=1"; - await runUpdateTest(updateParams, 1, [ { // if we fail maxBackgroundErrors download attempts, then we want to diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize.js index 7b9faecfb3de..cd4703a1cfac 100644 --- a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize.js +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize.js @@ -1,9 +1,5 @@ add_task(async function testPartialPatchWithBadPartialSize() { - SpecialPowers.pushPrefEnv({set: [ - [PREF_APP_UPDATE_DOWNLOADPROMPT_MAXATTEMPTS, 2], - ]}); let updateParams = "partialPatchOnly=1&invalidPartialSize=1"; - await runUpdateTest(updateParams, 1, [ { // if we fail maxBackgroundErrors download attempts, then we want to diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js index 2ac266869d13..9bd673d1ac55 100644 --- a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js @@ -1,9 +1,5 @@ add_task(async function testCompleteAndPartialPatchesWithBadSizes() { - SpecialPowers.pushPrefEnv({set: [ - [PREF_APP_UPDATE_DOWNLOADPROMPT_MAXATTEMPTS, 2], - ]}); let updateParams = "invalidPartialSize=1&invalidCompleteSize=1"; - await runUpdateTest(updateParams, 1, [ { // if we fail maxBackgroundErrors download attempts, then we want to diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js index b7a315e81e3b..c26e78b9b85f 100644 --- a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js +++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js @@ -1,9 +1,11 @@ add_task(async function testPartialPatchApplyFailureWithCompleteValidationFailure() { // because of the way we're simulating failure, we have to just pretend we've already // retried. - SpecialPowers.pushPrefEnv({set: [ - [PREF_APP_UPDATE_DOWNLOADPROMPT_MAXATTEMPTS, 0], - ]}); + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS, 0], + ], + }); let patchProps = {type: "partial", state: STATE_PENDING}; diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_completeBadSize.js new file mode 100644 index 000000000000..afd06f5375d1 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_completeBadSize.js @@ -0,0 +1,24 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.session +// Complete patch only +// Complete patch download failure +add_task(async function telemetry_completeBadSize() { + let updateParams = "&completePatchOnly=1&invalidCompleteSize=1"; + await runTelemetryUpdateTest(updateParams, "update-error"); + + let expected = getTelemetryUpdatePhaseValues({ + forSession: true, + noPartialPatch: true, + completeBadSize: true, + }); + checkTelemetryUpdatePhases(expected); + + testPostUpdateProcessing(); + // Verify that update phase startup telemetry is empty. + checkTelemetryUpdatePhaseEmpty(true); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_applied.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_applied.js new file mode 100644 index 000000000000..b6878d749009 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_applied.js @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.startup +// Complete patch only +// Complete patch download +// Complete patch applied +add_task(async function telemetry_complete_applied() { + let updateParams = "&completePatchOnly=1"; + await runTelemetryUpdateTest(updateParams, "update-downloaded"); + + writeStatusFile(STATE_SUCCEEDED); + testPostUpdateProcessing(); + + let expected = getTelemetryUpdatePhaseValues({ + noPartialPatch: true, + }); + checkTelemetryUpdatePhases(expected); + + // Verify that update phase session telemetry is empty. + checkTelemetryUpdatePhaseEmpty(false); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_applyFailure.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_applyFailure.js new file mode 100644 index 000000000000..142e49a99751 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_applyFailure.js @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.startup +// Complete patch only +// Complete patch download +// Complete patch apply failure +add_task(async function telemetry_complete_applyFailure() { + let updateParams = "&completePatchOnly=1"; + await runTelemetryUpdateTest(updateParams, "update-downloaded"); + + writeStatusFile(STATE_FAILED_CRC_ERROR); + testPostUpdateProcessing(); + + let expected = getTelemetryUpdatePhaseValues({ + noPartialPatch: true, + }); + checkTelemetryUpdatePhases(expected); + + // Verify that update phase session telemetry is empty. + checkTelemetryUpdatePhaseEmpty(false); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_stageFailure.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_stageFailure.js new file mode 100644 index 000000000000..fa4d54528f3e --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_stageFailure.js @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.session +// Complete patch only +// Complete patch download +// Complete patch stage failure +add_task(async function telemetry_complete_stageFailure() { + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_STAGING_ENABLED, true], + ], + }); + + let updateParams = "&completePatchOnly=1"; + await runTelemetryUpdateTest(updateParams, "update-staged", true); + + let expected = getTelemetryUpdatePhaseValues({ + forSession: true, + noPartialPatch: true, + noApplyComplete: true, + }); + checkTelemetryUpdatePhases(expected); + + testPostUpdateProcessing(); + // Verify that update phase startup telemetry is empty. + checkTelemetryUpdatePhaseEmpty(true); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_staged_applied.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_staged_applied.js new file mode 100644 index 000000000000..c91148e28972 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_complete_staged_applied.js @@ -0,0 +1,32 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.startup +// Complete patch only +// Complete patch download +// Complete patch staged +// Complete patch applied +add_task(async function telemetry_complete_staged_applied() { + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_STAGING_ENABLED, true], + ], + }); + + let updateParams = "&completePatchOnly=1"; + await runTelemetryUpdateTest(updateParams, "update-staged"); + + writeStatusFile(STATE_SUCCEEDED); + testPostUpdateProcessing(); + + let expected = getTelemetryUpdatePhaseValues({ + noPartialPatch: true, + }); + checkTelemetryUpdatePhases(expected); + + // Verify that update phase session telemetry is empty. + checkTelemetryUpdatePhaseEmpty(false); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_partialBadSize_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_partialBadSize_completeBadSize.js new file mode 100644 index 000000000000..44161ba65dd5 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_partialBadSize_completeBadSize.js @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.session +// Partial and complete patches +// Partial patch download failure +// Complete patch download failure +add_task(async function telemetry_partialBadSize_completeBadSize() { + let updateParams = "&invalidPartialSize=1&invalidCompleteSize=1"; + await runTelemetryUpdateTest(updateParams, "update-error"); + + let expected = getTelemetryUpdatePhaseValues({ + forSession: true, + partialBadSize: true, + completeBadSize: true, + }); + checkTelemetryUpdatePhases(expected); + + testPostUpdateProcessing(); + // Verify that update phase startup telemetry is empty. + checkTelemetryUpdatePhaseEmpty(true); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_partialBadSize_complete_staged_applied.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_partialBadSize_complete_staged_applied.js new file mode 100644 index 000000000000..f186bdcfd7e5 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_partialBadSize_complete_staged_applied.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.startup +// Partial and complete patches +// Partial patch download failure +// Complete patch download +// Complete patch staged +// Complete patch applied +add_task(async function telemetry_partialBadSize_complete_staged_applied() { + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_STAGING_ENABLED, true], + ], + }); + + let updateParams = "&invalidPartialSize=1"; + await runTelemetryUpdateTest(updateParams, "update-staged"); + + writeStatusFile(STATE_SUCCEEDED); + testPostUpdateProcessing(); + + let expected = getTelemetryUpdatePhaseValues({ + partialBadSize: true, + }); + checkTelemetryUpdatePhases(expected); + + // Verify that update phase session telemetry is empty. + checkTelemetryUpdatePhaseEmpty(false); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applied.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applied.js new file mode 100644 index 000000000000..4d215e2c1b0d --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applied.js @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.startup +// Partial and complete patches +// Partial patch download +// Partial patch applied +add_task(async function telemetry_partial_applied() { + let updateParams = ""; + await runTelemetryUpdateTest(updateParams, "update-downloaded"); + + writeStatusFile(STATE_SUCCEEDED); + testPostUpdateProcessing(); + + let expected = getTelemetryUpdatePhaseValues({ + noInternalComplete: true, + }); + checkTelemetryUpdatePhases(expected); + + // Verify that update phase session telemetry is empty. + checkTelemetryUpdatePhaseEmpty(false); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_applied.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_applied.js new file mode 100644 index 000000000000..0ee1bf4da7f0 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_applied.js @@ -0,0 +1,32 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.startup +// Partial and complete patches +// Partial patch download +// Partial patch apply failure +// Complete patch download +// Complete patch applied +add_task(async function telemetry_sp_partialBadSize_complete_staged_applied() { + let updateParams = ""; + await runTelemetryUpdateTest(updateParams, "update-downloaded"); + + writeStatusFile(STATE_FAILED_CRC_ERROR); + testPostUpdateProcessing(); + // Verify that update phase startup telemetry is empty. + checkTelemetryUpdatePhaseEmpty(true); + + // The download of the complete patch will happen automatically. + await waitForEvent("update-downloaded"); + writeStatusFile(STATE_SUCCEEDED); + testPostUpdateProcessing(); + + let expected = getTelemetryUpdatePhaseValues({}); + checkTelemetryUpdatePhases(expected); + + // Verify that update phase session telemetry is empty. + checkTelemetryUpdatePhaseEmpty(false); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_applyFailure.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_applyFailure.js new file mode 100644 index 000000000000..151bbe1e1f22 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_applyFailure.js @@ -0,0 +1,32 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.startup +// Partial and complete patches +// Partial patch download +// Partial patch apply failure +// Complete patch download +// Complete patch apply failure +add_task(async function telemetry_partial_applyFailure_complete_applyFailure() { + let updateParams = ""; + await runTelemetryUpdateTest(updateParams, "update-downloaded"); + + writeStatusFile(STATE_FAILED_CRC_ERROR); + testPostUpdateProcessing(); + // Verify that update phase startup telemetry is empty. + checkTelemetryUpdatePhaseEmpty(true); + + // The download of the complete patch will happen automatically. + await waitForEvent("update-downloaded"); + writeStatusFile(STATE_FAILED_CRC_ERROR); + testPostUpdateProcessing(); + + let expected = getTelemetryUpdatePhaseValues({}); + checkTelemetryUpdatePhases(expected); + + // Verify that update phase session telemetry is empty. + checkTelemetryUpdatePhaseEmpty(false); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_stageFailure.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_stageFailure.js new file mode 100644 index 000000000000..137da445f7a4 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_stageFailure.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.session +// Partial and complete patches +// Partial patch download +// Partial patch apply failure +// Complete patch download +// Complete patch stage failure +add_task(async function telemetry_partial_applyFailure_complete_stageFailure() { + let updateParams = ""; + await runTelemetryUpdateTest(updateParams, "update-downloaded"); + + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_STAGING_ENABLED, true], + ], + }); + // Now that staging is enabled setup the test updater. + await setupTestUpdater(); + // Remove the update-settings.ini file so staging fails. + removeUpdateSettingsIni(); + // Fail applying the partial. + writeStatusFile(STATE_FAILED_CRC_ERROR); + testPostUpdateProcessing(); + // Verify that update phase startup telemetry wasn't set. + checkTelemetryUpdatePhaseEmpty(true); + + // The download and staging of the complete patch will happen automatically. + await waitForEvent("update-staged"); + + let expected = getTelemetryUpdatePhaseValues({ + forSession: true, + noStagePartial: true, + noApplyComplete: true, + }); + checkTelemetryUpdatePhases(expected); + + testPostUpdateProcessing(); + // Verify that update phase startup telemetry is empty. + checkTelemetryUpdatePhaseEmpty(true); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_staged_applied.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_staged_applied.js new file mode 100644 index 000000000000..b365915075ee --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_applyFailure_complete_staged_applied.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.startup +// Partial and complete patches +// Partial patch download +// Partial patch apply failure +// Complete patch download +// Complete patch staged +// Complete patch applied +add_task(async function telemetry_partial_applyFailure_complete_staged_applied() { + let updateParams = ""; + await runTelemetryUpdateTest(updateParams, "update-downloaded"); + + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_STAGING_ENABLED, true], + ], + }); + // Now that staging is enabled setup the test updater. + await setupTestUpdater(); + // Fail applying the partial. + writeStatusFile(STATE_FAILED_CRC_ERROR); + testPostUpdateProcessing(); + // Verify that update phase startup telemetry is empty. + checkTelemetryUpdatePhaseEmpty(true); + + // The download and staging of the complete will happen automatically. + await waitForEvent("update-staged"); + // Succeed applying the complete patch. + writeStatusFile(STATE_SUCCEEDED); + testPostUpdateProcessing(); + + let expected = getTelemetryUpdatePhaseValues({ + noStagePartial: true, + }); + checkTelemetryUpdatePhases(expected); + + // Verify that update phase session telemetry is empty. + checkTelemetryUpdatePhaseEmpty(false); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_stageFailure_complete_stageFailure.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_stageFailure_complete_stageFailure.js new file mode 100644 index 000000000000..ccbd7dd01099 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_stageFailure_complete_stageFailure.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.session +// Partial and complete patches +// Partial patch download +// Partial patch stage failure +// Complete patch download +// Complete patch stage failure +add_task(async function telemetry_partial_stageFailure_complete_stageFailure() { + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_STAGING_ENABLED, true], + ], + }); + + let updateParams = ""; + await runTelemetryUpdateTest(updateParams, "update-staged", true); + + await waitForEvent("update-staged"); + + let expected = getTelemetryUpdatePhaseValues({ + forSession: true, + noApplyPartial: true, + noApplyComplete: true, + }); + checkTelemetryUpdatePhases(expected); + + testPostUpdateProcessing(); + // Verify that update phase startup telemetry is empty. + checkTelemetryUpdatePhaseEmpty(true); +}); diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_staged_applied.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_staged_applied.js new file mode 100644 index 000000000000..c37f10b063a3 --- /dev/null +++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_partial_staged_applied.js @@ -0,0 +1,32 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Telemetry test for Application Update phases. +// Telemetry update.startup +// Partial and complete patches +// Partial patch download +// Partial patch staged +// Partial patch applied +add_task(async function telemetry_partial_staged_applied() { + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_STAGING_ENABLED, true], + ], + }); + + let updateParams = ""; + await runTelemetryUpdateTest(updateParams, "update-staged"); + + writeStatusFile(STATE_SUCCEEDED); + testPostUpdateProcessing(); + + let expected = getTelemetryUpdatePhaseValues({ + noInternalComplete: true, + }); + checkTelemetryUpdatePhases(expected); + + // Verify that update phase session telemetry is empty. + checkTelemetryUpdatePhaseEmpty(false); +}); diff --git a/toolkit/mozapps/update/tests/browser/head.js b/toolkit/mozapps/update/tests/browser/head.js index 73354d48b5e6..6d4f59178e61 100644 --- a/toolkit/mozapps/update/tests/browser/head.js +++ b/toolkit/mozapps/update/tests/browser/head.js @@ -46,12 +46,16 @@ requestLongerTimeout(10); add_task(async function setupTestCommon() { await SpecialPowers.pushPrefEnv({ set: [ + [PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS, 0], + [PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS, 2], [PREF_APP_UPDATE_LOG, gDebugTest], + [PREF_APP_UPDATE_SERVICE_ENABLED, false], ], }); setUpdateTimerPrefs(); removeUpdateFiles(true); + AppMenuNotifications.removeNotification(/.*/); // Most app update mochitest-browser-chrome tests expect auto update to be // enabled. Those that don't will explicitly change this. await setAppUpdateAutoEnabledHelper(true); @@ -61,6 +65,7 @@ add_task(async function setupTestCommon() { * Common tasks to perform for all tests after each one has finished. */ registerCleanupFunction(async () => { + AppMenuNotifications.removeNotification(/.*/); gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", ""); gEnv.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", ""); UpdateListener.reset(); @@ -120,8 +125,9 @@ async function continueFileHandler(leafName) { } } if (continueFile.exists()) { - throw new Error("The continue file should not exist, path: " + - continueFile.path); + logTestInfo("The continue file should not exist, path: " + + continueFile.path); + continueFile.remove(false); } debugDump("Creating continue file, path: " + continueFile.path); continueFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); @@ -258,7 +264,6 @@ function runUpdateTest(updateParams, checkAttempts, steps) { gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1"); await SpecialPowers.pushPrefEnv({ set: [ - [PREF_APP_UPDATE_DOWNLOADPROMPTATTEMPTS, 0], [PREF_APP_UPDATE_DISABLEDFORTESTING, false], [PREF_APP_UPDATE_IDLETIME, 0], [PREF_APP_UPDATE_URL_MANUAL, URL_MANUAL_UPDATE], @@ -305,7 +310,6 @@ function runUpdateProcessingTest(updates, steps) { gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1"); await SpecialPowers.pushPrefEnv({ set: [ - [PREF_APP_UPDATE_DOWNLOADPROMPTATTEMPTS, 0], [PREF_APP_UPDATE_DISABLEDFORTESTING, false], [PREF_APP_UPDATE_IDLETIME, 0], [PREF_APP_UPDATE_URL_MANUAL, URL_MANUAL_UPDATE], @@ -676,7 +680,6 @@ function runAboutDialogUpdateTest(updateParams, backgroundUpdate, steps) { gEnv.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", "1"); await SpecialPowers.pushPrefEnv({ set: [ - [PREF_APP_UPDATE_SERVICE_ENABLED, false], [PREF_APP_UPDATE_DISABLEDFORTESTING, false], [PREF_APP_UPDATE_URL_MANUAL, detailsURL], ], @@ -805,7 +808,6 @@ function runAboutPrefsUpdateTest(updateParams, backgroundUpdate, steps) { gEnv.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", "1"); await SpecialPowers.pushPrefEnv({ set: [ - [PREF_APP_UPDATE_SERVICE_ENABLED, false], [PREF_APP_UPDATE_DISABLEDFORTESTING, false], [PREF_APP_UPDATE_URL_MANUAL, detailsURL], ], @@ -841,3 +843,274 @@ function runAboutPrefsUpdateTest(updateParams, backgroundUpdate, steps) { } })(); } + + +/** + * Removes the modified update-settings.ini file so the updater will fail to + * stage an update. + */ +function removeUpdateSettingsIni() { + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_STAGING_ENABLED)) { + let greDir = getGREDir(); + let updateSettingsIniBak = greDir.clone(); + updateSettingsIniBak.append(FILE_UPDATE_SETTINGS_INI_BAK); + if (updateSettingsIniBak.exists()) { + let updateSettingsIni = greDir.clone(); + updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI); + updateSettingsIni.remove(false); + } + } +} + +/** + * Runs a telemetry update test. This will set various common prefs for + * updating, checks for an update, and waits for the specified observer + * notification. + * + * @param updateParams + * Params which will be sent to app_update.sjs. + * @param event + * The observer notification to wait for before proceeding. + * @param stageFailure (optional) + * Whether to force a staging failure by removing the modified + * update-settings.ini file. + * @return A promise which will resolve after the . + */ +function runTelemetryUpdateTest(updateParams, event, stageFailure = false) { + // Some elements append a trailing /. After the chrome tests are removed this + // code can be changed so URL_HOST already has a trailing /. + let detailsURL = URL_HOST + "/"; + return (async function() { + Services.telemetry.clearScalars(); + gEnv.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", "1"); + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_APP_UPDATE_DISABLEDFORTESTING, false], + ], + }); + + await setupTestUpdater(); + + if (stageFailure) { + removeUpdateSettingsIni(); + } + + let updateURL = URL_HTTP_UPDATE_SJS + "?detailsURL=" + detailsURL + + updateParams + getVersionParams(); + setUpdateURL(updateURL); + if (Services.prefs.getBoolPref(PREF_APP_UPDATE_STAGING_ENABLED)) { + // Since MOZ_TEST_SKIP_UPDATE_STAGE is checked before + // MOZ_TEST_SLOW_SKIP_UPDATE_STAGE in updater.cpp this removes the need + // for the continue file to continue staging the update. + gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1"); + } + gAUS.checkForBackgroundUpdates(); + await waitForEvent(event); + })(); +} + +/** + * Gets an object with the expected update phase values that can be passed to + * checkTelemetryUpdatePhases for update phase telemetry tests. + * + * @param overrides + * Params which can override the default values. + * @return An object that can be passed to checkTelemetryUpdatePhases for update + * phase telemetry tests. + */ +function getTelemetryUpdatePhaseValues(overrides) { + // Set values that could never be recorded due to values that would prevent + // them from occurring. This makes it so callers only have to specify a couple + // of values. + if (overrides.noPartialPatch) { + if (!overrides.noInternalPartial) { + overrides.noInternalPartial = true; + } + } + + if (overrides.noCompletePatch) { + if (!overrides.noInternalComplete) { + overrides.noInternalComplete = true; + } + } + + if (overrides.noPartialPatch || overrides.partialBadSize || + overrides.noInternalPartial) { + if (!overrides.noStagePartial) { + overrides.noStagePartial = true; + } + if (!overrides.noApplyPartial) { + overrides.noApplyPartial = true; + } + } + + if (overrides.noCompletePatch || overrides.completeBadSize || + overrides.noInternalComplete) { + if (!overrides.noStageComplete) { + overrides.noStageComplete = true; + } + if (!overrides.noApplyComplete) { + overrides.noApplyComplete = true; + } + } + + if (!Services.prefs.getBoolPref(PREF_APP_UPDATE_STAGING_ENABLED)) { + if (!overrides.noStagePartial) { + overrides.noStagePartial = true; + } + if (!overrides.noStageComplete) { + overrides.noStageComplete = true; + } + } + + let marSize = parseInt(SIZE_SIMPLE_MAR); + let partialSize = + overrides.partialBadSize ? parseInt(SIZE_SIMPLE_MAR + "1") : marSize; + let completeSize = + overrides.completeBadSize ? parseInt(SIZE_SIMPLE_MAR + "1") : marSize; + + let partialDownloadBytes = overrides.partialBadSize ? 1 : marSize; + let completeDownloadBytes = overrides.completeBadSize ? 1 : marSize; + + let obj = {}; + obj.basePrefix = + overrides.forSession ? "update.session." : "update.startup."; + obj.from_app_version = Services.appinfo.version; + + obj.mars = {}; + obj.mars.mar_partial_size_bytes = + overrides.noPartialPatch ? null : partialSize; + obj.mars.mar_complete_size_bytes = + overrides.noCompletePatch ? null : completeSize; + + obj.intervals = {}; + obj.intervals.check = 1; + obj.intervals.download_bits_partial = null; + obj.intervals.download_bits_complete = null; + obj.intervals.download_internal_partial = + overrides.noInternalPartial ? null : 1; + obj.intervals.download_internal_complete = + overrides.noInternalComplete ? null : 1; + obj.intervals.stage_partial = overrides.noStagePartial ? null : 1; + obj.intervals.stage_complete = overrides.noStageComplete ? null : 1; + obj.intervals.apply_partial = overrides.noApplyPartial ? null : 1; + obj.intervals.apply_complete = overrides.noApplyComplete ? null : 1; + + obj.downloads = {}; + obj.downloads.bits_partial_ = {}; + obj.downloads.bits_partial_.bytes = null; + obj.downloads.bits_partial_.seconds = null; + obj.downloads.bits_complete_ = {}; + obj.downloads.bits_complete_.bytes = null; + obj.downloads.bits_complete_.seconds = null; + obj.downloads.internal_partial_ = {}; + obj.downloads.internal_partial_.bytes = + overrides.noInternalPartial ? null : partialDownloadBytes; + obj.downloads.internal_partial_.seconds = + overrides.noInternalPartial ? null : 1; + obj.downloads.internal_complete_ = {}; + obj.downloads.internal_complete_.bytes = + overrides.noInternalComplete ? null : completeDownloadBytes; + obj.downloads.internal_complete_.seconds = + overrides.noInternalComplete ? null : 1; + + return obj; +} + +/** + * Checks the telemetry values for app update phases under either update.startup + * or update.session based on the object passed to this function. + * + * @param expected + * An object containing the expected results to compare against the + * actual results. + */ +function checkTelemetryUpdatePhases(expected) { + let scalars = TelemetryTestUtils.getProcessScalars("parent"); + let basePrefix = expected.basePrefix; + let namePrefix = basePrefix; + { + let name = namePrefix + "from_app_version"; + if (expected.from_app_version) { + Assert.ok(!!scalars[name], + "The " + name + " value should exist."); + Assert.equal(scalars[name], + expected.from_app_version, + "The " + name + " value should equal the expected value."); + } else { + Assert.ok(!scalars[name], + "The " + name + " value should not exist."); + } + } + + for (let [nameSuffix, value] of Object.entries(expected.mars)) { + let name = namePrefix + nameSuffix; + if (value) { + Assert.ok(!!scalars[name], + "The " + name + " value should exist."); + Assert.equal(scalars[name], value, + "The " + name + " value should equal the expected value."); + } else { + Assert.ok(!scalars[name], + "The " + name + " value should not exist."); + } + } + + namePrefix = basePrefix + "intervals."; + for (let [suffix, value] of Object.entries(expected.intervals)) { + let name = namePrefix + suffix; + if (value) { + Assert.ok(!!scalars[name], + "The " + name + " value should exist."); + Assert.greaterOrEqual(scalars[name], value, + "The " + name + " value should be equal to or " + + "greater than " + value + "."); + } else { + Assert.ok(!scalars[name], + "The " + name + " value should not exist."); + } + } + + namePrefix = basePrefix + "downloads."; + for (let [nameMid, values] of Object.entries(expected.downloads)) { + let name = namePrefix + nameMid + "bytes"; + if (values.bytes) { + Assert.ok(!!scalars[name], + "The " + name + " value should exist."); + Assert.greaterOrEqual(scalars[name], values.bytes, + "The " + name + " value should be equal to or " + + "greater than " + values.bytes + "."); + } else { + Assert.ok(!scalars[name], + "The " + name + " value should not exist."); + } + + name = namePrefix + nameMid + "seconds"; + if (values.seconds) { + Assert.ok(!!scalars[name], + "The " + name + " value should exist."); + Assert.greaterOrEqual(scalars[name], values.seconds, + "The " + name + " value should be equal to or " + + "greater than " + values.seconds + "."); + } else { + Assert.ok(!scalars[name], + "The " + name + " value should not exist."); + } + } +} + +/** + * Checks whether telemetry for update.startup or update.session is set by + * checking if there is a value for the from_app_version scalar. + * + * @param isStartup + * When true update.startup.from_app_version will be checked and when + * false update.session.from_app_version will be checked. + */ +function checkTelemetryUpdatePhaseEmpty(isStartup) { + let scalars = TelemetryTestUtils.getProcessScalars("parent"); + let name = + "update." + (isStartup ? "startup" : "session") + ".from_app_version"; + Assert.ok(!scalars[name], + "The " + name + " value should not exist."); +} diff --git a/toolkit/mozapps/update/tests/data/shared.js b/toolkit/mozapps/update/tests/data/shared.js index 4e08500b1e3e..bc1f803c358e 100644 --- a/toolkit/mozapps/update/tests/data/shared.js +++ b/toolkit/mozapps/update/tests/data/shared.js @@ -10,36 +10,38 @@ const {FileUtils} = ChromeUtils.import("resource://gre/modules/FileUtils.jsm"); const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +const {TelemetryTestUtils} = + ChromeUtils.import("resource://testing-common/TelemetryTestUtils.jsm"); ChromeUtils.defineModuleGetter(this, "ctypes", "resource://gre/modules/ctypes.jsm"); ChromeUtils.defineModuleGetter(this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm"); -const PREF_APP_UPDATE_AUTO = "app.update.auto"; -const PREF_APP_UPDATE_BACKGROUNDERRORS = "app.update.backgroundErrors"; -const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors"; -const PREF_APP_UPDATE_BITS_ENABLED = "app.update.BITS.enabled"; -const PREF_APP_UPDATE_CANCELATIONS = "app.update.cancelations"; -const PREF_APP_UPDATE_CHANNEL = "app.update.channel"; -const PREF_APP_UPDATE_DOORHANGER = "app.update.doorhanger"; -const PREF_APP_UPDATE_DOWNLOADPROMPTATTEMPTS = "app.update.download.attempts"; -const PREF_APP_UPDATE_DOWNLOADPROMPT_MAXATTEMPTS = "app.update.download.maxAttempts"; -const PREF_APP_UPDATE_DISABLEDFORTESTING = "app.update.disabledForTesting"; -const PREF_APP_UPDATE_IDLETIME = "app.update.idletime"; -const PREF_APP_UPDATE_INTERVAL = "app.update.interval"; -const PREF_APP_UPDATE_LASTUPDATETIME = "app.update.lastUpdateTime.background-update-timer"; -const PREF_APP_UPDATE_LOG = "app.update.log"; -const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported"; -const PREF_APP_UPDATE_PROMPTWAITTIME = "app.update.promptWaitTime"; -const PREF_APP_UPDATE_RETRYTIMEOUT = "app.update.socket.retryTimeout"; -const PREF_APP_UPDATE_SERVICE_ENABLED = "app.update.service.enabled"; -const PREF_APP_UPDATE_SILENT = "app.update.silent"; -const PREF_APP_UPDATE_SOCKET_MAXERRORS = "app.update.socket.maxErrors"; -const PREF_APP_UPDATE_STAGING_ENABLED = "app.update.staging.enabled"; -const PREF_APP_UPDATE_URL = "app.update.url"; -const PREF_APP_UPDATE_URL_DETAILS = "app.update.url.details"; -const PREF_APP_UPDATE_URL_MANUAL = "app.update.url.manual"; +const PREF_APP_UPDATE_AUTO = "app.update.auto"; +const PREF_APP_UPDATE_BACKGROUNDERRORS = "app.update.backgroundErrors"; +const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors"; +const PREF_APP_UPDATE_BITS_ENABLED = "app.update.BITS.enabled"; +const PREF_APP_UPDATE_CANCELATIONS = "app.update.cancelations"; +const PREF_APP_UPDATE_CHANNEL = "app.update.channel"; +const PREF_APP_UPDATE_DOORHANGER = "app.update.doorhanger"; +const PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS = "app.update.download.maxAttempts"; +const PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS = "app.update.download.attempts"; +const PREF_APP_UPDATE_DISABLEDFORTESTING = "app.update.disabledForTesting"; +const PREF_APP_UPDATE_IDLETIME = "app.update.idletime"; +const PREF_APP_UPDATE_INTERVAL = "app.update.interval"; +const PREF_APP_UPDATE_LASTUPDATETIME = "app.update.lastUpdateTime.background-update-timer"; +const PREF_APP_UPDATE_LOG = "app.update.log"; +const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported"; +const PREF_APP_UPDATE_PROMPTWAITTIME = "app.update.promptWaitTime"; +const PREF_APP_UPDATE_RETRYTIMEOUT = "app.update.socket.retryTimeout"; +const PREF_APP_UPDATE_SERVICE_ENABLED = "app.update.service.enabled"; +const PREF_APP_UPDATE_SILENT = "app.update.silent"; +const PREF_APP_UPDATE_SOCKET_MAXERRORS = "app.update.socket.maxErrors"; +const PREF_APP_UPDATE_STAGING_ENABLED = "app.update.staging.enabled"; +const PREF_APP_UPDATE_URL = "app.update.url"; +const PREF_APP_UPDATE_URL_DETAILS = "app.update.url.details"; +const PREF_APP_UPDATE_URL_MANUAL = "app.update.url.manual"; const PREFBRANCH_APP_PARTNER = "app.partner."; const PREF_DISTRIBUTION_ID = "distribution.id";