From 00c42a4376f6044456b8b9213ff79e87602c2276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Thu, 17 Apr 2014 00:01:55 +0200 Subject: [PATCH 01/15] Bug 995321 - nsIDocumentEncoder.encodeToString should offer a way to limit the size of the output, r=smaug. --- content/base/public/nsIDocumentEncoder.idl | 17 ++++- content/base/src/nsDocumentEncoder.cpp | 28 ++++++-- content/base/src/nsPlainTextSerializer.cpp | 3 +- content/base/src/nsXMLContentSerializer.cpp | 13 ++-- content/base/test/mochitest.ini | 1 + .../test_encodeToStringWithMaxLength.html | 64 +++++++++++++++++++ 6 files changed, 115 insertions(+), 11 deletions(-) create mode 100644 content/base/test/test_encodeToStringWithMaxLength.html diff --git a/content/base/public/nsIDocumentEncoder.idl b/content/base/public/nsIDocumentEncoder.idl index b6a60d25eb1..b5963bb2db8 100644 --- a/content/base/public/nsIDocumentEncoder.idl +++ b/content/base/public/nsIDocumentEncoder.idl @@ -35,7 +35,7 @@ interface nsIDocumentEncoderNodeFixup : nsISupports nsIDOMNode fixupNode(in nsIDOMNode aNode, out boolean aSerializeCloneKids); }; -[scriptable, uuid(7222bdf1-c2b9-41f1-a40a-a3d65283a95b)] +[scriptable, uuid(1158bd7e-a08b-4ff6-9417-6f99144cfccc)] interface nsIDocumentEncoder : nsISupports { // Output methods flag bits. There are a frightening number of these, @@ -329,6 +329,21 @@ interface nsIDocumentEncoder : nsISupports AString encodeToStringWithContext( out AString aContextString, out AString aInfoString); + /** + * Encode the document into a string of limited size. + * @param aMaxLength After aMaxLength characters, the encoder will stop + * encoding new data. + * Only values > 0 will be considered. + * The returned string may be slightly larger than + * aMaxLength because some serializers (eg. HTML) + * may need to close some tags after they stop + * encoding new data, or finish a line (72 columns + * by default for the plain text serializer). + * + * @return The document encoded into a string. + */ + AString encodeToStringWithMaxLength(in unsigned long aMaxLength); + /** * Set the fixup object associated with node persistence. * @param aFixup The fixup object. diff --git a/content/base/src/nsDocumentEncoder.cpp b/content/base/src/nsDocumentEncoder.cpp index b6f334bf3fe..dd1c9fa173c 100644 --- a/content/base/src/nsDocumentEncoder.cpp +++ b/content/base/src/nsDocumentEncoder.cpp @@ -81,7 +81,8 @@ protected: nsINode* aOriginalNode = nullptr); nsresult SerializeToStringRecursive(nsINode* aNode, nsAString& aStr, - bool aDontSerializeRoot); + bool aDontSerializeRoot, + uint32_t aMaxLength = 0); nsresult SerializeNodeEnd(nsINode* aNode, nsAString& aStr); // This serializes the content of aNode. nsresult SerializeToStringIterative(nsINode* aNode, @@ -450,8 +451,13 @@ nsDocumentEncoder::SerializeNodeEnd(nsINode* aNode, nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode, nsAString& aStr, - bool aDontSerializeRoot) + bool aDontSerializeRoot, + uint32_t aMaxLength) { + if (aMaxLength > 0 && aStr.Length() >= aMaxLength) { + return NS_OK; + } + if (!IsVisibleNode(aNode)) return NS_OK; @@ -487,7 +493,12 @@ nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode, } if (!aDontSerializeRoot) { - rv = SerializeNodeStart(maybeFixedNode, 0, -1, aStr, aNode); + int32_t endOffset = -1; + if (aMaxLength > 0) { + MOZ_ASSERT(aMaxLength >= aStr.Length()); + endOffset = aMaxLength - aStr.Length(); + } + rv = SerializeNodeStart(maybeFixedNode, 0, endOffset, aStr, aNode); NS_ENSURE_SUCCESS(rv, rv); } @@ -496,7 +507,7 @@ nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode, for (nsINode* child = nsNodeUtils::GetFirstChildOfTemplateOrNode(node); child; child = child->GetNextSibling()) { - rv = SerializeToStringRecursive(child, aStr, false); + rv = SerializeToStringRecursive(child, aStr, false, aMaxLength); NS_ENSURE_SUCCESS(rv, rv); } @@ -1016,6 +1027,13 @@ nsDocumentEncoder::SerializeRangeToString(nsRange *aRange, NS_IMETHODIMP nsDocumentEncoder::EncodeToString(nsAString& aOutputString) +{ + return EncodeToStringWithMaxLength(0, aOutputString); +} + +NS_IMETHODIMP +nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength, + nsAString& aOutputString) { if (!mDocument) return NS_ERROR_NOT_INITIALIZED; @@ -1146,7 +1164,7 @@ nsDocumentEncoder::EncodeToString(nsAString& aOutputString) rv = mSerializer->AppendDocumentStart(mDocument, output); if (NS_SUCCEEDED(rv)) { - rv = SerializeToStringRecursive(mDocument, output, false); + rv = SerializeToStringRecursive(mDocument, output, false, aMaxLength); } } diff --git a/content/base/src/nsPlainTextSerializer.cpp b/content/base/src/nsPlainTextSerializer.cpp index 24d40c9e602..8736ac5a54b 100644 --- a/content/base/src/nsPlainTextSerializer.cpp +++ b/content/base/src/nsPlainTextSerializer.cpp @@ -278,7 +278,8 @@ nsPlainTextSerializer::AppendText(nsIContent* aText, return NS_ERROR_FAILURE; } - int32_t endoffset = (aEndOffset == -1) ? frag->GetLength() : aEndOffset; + int32_t fragLength = frag->GetLength(); + int32_t endoffset = (aEndOffset == -1) ? fragLength : std::min(aEndOffset, fragLength); NS_ASSERTION(aStartOffset <= endoffset, "A start offset is beyond the end of the text fragment!"); int32_t length = endoffset - aStartOffset; diff --git a/content/base/src/nsXMLContentSerializer.cpp b/content/base/src/nsXMLContentSerializer.cpp index 293ee12e682..0ecf61aa4a9 100644 --- a/content/base/src/nsXMLContentSerializer.cpp +++ b/content/base/src/nsXMLContentSerializer.cpp @@ -138,7 +138,8 @@ nsXMLContentSerializer::AppendTextData(nsIContent* aNode, return NS_ERROR_FAILURE; } - int32_t endoffset = (aEndOffset == -1) ? frag->GetLength() : aEndOffset; + int32_t fragLength = frag->GetLength(); + int32_t endoffset = (aEndOffset == -1) ? fragLength : std::min(aEndOffset, fragLength); int32_t length = endoffset - aStartOffset; NS_ASSERTION(aStartOffset >= 0, "Negative start offset for text fragment!"); @@ -300,12 +301,16 @@ nsXMLContentSerializer::AppendComment(nsIContent* aComment, rv = comment->GetData(data); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; - if (aStartOffset || (aEndOffset != -1)) { - int32_t length = (aEndOffset == -1) ? data.Length() : aEndOffset; + int32_t dataLength = data.Length(); + if (aStartOffset || (aEndOffset != -1 && aEndOffset < dataLength)) { + int32_t length = + (aEndOffset == -1) ? dataLength : std::min(aEndOffset, dataLength); length -= aStartOffset; nsAutoString frag; - data.Mid(frag, aStartOffset, length); + if (length > 0) { + data.Mid(frag, aStartOffset, length); + } data.Assign(frag); } diff --git a/content/base/test/mochitest.ini b/content/base/test/mochitest.ini index 8e1e2fb6277..c792277dcc1 100644 --- a/content/base/test/mochitest.ini +++ b/content/base/test/mochitest.ini @@ -559,6 +559,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 904183 # b2g(bug 904183 [test_domparser_null_char.html] [test_domparsing.html] [test_elementTraversal.html] +[test_encodeToStringWithMaxLength.html] [test_fileapi.html] skip-if = e10s [test_fileapi_slice.html] diff --git a/content/base/test/test_encodeToStringWithMaxLength.html b/content/base/test/test_encodeToStringWithMaxLength.html new file mode 100644 index 00000000000..587aff77b59 --- /dev/null +++ b/content/base/test/test_encodeToStringWithMaxLength.html @@ -0,0 +1,64 @@ + + + + + + Test for Bug 995321 - encodeToStringWithMaxLength + + + + + +Mozilla Bug 995321 +

+ +
+
+ + From 6e082922a8709517a9d468ac51873ad1e7abc723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Thu, 17 Apr 2014 00:02:33 +0200 Subject: [PATCH 02/15] Bug 971048 - Run language detection on webpages and display infobar when language is not the current UI locale, r=felipe. --- browser/app/profile/firefox.js | 2 + browser/base/content/browser.js | 13 ++++ browser/base/content/content.js | 48 +++++++++++++ .../components/translation/Translation.jsm | 70 ++++++++++--------- .../test/browser_translation_infobar.js | 70 ++++++++++--------- .../translation/translation-infobar.xml | 6 +- 6 files changed, 142 insertions(+), 67 deletions(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 215725139fa..dad3ff99668 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1425,6 +1425,8 @@ pref("browser.cache.auto_delete_cache_version", 1); // -1 means no experiment is run and we use the preferred value for frecency (6h) pref("browser.cache.frecency_experiment", 0); +pref("browser.translation.detectLanguage", false); + // Telemetry experiments settings. pref("experiments.enabled", false); pref("experiments.manifest.fetchIntervalSeconds", 86400); diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 21a1e0b336b..916a103d239 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -147,6 +147,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "gCustomizationTabPreloader", XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Translation", + "resource:///modules/translation/Translation.jsm"); + XPCOMUtils.defineLazyModuleGetter(this, "SitePermissions", "resource:///modules/SitePermissions.jsm"); @@ -1033,6 +1036,7 @@ var gBrowserInit = { gFormSubmitObserver.init(); gRemoteTabsUI.init(); gPageStyleMenu.init(); + LanguageDetectionListener.init(); // Initialize the full zoom setting. // We do this before the session restore service gets initialized so we can @@ -5342,6 +5346,15 @@ function setStyleDisabled(disabled) { } +var LanguageDetectionListener = { + init: function() { + window.messageManager.addMessageListener("LanguageDetection:Result", msg => { + Translation.languageDetected(msg.target, msg.data); + }); + } +}; + + var BrowserOffline = { _inited: false, diff --git a/browser/base/content/content.js b/browser/base/content/content.js index 91376b83f24..b38aff3f3a6 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -12,6 +12,8 @@ Cu.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ContentLinkHandler", "resource:///modules/ContentLinkHandler.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector", + "resource:///modules/translation/LanguageDetector.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent", "resource://gre/modules/LoginManagerContent.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "InsecurePasswordUtils", @@ -387,3 +389,49 @@ let PageStyleHandler = { }, }; PageStyleHandler.init(); + +let TranslationHandler = { + init: function() { + let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebProgress); + webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); + }, + + /* nsIWebProgressListener implementation */ + onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) { + if (!aWebProgress.isTopLevel || + !(aStateFlags & Ci.nsIWebProgressListener.STATE_STOP)) + return; + + let url = aRequest.name; + if (!url.startsWith("http://") && !url.startsWith("https://")) + return; + + // Grab a 60k sample of text from the page. + let encoder = Cc["@mozilla.org/layout/documentEncoder;1?type=text/plain"] + .createInstance(Ci.nsIDocumentEncoder); + encoder.init(content.document, "text/plain", encoder.SkipInvisibleContent); + let string = encoder.encodeToStringWithMaxLength(60 * 1024); + + // Language detection isn't reliable on very short strings. + if (string.length < 100) + return; + + LanguageDetector.detectLanguage(string).then(result => { + if (result.confident) + sendAsyncMessage("LanguageDetection:Result", result.language); + }); + }, + + // Unused methods. + onProgressChange: function() {}, + onLocationChange: function() {}, + onStatusChange: function() {}, + onSecurityChange: function() {}, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, + Ci.nsISupportsWeakReference]) +}; + +if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) + TranslationHandler.init(); diff --git a/browser/components/translation/Translation.jsm b/browser/components/translation/Translation.jsm index 6459291b45c..2cf0ae5862a 100644 --- a/browser/components/translation/Translation.jsm +++ b/browser/components/translation/Translation.jsm @@ -11,42 +11,11 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/Promise.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector", - "resource:///modules/translation/LanguageDetector.jsm"); -/* Create an object keeping the information related to translation for - * a specific browser. This object is passed to the translation - * infobar so that it can initialize itself. The properties exposed to - * the infobar are: - * - supportedSourceLanguages, array of supported source language codes - * - supportedTargetLanguages, array of supported target language codes - * - detectedLanguage, code of the language detected on the web page. - * - defaultTargetLanguage, code of the language to use by default for - * translation. - * - state, the state in which the infobar should be displayed - * - STATE_{OFFER,TRANSLATING,TRANSLATED,ERROR} constants. - * - translatedFrom, if already translated, source language code. - * - translatedTo, if already translated, target language code. - * - translate, method starting the translation of the current page. - * - showOriginalContent, method showing the original page content. - * - showTranslatedContent, method showing the translation for an - * already translated page whose original content is shown. - * - originalShown, boolean indicating if the original or translated - * version of the page is shown. - */ -this.Translation = function(aBrowser) { - this.browser = aBrowser; -}; - -this.Translation.prototype = { +this.Translation = { supportedSourceLanguages: ["en", "zh", "ja", "es", "de", "fr", "ru", "ar", "ko", "pt"], supportedTargetLanguages: ["en", "pl", "tr", "vi"], - STATE_OFFER: 0, - STATE_TRANSLATING: 1, - STATE_TRANSLATED: 2, - STATE_ERROR: 3, - _defaultTargetLanguage: "", get defaultTargetLanguage() { if (!this._defaultTargetLanguage) { @@ -58,6 +27,43 @@ this.Translation.prototype = { return this._defaultTargetLanguage; }, + languageDetected: function(aBrowser, aDetectedLanguage) { + if (this.supportedSourceLanguages.indexOf(aDetectedLanguage) != -1 && + aDetectedLanguage != this.defaultTargetLanguage) { + if (!aBrowser.translationUI) + aBrowser.translationUI = new TranslationUI(aBrowser); + + aBrowser.translationUI.showTranslationUI(aDetectedLanguage); + } + } +}; + +/* TranslationUI objects keep the information related to translation for + * a specific browser. This object is passed to the translation + * infobar so that it can initialize itself. The properties exposed to + * the infobar are: + * - detectedLanguage, code of the language detected on the web page. + * - state, the state in which the infobar should be displayed + * - STATE_{OFFER,TRANSLATING,TRANSLATED,ERROR} constants. + * - translatedFrom, if already translated, source language code. + * - translatedTo, if already translated, target language code. + * - translate, method starting the translation of the current page. + * - showOriginalContent, method showing the original page content. + * - showTranslatedContent, method showing the translation for an + * already translated page whose original content is shown. + * - originalShown, boolean indicating if the original or translated + * version of the page is shown. + */ +function TranslationUI(aBrowser) { + this.browser = aBrowser; +} + +TranslationUI.prototype = { + STATE_OFFER: 0, + STATE_TRANSLATING: 1, + STATE_TRANSLATED: 2, + STATE_ERROR: 3, + get doc() this.browser.contentDocument, translate: function(aFrom, aTo) { diff --git a/browser/components/translation/test/browser_translation_infobar.js b/browser/components/translation/test/browser_translation_infobar.js index e7ab67ae0bf..c68ea34fdd8 100644 --- a/browser/components/translation/test/browser_translation_infobar.js +++ b/browser/components/translation/test/browser_translation_infobar.js @@ -29,8 +29,6 @@ function waitForCondition(condition, nextTest, errorMsg) { } var TranslationStub = { - __proto__: Translation.prototype, - translate: function(aFrom, aTo) { this.state = this.STATE_TRANSLATING; this.translatedFrom = aFrom; @@ -54,6 +52,14 @@ var TranslationStub = { } }; +function showTranslationUI(aDetectedLanguage) { + let browser = gBrowser.selectedBrowser; + Translation.languageDetected(browser, aDetectedLanguage); + let ui = browser.translationUI; + for (let name of ["translate", "_reset", "failTranslation", "finishTranslation"]) + ui[name] = TranslationStub[name]; + return ui.notificationBox.getNotificationWithValue("translation"); +} function test() { waitForExplicitFinish(); @@ -83,35 +89,35 @@ function checkURLBarIcon(aExpectTranslated = false) { function run_tests(aFinishCallback) { info("Show an info bar saying the current page is in French"); - let notif = TranslationStub.showTranslationUI("fr"); - is(notif.state, TranslationStub.STATE_OFFER, "the infobar is offering translation"); + let notif = showTranslationUI("fr"); + is(notif.state, notif.translation.STATE_OFFER, "the infobar is offering translation"); is(notif._getAnonElt("detectedLanguage").value, "fr", "The detected language is displayed"); checkURLBarIcon(); info("Click the 'Translate' button"); notif._getAnonElt("translate").click(); - is(notif.state, TranslationStub.STATE_TRANSLATING, "the infobar is in the translating state"); - ok(!!TranslationStub.translatedFrom, "Translation.translate has been called"); - is(TranslationStub.translatedFrom, "fr", "from language correct"); - is(TranslationStub.translatedTo, TranslationStub.defaultTargetLanguage, "from language correct"); + is(notif.state, notif.translation.STATE_TRANSLATING, "the infobar is in the translating state"); + ok(!!notif.translation.translatedFrom, "Translation.translate has been called"); + is(notif.translation.translatedFrom, "fr", "from language correct"); + is(notif.translation.translatedTo, Translation.defaultTargetLanguage, "from language correct"); checkURLBarIcon(); info("Make the translation fail and check we are in the error state."); - TranslationStub.failTranslation(); - is(notif.state, TranslationStub.STATE_ERROR, "infobar in the error state"); + notif.translation.failTranslation(); + is(notif.state, notif.translation.STATE_ERROR, "infobar in the error state"); checkURLBarIcon(); info("Click the try again button"); notif._getAnonElt("tryAgain").click(); - is(notif.state, TranslationStub.STATE_TRANSLATING, "infobar in the translating state"); - ok(!!TranslationStub.translatedFrom, "Translation.translate has been called"); - is(TranslationStub.translatedFrom, "fr", "from language correct"); - is(TranslationStub.translatedTo, TranslationStub.defaultTargetLanguage, "from language correct"); + is(notif.state, notif.translation.STATE_TRANSLATING, "infobar in the translating state"); + ok(!!notif.translation.translatedFrom, "Translation.translate has been called"); + is(notif.translation.translatedFrom, "fr", "from language correct"); + is(notif.translation.translatedTo, Translation.defaultTargetLanguage, "from language correct"); checkURLBarIcon(); info("Make the translation succeed and check we are in the 'translated' state."); - TranslationStub.finishTranslation(); - is(notif.state, TranslationStub.STATE_TRANSLATED, "infobar in the translated state"); + notif.translation.finishTranslation(); + is(notif.state, notif.translation.STATE_TRANSLATED, "infobar in the translated state"); checkURLBarIcon(true); info("Test 'Show original' / 'Show Translation' buttons."); @@ -137,45 +143,45 @@ function run_tests(aFinishCallback) { let from = notif._getAnonElt("fromLanguage"); from.value = "es"; from.doCommand(); - is(notif.state, TranslationStub.STATE_TRANSLATING, "infobar in the translating state"); - ok(!!TranslationStub.translatedFrom, "Translation.translate has been called"); - is(TranslationStub.translatedFrom, "es", "from language correct"); - is(TranslationStub.translatedTo, TranslationStub.defaultTargetLanguage, "to language correct"); + is(notif.state, notif.translation.STATE_TRANSLATING, "infobar in the translating state"); + ok(!!notif.translation.translatedFrom, "Translation.translate has been called"); + is(notif.translation.translatedFrom, "es", "from language correct"); + is(notif.translation.translatedTo, Translation.defaultTargetLanguage, "to language correct"); // We want to show the 'translated' icon while re-translating, // because we are still displaying the previous translation. checkURLBarIcon(true); - TranslationStub.finishTranslation(); + notif.translation.finishTranslation(); checkURLBarIcon(true); info("Check that changing the target language causes a re-translation"); let to = notif._getAnonElt("toLanguage"); to.value = "pl"; to.doCommand(); - is(notif.state, TranslationStub.STATE_TRANSLATING, "infobar in the translating state"); - ok(!!TranslationStub.translatedFrom, "Translation.translate has been called"); - is(TranslationStub.translatedFrom, "es", "from language correct"); - is(TranslationStub.translatedTo, "pl", "to language correct"); + is(notif.state, notif.translation.STATE_TRANSLATING, "infobar in the translating state"); + ok(!!notif.translation.translatedFrom, "Translation.translate has been called"); + is(notif.translation.translatedFrom, "es", "from language correct"); + is(notif.translation.translatedTo, "pl", "to language correct"); checkURLBarIcon(true); - TranslationStub.finishTranslation(); + notif.translation.finishTranslation(); checkURLBarIcon(true); // Cleanup. notif.close(); info("Reopen the info bar to check that it's possible to override the detected language."); - notif = TranslationStub.showTranslationUI("fr"); - is(notif.state, TranslationStub.STATE_OFFER, "the infobar is offering translation"); + notif = showTranslationUI("fr"); + is(notif.state, notif.translation.STATE_OFFER, "the infobar is offering translation"); is(notif._getAnonElt("detectedLanguage").value, "fr", "The detected language is displayed"); // Change the language and click 'Translate' notif._getAnonElt("detectedLanguage").value = "ja"; notif._getAnonElt("translate").click(); - is(notif.state, TranslationStub.STATE_TRANSLATING, "the infobar is in the translating state"); - ok(!!TranslationStub.translatedFrom, "Translation.translate has been called"); - is(TranslationStub.translatedFrom, "ja", "from language correct"); + is(notif.state, notif.translation.STATE_TRANSLATING, "the infobar is in the translating state"); + ok(!!notif.translation.translatedFrom, "Translation.translate has been called"); + is(notif.translation.translatedFrom, "ja", "from language correct"); notif.close(); info("Reopen to check the 'Not Now' button closes the notification."); - notif = TranslationStub.showTranslationUI("fr"); + notif = showTranslationUI("fr"); let notificationBox = gBrowser.getNotificationBox(); ok(!!notificationBox.getNotificationWithValue("translation"), "there's a 'translate' notification"); notif._getAnonElt("notNow").click(); diff --git a/browser/components/translation/translation-infobar.xml b/browser/components/translation/translation-infobar.xml index 053856198d8..4728fca127b 100644 --- a/browser/components/translation/translation-infobar.xml +++ b/browser/components/translation/translation-infobar.xml @@ -102,7 +102,7 @@ // Fill the lists of supported source languages. let detectedLanguage = this._getAnonElt("detectedLanguage"); let fromLanguage = this._getAnonElt("fromLanguage"); - for (let code of this.translation.supportedSourceLanguages) { + for (let code of Translation.supportedSourceLanguages) { let name = bundle.GetStringFromName(code); detectedLanguage.appendItem(name, code); fromLanguage.appendItem(name, code); @@ -115,7 +115,7 @@ // Fill the list of supporter target languages. let toLanguage = this._getAnonElt("toLanguage"); - for (let code of this.translation.supportedTargetLanguages) + for (let code of Translation.supportedTargetLanguages) toLanguage.appendItem(bundle.GetStringFromName(code), code); if (aTranslation.translatedTo) @@ -143,7 +143,7 @@ this._getAnonElt("fromLanguage").value = this._getAnonElt("detectedLanguage").value; this._getAnonElt("toLanguage").value = - this.translation.defaultTargetLanguage; + Translation.defaultTargetLanguage; } this._handleButtonHiding(false); From 901c53e937646d6dfe943c8fdfd5dca293cb2c72 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Tue, 15 Apr 2014 12:49:38 -0700 Subject: [PATCH 03/15] Bug 997406 - Default to Link context menu items in context menus. r=bnicholson --- .../tests/testPictureLinkContextMenu.java | 10 +++---- mobile/android/chrome/content/browser.js | 30 +++++++++++++++---- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/mobile/android/base/tests/testPictureLinkContextMenu.java b/mobile/android/base/tests/testPictureLinkContextMenu.java index 3ac5f4d4a5d..3848c226bec 100644 --- a/mobile/android/base/tests/testPictureLinkContextMenu.java +++ b/mobile/android/base/tests/testPictureLinkContextMenu.java @@ -10,7 +10,7 @@ public class testPictureLinkContextMenu extends ContentContextMenuTest { private static final String tabs [] = { "Image", "Link" }; private static final String photoMenuItems [] = { "Copy Image Location", "Share Image", "Set Image As", "Save Image" }; private static final String linkMenuItems [] = { "Open Link in New Tab", "Open Link in Private Tab", "Copy Link", "Share Link", "Bookmark Link"}; - private static final String linkTitle = "^Link$"; + private static final String imageTitle = "^Image$"; public void testPictureLinkContextMenu() { blockForGeckoReady(); @@ -20,21 +20,19 @@ public class testPictureLinkContextMenu extends ContentContextMenuTest { loadAndPaint(PICTURE_PAGE_URL); verifyPageTitle(PICTURE_PAGE_TITLE); + switchTabs(imageTitle); verifyContextMenuItems(photoMenuItems); verifyTabs(tabs); + switchTabs(imageTitle); verifyCopyOption(photoMenuItems[0], "Firefox.jpg"); // Test the "Copy Image Location" option + switchTabs(imageTitle); verifyShareOption(photoMenuItems[1], PICTURE_PAGE_TITLE); // Test the "Share Image" option - switchTabs(linkTitle); verifyContextMenuItems(linkMenuItems); openTabFromContextMenu(linkMenuItems[0],2); // Test the "Open in New Tab" option - expecting 2 tabs: the original and the new one - switchTabs(linkTitle); openTabFromContextMenu(linkMenuItems[1],2); // Test the "Open in Private Tab" option - expecting only 2 tabs in normal mode - switchTabs(linkTitle); verifyCopyOption(linkMenuItems[2], BLANK_PAGE_URL); // Test the "Copy Link" option - switchTabs(linkTitle); verifyShareOption(linkMenuItems[3], PICTURE_PAGE_TITLE); // Test the "Share Link" option - switchTabs(linkTitle); verifyBookmarkLinkOption(linkMenuItems[4],BLANK_PAGE_URL); // Test the "Bookmark Link" option } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 2cd5ac5fe26..8b6f7cc4cc0 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2102,6 +2102,11 @@ var NativeWindow = { else this._targetRef = null; }, + get defaultContext() { + delete this.defaultContext; + return this.defaultContext = Strings.browser.GetStringFromName("browser.menu.context.default"); + }, + /* Gets menuitems for an arbitrary node * Parameters: * element - The element to look at. If this element has a contextmenu attribute, the @@ -2183,7 +2188,7 @@ var NativeWindow = { } catch(ex) { } // Fallback to the default - return Strings.browser.GetStringFromName("browser.menu.context.default"); + return this.defaultContext; }, // Adds context menu items added through the add-on api @@ -2338,7 +2343,8 @@ var NativeWindow = { */ _reformatList: function(target) { let contexts = Object.keys(this.menus); - if (contexts.length == 1) { + + if (contexts.length === 1) { // If there's only one context, we'll only show a single flat single select list return this._reformatMenuItems(target, this.menus[contexts[0]]); } @@ -2357,12 +2363,24 @@ var NativeWindow = { */ _reformatListAsTabs: function(target, menus) { let itemArray = []; - for (let context in menus) { + + // Sort the keys so that "link" is always first + let contexts = Object.keys(this.menus); + contexts.sort((context1, context2) => { + if (context1 === this.defaultContext) { + return -1; + } else if (context2 === this.defaultContext) { + return 1; + } + return 0; + }); + + contexts.forEach(context => { itemArray.push({ label: context, items: this._reformatMenuItems(target, menus[context]) }); - } + }); return itemArray; }, @@ -8386,8 +8404,10 @@ HTMLContextMenuItem.prototype = Object.create(ContextMenuItem.prototype, { } var items = NativeWindow.contextmenus._getHTMLContextMenuItemsForMenu(elt, target); + // This menu will always only have one context, but we still make sure its the "right" one. + var context = NativeWindow.contextmenus._getContextType(target); if (items.length > 0) { - NativeWindow.contextmenus._addMenuItems(items, "link"); + NativeWindow.contextmenus._addMenuItems(items, context); } } catch(ex) { From 4634032ecdf08bcc8cb862103f953188d4e4c427 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 10 Apr 2014 13:18:19 -0700 Subject: [PATCH 04/15] Bug 989137 - Part 12: Ability to ignore hashes of downloaded experiments; r=gfritzsche We normally validate hashes of XPIs as part of installing experiments. While a useful security feature, this patch makes that optional. This in turn makes automated and manual testing easier. --HG-- extra : rebase_source : 292653411391843fad3f62d0bee01e11779313b6 --- browser/experiments/Experiments.jsm | 9 +++++++-- .../test/xpcshell/test_activate.js | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/browser/experiments/Experiments.jsm b/browser/experiments/Experiments.jsm index 0591853a17d..45de651b912 100644 --- a/browser/experiments/Experiments.jsm +++ b/browser/experiments/Experiments.jsm @@ -252,6 +252,10 @@ Experiments.Policy = function () { this._log = Log.repository.getLoggerWithMessagePrefix( "Browser.Experiments.Policy", "Policy #" + gPolicyCounter++ + "::"); + + // Set to true to ignore hash verification on downloaded XPIs. This should + // not be used outside of testing. + this.ignoreHashes = false; }; Experiments.Policy.prototype = { @@ -1498,8 +1502,9 @@ Experiments.ExperimentEntry.prototype = { _installAddon: function* () { let deferred = Promise.defer(); - let install = yield addonInstallForURL(this._manifestData.xpiURL, - this._manifestData.xpiHash); + let hash = this._policy.ignoreHashes ? null : this._manifestData.xpiHash; + + let install = yield addonInstallForURL(this._manifestData.xpiURL, hash); gActiveInstallURLs.add(install.sourceURI.spec); let failureHandler = (install, handler) => { diff --git a/browser/experiments/test/xpcshell/test_activate.js b/browser/experiments/test/xpcshell/test_activate.js index 9e10d3f8cf5..f185344c445 100644 --- a/browser/experiments/test/xpcshell/test_activate.js +++ b/browser/experiments/test/xpcshell/test_activate.js @@ -140,4 +140,23 @@ add_task(function* test_startStop() { Assert.equal(experiment.enabled, false, "Experiment should be disabled."); addons = yield getExperimentAddons(); Assert.equal(addons.length, 0, "Experiment add-on is uninstalled."); + + // Ensure hash validation works. + // We set an incorrect hash and expect the install to fail. + experiment._manifestData.xpiHash = "sha1:41014dcc66b4dcedcd973491a1530a32f0517d8a"; + let errored = false; + try { + yield experiment.start(); + } catch (ex) { + errored = true; + } + Assert.ok(experiment._failedStart, "Experiment failed to start."); + Assert.ok(errored, "start() threw an exception."); + + // Make sure "ignore hashes" mode works. + gPolicy.ignoreHashes = true; + let changes = yield experiment.start(); + Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL); + yield experiment.stop(); + gPolicy.ignoreHashes = false; }); From a792704f5f21ee8d088283f3f1e75e4426e4680c Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 10 Apr 2014 13:27:36 -0700 Subject: [PATCH 05/15] Bug 989137 - Part 13: Rename functions for associating with Add-on Manager; r=gfritzsche The previous experiment feature expands the scope of these functions. A rename was in order. --HG-- extra : rebase_source : 054d7a440e3e15436ff7fbcbc4f91b28dd4fc9fd --- browser/experiments/Experiments.jsm | 10 ++++++---- browser/experiments/test/xpcshell/test_api.js | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/browser/experiments/Experiments.jsm b/browser/experiments/Experiments.jsm index 45de651b912..0fd903604ef 100644 --- a/browser/experiments/Experiments.jsm +++ b/browser/experiments/Experiments.jsm @@ -376,7 +376,7 @@ Experiments.Experiments.prototype = { AsyncShutdown.profileBeforeChange.addBlocker("Experiments.jsm shutdown", this.uninit.bind(this)); - this._startWatchingAddons(); + this._registerWithAddonManager(); this._loadTask = Task.spawn(this._loadFromCache.bind(this)); this._loadTask.then( @@ -406,7 +406,7 @@ Experiments.Experiments.prototype = { yield this._loadTask; if (!this._shutdown) { - this._stopWatchingAddons(); + this._unregisterWithAddonManager(); gPrefs.ignore(PREF_LOGGING, configureLogging); gPrefs.ignore(PREF_MANIFEST_URI, this.updateManifest, this); @@ -427,12 +427,14 @@ Experiments.Experiments.prototype = { this._log.info("Completed uninitialization."); }), - _startWatchingAddons: function () { + _registerWithAddonManager: function () { + this._log.trace("Registering instance with Addon Manager."); + AddonManager.addAddonListener(this); AddonManager.addInstallListener(this); }, - _stopWatchingAddons: function () { + _unregisterWithAddonManager: function () { AddonManager.removeInstallListener(this); AddonManager.removeAddonListener(this); }, diff --git a/browser/experiments/test/xpcshell/test_api.js b/browser/experiments/test/xpcshell/test_api.js index 7998827cf33..62f061838da 100644 --- a/browser/experiments/test/xpcshell/test_api.js +++ b/browser/experiments/test/xpcshell/test_api.js @@ -1366,9 +1366,9 @@ add_task(function* testUnknownExperimentsUninstalled() { Assert.equal(addons.length, 0, "Precondition: No experiment add-ons are present."); // Simulate us not listening. - experiments._stopWatchingAddons(); + experiments._unregisterWithAddonManager(); yield AddonTestUtils.installXPIFromURL(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1); - experiments._startWatchingAddons(); + experiments._registerWithAddonManager(); addons = yield getExperimentAddons(); Assert.equal(addons.length, 1, "Experiment 1 installed via AddonManager"); @@ -1452,9 +1452,9 @@ add_task(function* testEnabledAfterRestart() { Assert.ok(addons[0].isActive, "That experiment is active."); dump("Restarting Addon Manager\n"); - experiments._stopWatchingAddons(); + experiments._unregisterWithAddonManager(); restartManager(); - experiments._startWatchingAddons(); + experiments._registerWithAddonManager(); addons = yield getExperimentAddons(); Assert.equal(addons.length, 1, "The experiment is still there after restart."); From eb1e150c73c501129e6393d3a80a763d8d505433 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 10 Apr 2014 13:29:02 -0700 Subject: [PATCH 06/15] Bug 989137 - Part 14: Return a promise from Experiments.init(); r=gfritzsche Upcoming tests will need to wait on init() to finish before running. We now return a promise so these tests don't have to look at internal variables. --HG-- extra : rebase_source : 487645efe419fb3fa3371f85a81e8f3e1ccd9012 extra : amend_source : 3d6bb785e0ecdabcbff1e5bc62ee7dc56cbb7244 extra : histedit_source : 8d12f7e5c47995a408509a85a527bc86f9a76ccb --- browser/experiments/Experiments.jsm | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/browser/experiments/Experiments.jsm b/browser/experiments/Experiments.jsm index 0fd903604ef..ec0f0809f38 100644 --- a/browser/experiments/Experiments.jsm +++ b/browser/experiments/Experiments.jsm @@ -378,17 +378,22 @@ Experiments.Experiments.prototype = { this._registerWithAddonManager(); - this._loadTask = Task.spawn(this._loadFromCache.bind(this)); + let deferred = Promise.defer(); + + this._loadTask = this._loadFromCache(); this._loadTask.then( () => { this._log.trace("_loadTask finished ok"); this._loadTask = null; - this._run(); + this._run().then(deferred.resolve, deferred.reject); }, (e) => { this._log.error("_loadFromCache caught error: " + e); + deferred.reject(e); } ); + + return deferred.promise; }, /** @@ -818,7 +823,7 @@ Experiments.Experiments.prototype = { /* * Task function, load the cached experiments manifest file from disk. */ - _loadFromCache: function*() { + _loadFromCache: Task.async(function* () { this._log.trace("_loadFromCache"); let path = this._cacheFilePath; try { @@ -828,7 +833,7 @@ Experiments.Experiments.prototype = { // No cached manifest yet. this._experiments = new Map(); } - }, + }), _populateFromCache: function (data) { this._log.trace("populateFromCache() - data: " + JSON.stringify(data)); From 88da3e35c914dba1082ee16dcb716f80757e58ba Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 10 Apr 2014 14:30:40 -0700 Subject: [PATCH 07/15] Bug 989137 - Part 15: Convert browser_experiments.js to add_task; r=Unfocused Upcoming tests rely heavily on promises. I just learned that browser chrome tests support add_task() as an alternative to test(). This patch ports the browser_experiments.js test and utilized utility functions to use add_task() and promises. --HG-- extra : rebase_source : 434b7dc0e899fef0c508d5dd01438514da519ba3 extra : histedit_source : 517043f2bbe7a22bd53e8ae0c77f585ac107f6c9 --- .../test/browser/browser_experiments.js | 225 ++++++++---------- .../mozapps/extensions/test/browser/head.js | 46 +++- 2 files changed, 141 insertions(+), 130 deletions(-) diff --git a/toolkit/mozapps/extensions/test/browser/browser_experiments.js b/toolkit/mozapps/extensions/test/browser/browser_experiments.js index 730b52de777..79f63ee07df 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_experiments.js +++ b/toolkit/mozapps/extensions/test/browser/browser_experiments.js @@ -7,169 +7,150 @@ let gCategoryUtilities; let gInstalledAddons = []; let gContext = this; -function test() { - waitForExplicitFinish(); +add_task(function* initializeState() { + gManagerWindow = yield open_manager(); + gCategoryUtilities = new CategoryUtilities(gManagerWindow); - open_manager(null, (win) => { - gManagerWindow = win; - gCategoryUtilities = new CategoryUtilities(win); + // The Experiments Manager will interfere with us by preventing installs + // of experiments it doesn't know about. We remove it from the equation + // because here we are only concerned with core Addon Manager operation, + // not the superset Experiments Manager has imposed. + if ("@mozilla.org/browser/experiments-service;1" in Components.classes) { + Components.utils.import("resource:///modules/experiments/Experiments.jsm", gContext); - // The Experiments Manager will interfere with us by preventing installs - // of experiments it doesn't know about. We remove it from the equation - // because here we are only concerned with core Addon Manager operation, - // not the superset Experiments Manager has imposed. - if ("@mozilla.org/browser/experiments-service;1" in Components.classes) { - Components.utils.import("resource:///modules/experiments/Experiments.jsm", gContext); - - // There is a race condition between XPCOM service initialization and - // this test running. We have to initialize the instance first, then - // uninitialize it to prevent this. - let instance = gContext.Experiments.instance(); - instance.uninit().then(run_next_test); - } else { - run_next_test(); - } - }); -} - -function end_test() { - for (let addon of gInstalledAddons) { - addon.uninstall(); + // There is a race condition between XPCOM service initialization and + // this test running. We have to initialize the instance first, then + // uninitialize it to prevent this. + let instance = gContext.Experiments.instance(); + yield instance.uninit(); } - - close_manager(gManagerWindow, () => { - if ("@mozilla.org/browser/experiments-service;1" in Components.classes) { - gContext.Experiments.instance().init(); - finish(); - } else { - finish(); - } - }); -} +}); // On an empty profile with no experiments, the experiment category // should be hidden. -add_test(function testInitialState() { +add_task(function* testInitialState() { Assert.ok(gCategoryUtilities.get("experiment", false), "Experiment tab is defined."); Assert.ok(!gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab hidden by default."); - - run_next_test(); }); -add_test(function testExperimentInfoNotVisible() { - gCategoryUtilities.openType("extension", () => { - let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0]; - is_element_hidden(el, "Experiment info not visible on other types."); - - run_next_test(); - }); +add_task(function* testExperimentInfoNotVisible() { + yield gCategoryUtilities.openType("extension"); + let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0]; + is_element_hidden(el, "Experiment info not visible on other types."); }); // If we have an active experiment, we should see the experiments tab // and that tab should have some messages. -add_test(function testActiveExperiment() { - install_addon("addons/browser_experiment1.xpi", (addon) => { - gInstalledAddons.push(addon); +add_task(function* testActiveExperiment() { + let addon = yield install_addon("addons/browser_experiment1.xpi"); + gInstalledAddons.push(addon); - Assert.ok(addon.userDisabled, "Add-on is disabled upon initial install."); - Assert.equal(addon.isActive, false, "Add-on is not active."); + Assert.ok(addon.userDisabled, "Add-on is disabled upon initial install."); + Assert.equal(addon.isActive, false, "Add-on is not active."); - Assert.ok(gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab visible."); + Assert.ok(gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab visible."); - gCategoryUtilities.openType("experiment", (win) => { - let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0]; - is_element_visible(el, "Experiment info is visible on experiment tab."); - - run_next_test(); - }); - }); + yield gCategoryUtilities.openType("experiment"); + let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0]; + is_element_visible(el, "Experiment info is visible on experiment tab."); }); -add_test(function testExperimentLearnMore() { +add_task(function* testExperimentLearnMore() { // Actual URL is irrelevant. Services.prefs.setCharPref("toolkit.telemetry.infoURL", "http://mochi.test:8888/server.js"); - gCategoryUtilities.openType("experiment", (win) => { - let btn = gManagerWindow.document.getElementById("experiments-learn-more"); + yield gCategoryUtilities.openType("experiment"); + let btn = gManagerWindow.document.getElementById("experiments-learn-more"); - if (!gUseInContentUI) { - is_element_hidden(btn, "Learn more button hidden if not using in-content UI."); - Services.prefs.clearUserPref("toolkit.telemetry.infoURL"); + if (!gUseInContentUI) { + is_element_hidden(btn, "Learn more button hidden if not using in-content UI."); + Services.prefs.clearUserPref("toolkit.telemetry.infoURL"); - run_next_test(); - return; - } else { - is_element_visible(btn, "Learn more button visible."); - } + return; + } - window.addEventListener("DOMContentLoaded", function onLoad(event) { - info("Telemetry privacy policy window opened."); - window.removeEventListener("DOMContentLoaded", onLoad, false); + is_element_visible(btn, "Learn more button visible."); - let browser = gBrowser.selectedTab.linkedBrowser; - let expected = Services.prefs.getCharPref("toolkit.telemetry.infoURL"); - Assert.equal(browser.currentURI.spec, expected, "New tab should have loaded privacy policy."); - browser.contentWindow.close(); + let deferred = Promise.defer(); + window.addEventListener("DOMContentLoaded", function onLoad(event) { + info("Telemetry privacy policy window opened."); + window.removeEventListener("DOMContentLoaded", onLoad, false); - Services.prefs.clearUserPref("toolkit.telemetry.infoURL"); + let browser = gBrowser.selectedTab.linkedBrowser; + let expected = Services.prefs.getCharPref("toolkit.telemetry.infoURL"); + Assert.equal(browser.currentURI.spec, expected, "New tab should have loaded privacy policy."); + browser.contentWindow.close(); - run_next_test(); - }, false); + Services.prefs.clearUserPref("toolkit.telemetry.infoURL"); - info("Opening telemetry privacy policy."); - EventUtils.synthesizeMouseAtCenter(btn, {}, gManagerWindow); - }); + deferred.resolve(); + }, false); + + info("Opening telemetry privacy policy."); + EventUtils.synthesizeMouseAtCenter(btn, {}, gManagerWindow); + + yield deferred.promise; }); -add_test(function testOpenPreferences() { - gCategoryUtilities.openType("experiment", (win) => { - let btn = gManagerWindow.document.getElementById("experiments-change-telemetry"); - if (!gUseInContentUI) { - is_element_hidden(btn, "Change telemetry button not enabled in out of window UI."); - info("Skipping preferences open test because not using in-content UI."); - run_next_test(); - return; - } +add_task(function* testOpenPreferences() { + yield gCategoryUtilities.openType("experiment"); + let btn = gManagerWindow.document.getElementById("experiments-change-telemetry"); + if (!gUseInContentUI) { + is_element_hidden(btn, "Change telemetry button not enabled in out of window UI."); + info("Skipping preferences open test because not using in-content UI."); + return; + } - is_element_visible(btn, "Change telemetry button visible in in-content UI."); + is_element_visible(btn, "Change telemetry button visible in in-content UI."); - Services.obs.addObserver(function observer(prefWin, topic, data) { - Services.obs.removeObserver(observer, "advanced-pane-loaded"); + let deferred = Promise.defer(); + Services.obs.addObserver(function observer(prefWin, topic, data) { + Services.obs.removeObserver(observer, "advanced-pane-loaded"); - info("Advanced preference pane opened."); + info("Advanced preference pane opened."); - // We want this test to fail if the preferences pane changes. - let el = prefWin.document.getElementById("dataChoicesPanel"); - is_element_visible(el); + // We want this test to fail if the preferences pane changes. + let el = prefWin.document.getElementById("dataChoicesPanel"); + is_element_visible(el); - prefWin.close(); - info("Closed preferences pane."); + prefWin.close(); + info("Closed preferences pane."); - run_next_test(); - }, "advanced-pane-loaded", false); + deferred.resolve(); + }, "advanced-pane-loaded", false); - info("Loading preferences pane."); - EventUtils.synthesizeMouseAtCenter(btn, {}, gManagerWindow); - }); + info("Loading preferences pane."); + EventUtils.synthesizeMouseAtCenter(btn, {}, gManagerWindow); + + yield deferred.promise; }); -add_test(function testButtonPresence() { - gCategoryUtilities.openType("experiment", (win) => { - let item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org"); - Assert.ok(item, "Got add-on element."); +add_task(function* testButtonPresence() { + yield gCategoryUtilities.openType("experiment"); + let item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org"); + Assert.ok(item, "Got add-on element."); - let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); - // Corresponds to the uninstall permission. - is_element_visible(el, "Remove button is visible."); - // Corresponds to lack of disable permission. - el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn"); - is_element_hidden(el, "Disable button not visible."); - // Corresponds to lack of enable permission. - el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn"); - is_element_hidden(el, "Enable button not visible."); - - run_next_test(); - }); + let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + // Corresponds to the uninstall permission. + is_element_visible(el, "Remove button is visible."); + // Corresponds to lack of disable permission. + el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn"); + is_element_hidden(el, "Disable button not visible."); + // Corresponds to lack of enable permission. + el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn"); + is_element_hidden(el, "Enable button not visible."); +}); + +add_task(function* testCleanup() { + for (let addon of gInstalledAddons) { + addon.uninstall(); + } + + yield close_manager(gManagerWindow); + + if ("@mozilla.org/browser/experiments-service;1" in Components.classes) { + yield gContext.Experiments.instance().init(); + } }); diff --git a/toolkit/mozapps/extensions/test/browser/head.js b/toolkit/mozapps/extensions/test/browser/head.js index 94a773992ea..c046f8029b6 100644 --- a/toolkit/mozapps/extensions/test/browser/head.js +++ b/toolkit/mozapps/extensions/test/browser/head.js @@ -283,6 +283,8 @@ function wait_for_manager_load(aManagerWindow, aCallback) { } function open_manager(aView, aCallback, aLoadCallback, aLongerTimeout) { + let deferred = Promise.defer(); + function setup_manager(aManagerWindow) { if (aLoadCallback) log_exceptions(aLoadCallback, aManagerWindow); @@ -299,7 +301,10 @@ function open_manager(aView, aCallback, aLoadCallback, aLongerTimeout) { // Some functions like synthesizeMouse don't like to be called during // the load event so ensure that has completed executeSoon(function() { - log_exceptions(aCallback, aManagerWindow); + if (aCallback) { + log_exceptions(aCallback, aManagerWindow); + } + deferred.resolve(aManagerWindow); }); }, null, aLongerTimeout); }); @@ -309,7 +314,7 @@ function open_manager(aView, aCallback, aLoadCallback, aLongerTimeout) { if (gUseInContentUI) { gBrowser.selectedTab = gBrowser.addTab(); switchToTabHavingURI(MANAGER_URI, true); - + // This must be a new load, else the ping/pong would have // found the window above. Services.obs.addObserver(function (aSubject, aTopic, aData) { @@ -318,7 +323,7 @@ function open_manager(aView, aCallback, aLoadCallback, aLongerTimeout) { return; setup_manager(aSubject); }, "EM-loaded", false); - return; + return deferred.promise; } openDialog(MANAGER_URI); @@ -326,9 +331,12 @@ function open_manager(aView, aCallback, aLoadCallback, aLongerTimeout) { Services.obs.removeObserver(arguments.callee, aTopic); setup_manager(aSubject); }, "EM-loaded", false); + + return deferred.promise; } function close_manager(aManagerWindow, aCallback, aLongerTimeout) { + let deferred = Promise.defer(); requestLongerTimeout(aLongerTimeout ? aLongerTimeout : 2); ok(aManagerWindow != null, "Should have an add-ons manager window to close"); @@ -336,10 +344,15 @@ function close_manager(aManagerWindow, aCallback, aLongerTimeout) { aManagerWindow.addEventListener("unload", function() { this.removeEventListener("unload", arguments.callee, false); - log_exceptions(aCallback); + if (aCallback) { + log_exceptions(aCallback); + } + deferred.resolve(); }, false); aManagerWindow.close(); + + return deferred.promise; } function restart_manager(aManagerWindow, aView, aCallback, aLoadCallback) { @@ -424,17 +437,25 @@ function is_element_hidden(aElement, aMsg) { * The callback will receive the Addon for the installed add-on. */ function install_addon(path, cb, pathPrefix=TESTROOT) { + let deferred = Promise.defer(); + AddonManager.getInstallForURL(pathPrefix + path, (install) => { install.addListener({ onInstallEnded: () => { executeSoon(() => { - cb(install.addon); + if (cb) { + cb(install.addon); + } + + deferred.resolve(install.addon); }); }, }); install.install(); }, "application/x-xpinstall"); + + return deferred.promise; } function CategoryUtilities(aManagerWindow) { @@ -496,17 +517,26 @@ CategoryUtilities.prototype = { }, open: function(aCategory, aCallback) { + let deferred = Promise.defer(); + isnot(this.window, null, "Should not open category when manager window is not loaded"); ok(this.isVisible(aCategory), "Category should be visible if attempting to open it"); EventUtils.synthesizeMouse(aCategory, 2, 2, { }, this.window); - if (aCallback) - wait_for_view_load(this.window, aCallback); + wait_for_view_load(this.window, (win) => { + if (aCallback) { + log_exceptions(aCallback, win); + } + + deferred.resolve(win); + }); + + return deferred.promise; }, openType: function(aCategoryType, aCallback) { - this.open(this.get(aCategoryType), aCallback); + return this.open(this.get(aCategoryType), aCallback); } } From ab076913da2a82596abc8725aac043ea7ec03c16 Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Wed, 16 Apr 2014 19:47:32 -0400 Subject: [PATCH 08/15] Backout 47845138fae1 (bug 996148) because the solution from bug 997227 is better. r=backout. --- browser/base/content/browser.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 916a103d239..d02ba688552 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -4468,9 +4468,6 @@ var TabsInTitlebar = { let titlebar = $("titlebar"); let titlebarContent = $("titlebar-content"); let menubar = $("toolbar-menubar"); -#ifdef XP_MACOSX - let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width; -#endif if (allowed) { // We set the tabsintitlebar attribute first so that our CSS for @@ -4488,6 +4485,7 @@ var TabsInTitlebar = { let captionButtonsBoxWidth = rect($("titlebar-buttonbox-container")).width; #ifdef XP_MACOSX + let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width; // No need to look up the menubar stuff on OS X: let menuHeight = 0; let fullMenuHeight = 0; @@ -4562,6 +4560,9 @@ var TabsInTitlebar = { // Finally, size the placeholders: +#ifdef XP_MACOSX + this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth); +#endif this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth); if (!this._draghandles) { @@ -4589,10 +4590,6 @@ var TabsInTitlebar = { titlebar.style.marginBottom = ""; menubar.style.paddingBottom = ""; } - -#ifdef XP_MACOSX - this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth); -#endif }, _sizePlaceholder: function (type, width) { From d5f90edd83232e72b17cdee19cad132551ee5a2c Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Wed, 16 Apr 2014 19:48:01 -0400 Subject: [PATCH 09/15] Bug 997227 - Entering and exiting tabview makes items in tabs toolbar collide with fullscreen button. r=Gijs, MattN. --- browser/base/content/browser.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index d02ba688552..03909e33c55 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -4584,6 +4584,10 @@ var TabsInTitlebar = { document.documentElement.removeAttribute("tabsintitlebar"); updateTitlebarDisplay(); +#ifdef XP_MACOSX + let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width; + this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth); +#endif // Reset the margins and padding that might have been modified: titlebarContent.style.marginTop = ""; titlebarContent.style.marginBottom = ""; From c84abce3848b33b6e4cdb20afa8a6cc8b7b7763f Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 17 Apr 2014 11:25:44 +1000 Subject: [PATCH 10/15] Bug 985868 - only report prolonged sync errors once per application invocation. r=rnewman --- services/sync/modules/policies.js | 15 +++- services/sync/tests/unit/test_errorhandler.js | 79 ++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/services/sync/modules/policies.js b/services/sync/modules/policies.js index 6dfaf40242d..97a478bd67c 100644 --- a/services/sync/modules/policies.js +++ b/services/sync/modules/policies.js @@ -502,6 +502,13 @@ ErrorHandler.prototype = { */ dontIgnoreErrors: false, + /** + * Flag that indicates if we have already reported a prolonged failure. + * Once set, we don't report it again, meaning this error is only reported + * one per run. + */ + didReportProlongedError: false, + init: function init() { Svc.Obs.add("weave:engine:sync:applied", this); Svc.Obs.add("weave:engine:sync:error", this); @@ -772,7 +779,13 @@ ErrorHandler.prototype = { if (lastSync && ((Date.now() - Date.parse(lastSync)) > Svc.Prefs.get("errorhandler.networkFailureReportTimeout") * 1000)) { Status.sync = PROLONGED_SYNC_FAILURE; - this._log.trace("shouldReportError: true (prolonged sync failure)."); + if (this.didReportProlongedError) { + this._log.trace("shouldReportError: false (prolonged sync failure, but" + + " we've already reported it)."); + return false; + } + this._log.trace("shouldReportError: true (first prolonged sync failure)."); + this.didReportProlongedError = true; return true; } diff --git a/services/sync/tests/unit/test_errorhandler.js b/services/sync/tests/unit/test_errorhandler.js index 4e0906c2317..34e3e6d1baa 100644 --- a/services/sync/tests/unit/test_errorhandler.js +++ b/services/sync/tests/unit/test_errorhandler.js @@ -150,6 +150,7 @@ function clean() { Service.startOver(); Status.resetSync(); Status.resetBackoff(); + errorHandler.didReportProlongedError = false; } add_identity_test(this, function test_401_logout() { @@ -297,18 +298,32 @@ add_identity_test(this, function test_shouldReportError() { do_check_true(errorHandler.shouldReportError()); // Test non-network, prolonged, login error reported + do_check_false(errorHandler.didReportProlongedError); Status.resetSync(); setLastSync(PROLONGED_ERROR_DURATION); errorHandler.dontIgnoreErrors = false; Status.login = LOGIN_FAILED_NO_PASSWORD; do_check_true(errorHandler.shouldReportError()); + do_check_true(errorHandler.didReportProlongedError); + + // Second time with prolonged error and without resetting + // didReportProlongedError, sync error should not be reported. + Status.resetSync(); + setLastSync(PROLONGED_ERROR_DURATION); + errorHandler.dontIgnoreErrors = false; + Status.login = LOGIN_FAILED_NO_PASSWORD; + do_check_false(errorHandler.shouldReportError()); + do_check_true(errorHandler.didReportProlongedError); // Test non-network, prolonged, sync error reported Status.resetSync(); setLastSync(PROLONGED_ERROR_DURATION); errorHandler.dontIgnoreErrors = false; + errorHandler.didReportProlongedError = false; Status.sync = CREDENTIALS_CHANGED; do_check_true(errorHandler.shouldReportError()); + do_check_true(errorHandler.didReportProlongedError); + errorHandler.didReportProlongedError = false; // Test network, prolonged, login error reported Status.resetSync(); @@ -316,6 +331,8 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = false; Status.login = LOGIN_FAILED_NETWORK_ERROR; do_check_true(errorHandler.shouldReportError()); + do_check_true(errorHandler.didReportProlongedError); + errorHandler.didReportProlongedError = false; // Test network, prolonged, sync error reported Status.resetSync(); @@ -323,6 +340,8 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = false; Status.sync = LOGIN_FAILED_NETWORK_ERROR; do_check_true(errorHandler.shouldReportError()); + do_check_true(errorHandler.didReportProlongedError); + errorHandler.didReportProlongedError = false; // Test non-network, non-prolonged, login error reported Status.resetSync(); @@ -330,6 +349,7 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = false; Status.login = LOGIN_FAILED_NO_PASSWORD; do_check_true(errorHandler.shouldReportError()); + do_check_false(errorHandler.didReportProlongedError); // Test non-network, non-prolonged, sync error reported Status.resetSync(); @@ -337,6 +357,7 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = false; Status.sync = CREDENTIALS_CHANGED; do_check_true(errorHandler.shouldReportError()); + do_check_false(errorHandler.didReportProlongedError); // Test network, non-prolonged, login error reported Status.resetSync(); @@ -344,6 +365,7 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = false; Status.login = LOGIN_FAILED_NETWORK_ERROR; do_check_false(errorHandler.shouldReportError()); + do_check_false(errorHandler.didReportProlongedError); // Test network, non-prolonged, sync error reported Status.resetSync(); @@ -351,6 +373,7 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = false; Status.sync = LOGIN_FAILED_NETWORK_ERROR; do_check_false(errorHandler.shouldReportError()); + do_check_false(errorHandler.didReportProlongedError); // Test server maintenance, sync errors are not reported Status.resetSync(); @@ -358,6 +381,7 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = false; Status.sync = SERVER_MAINTENANCE; do_check_false(errorHandler.shouldReportError()); + do_check_false(errorHandler.didReportProlongedError); // Test server maintenance, login errors are not reported Status.resetSync(); @@ -365,6 +389,7 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = false; Status.login = SERVER_MAINTENANCE; do_check_false(errorHandler.shouldReportError()); + do_check_false(errorHandler.didReportProlongedError); // Test prolonged, server maintenance, sync errors are reported Status.resetSync(); @@ -372,6 +397,8 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = false; Status.sync = SERVER_MAINTENANCE; do_check_true(errorHandler.shouldReportError()); + do_check_true(errorHandler.didReportProlongedError); + errorHandler.didReportProlongedError = false; // Test prolonged, server maintenance, login errors are reported Status.resetSync(); @@ -379,6 +406,8 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = false; Status.login = SERVER_MAINTENANCE; do_check_true(errorHandler.shouldReportError()); + do_check_true(errorHandler.didReportProlongedError); + errorHandler.didReportProlongedError = false; // Test dontIgnoreErrors, server maintenance, sync errors are reported Status.resetSync(); @@ -386,6 +415,8 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = true; Status.sync = SERVER_MAINTENANCE; do_check_true(errorHandler.shouldReportError()); + // dontIgnoreErrors means we don't set didReportProlongedError + do_check_false(errorHandler.didReportProlongedError); // Test dontIgnoreErrors, server maintenance, login errors are reported Status.resetSync(); @@ -393,6 +424,7 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = true; Status.login = SERVER_MAINTENANCE; do_check_true(errorHandler.shouldReportError()); + do_check_false(errorHandler.didReportProlongedError); // Test dontIgnoreErrors, prolonged, server maintenance, // sync errors are reported @@ -401,6 +433,7 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = true; Status.sync = SERVER_MAINTENANCE; do_check_true(errorHandler.shouldReportError()); + do_check_false(errorHandler.didReportProlongedError); // Test dontIgnoreErrors, prolonged, server maintenance, // login errors are reported @@ -409,7 +442,7 @@ add_identity_test(this, function test_shouldReportError() { errorHandler.dontIgnoreErrors = true; Status.login = SERVER_MAINTENANCE; do_check_true(errorHandler.shouldReportError()); - + do_check_false(errorHandler.didReportProlongedError); }); add_identity_test(this, function test_shouldReportError_master_password() { @@ -625,6 +658,7 @@ add_task(function test_login_prolonged_non_network_error() { Svc.Obs.add("weave:ui:login:error", function onSyncError() { Svc.Obs.remove("weave:ui:login:error", onSyncError); do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE); + do_check_true(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -651,6 +685,7 @@ add_task(function test_sync_prolonged_non_network_error() { Svc.Obs.add("weave:ui:sync:error", function onSyncError() { Svc.Obs.remove("weave:ui:sync:error", onSyncError); do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE); + do_check_true(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -671,6 +706,7 @@ add_identity_test(this, function test_login_prolonged_network_error() { Svc.Obs.add("weave:ui:login:error", function onSyncError() { Svc.Obs.remove("weave:ui:login:error", onSyncError); do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE); + do_check_true(errorHandler.didReportProlongedError); clean(); deferred.resolve(); @@ -688,6 +724,7 @@ add_test(function test_sync_prolonged_network_error() { Svc.Obs.add("weave:ui:sync:error", function onSyncError() { Svc.Obs.remove("weave:ui:sync:error", onSyncError); do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE); + do_check_true(errorHandler.didReportProlongedError); Services.io.offline = false; clean(); @@ -708,6 +745,7 @@ add_task(function test_login_non_network_error() { Svc.Obs.add("weave:ui:login:error", function onSyncError() { Svc.Obs.remove("weave:ui:login:error", onSyncError); do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD); + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -734,6 +772,7 @@ add_task(function test_sync_non_network_error() { Svc.Obs.add("weave:ui:sync:error", function onSyncError() { Svc.Obs.remove("weave:ui:sync:error", onSyncError); do_check_eq(Status.sync, CREDENTIALS_CHANGED); + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -755,6 +794,7 @@ add_identity_test(this, function test_login_network_error() { Svc.Obs.remove("weave:ui:clear-error", onClearError); do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR); + do_check_false(errorHandler.didReportProlongedError); Services.io.offline = false; clean(); @@ -773,6 +813,7 @@ add_test(function test_sync_network_error() { Svc.Obs.add("weave:ui:sync:finish", function onUIUpdate() { Svc.Obs.remove("weave:ui:sync:finish", onUIUpdate); do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR); + do_check_false(errorHandler.didReportProlongedError); Services.io.offline = false; clean(); @@ -807,6 +848,7 @@ add_identity_test(this, function test_sync_server_maintenance_error() { do_check_eq(Status.service, SYNC_FAILED_PARTIAL); do_check_eq(Status.sync, SERVER_MAINTENANCE); + do_check_false(errorHandler.didReportProlongedError); Svc.Obs.remove("weave:ui:sync:error", onSyncError); clean(); @@ -850,6 +892,7 @@ add_identity_test(this, function test_info_collections_login_server_maintenance_ do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + do_check_false(errorHandler.didReportProlongedError); Svc.Obs.remove("weave:ui:login:error", onUIUpdate); clean(); @@ -892,6 +935,7 @@ add_identity_test(this, function test_meta_global_login_server_maintenance_error do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + do_check_false(errorHandler.didReportProlongedError); Svc.Obs.remove("weave:ui:login:error", onUIUpdate); clean(); @@ -937,6 +981,7 @@ add_identity_test(this, function test_crypto_keys_login_server_maintenance_error do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + do_check_false(errorHandler.didReportProlongedError); Svc.Obs.remove("weave:ui:login:error", onUIUpdate); clean(); @@ -964,6 +1009,7 @@ add_task(function test_sync_prolonged_server_maintenance_error() { Svc.Obs.remove("weave:ui:sync:error", onUIUpdate); do_check_eq(Status.service, SYNC_FAILED); do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE); + do_check_true(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -998,6 +1044,7 @@ add_identity_test(this, function test_info_collections_login_prolonged_server_ma do_check_eq(backoffInterval, 42); do_check_eq(Status.service, SYNC_FAILED); do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE); + do_check_true(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1033,6 +1080,7 @@ add_identity_test(this, function test_meta_global_login_prolonged_server_mainten do_check_eq(backoffInterval, 42); do_check_eq(Status.service, SYNC_FAILED); do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE); + do_check_true(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1070,6 +1118,7 @@ add_identity_test(this, function test_download_crypto_keys_login_prolonged_serve do_check_eq(backoffInterval, 42); do_check_eq(Status.service, SYNC_FAILED); do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE); + do_check_true(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1105,6 +1154,7 @@ add_identity_test(this, function test_upload_crypto_keys_login_prolonged_server_ do_check_eq(backoffInterval, 42); do_check_eq(Status.service, SYNC_FAILED); do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE); + do_check_true(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1141,6 +1191,7 @@ add_identity_test(this, function test_wipeServer_login_prolonged_server_maintena do_check_eq(backoffInterval, 42); do_check_eq(Status.service, SYNC_FAILED); do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE); + do_check_true(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1183,6 +1234,7 @@ add_identity_test(this, function test_wipeRemote_prolonged_server_maintenance_er do_check_eq(Status.service, SYNC_FAILED); do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE); do_check_eq(Svc.Prefs.get("firstSync"), "wipeRemote"); + do_check_true(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1214,6 +1266,7 @@ add_task(function test_sync_syncAndReportErrors_server_maintenance_error() { Svc.Obs.remove("weave:ui:sync:error", onUIUpdate); do_check_eq(Status.service, SYNC_FAILED_PARTIAL); do_check_eq(Status.sync, SERVER_MAINTENANCE); + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1249,6 +1302,7 @@ add_identity_test(this, function test_info_collections_login_syncAndReportErrors do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1285,6 +1339,7 @@ add_identity_test(this, function test_meta_global_login_syncAndReportErrors_serv do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1323,6 +1378,7 @@ add_identity_test(this, function test_download_crypto_keys_login_syncAndReportEr do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1359,6 +1415,7 @@ add_identity_test(this, function test_upload_crypto_keys_login_syncAndReportErro do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1395,6 +1452,7 @@ add_identity_test(this, function test_wipeServer_login_syncAndReportErrors_serve do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1436,6 +1494,7 @@ add_identity_test(this, function test_wipeRemote_syncAndReportErrors_server_main do_check_eq(Status.service, SYNC_FAILED); do_check_eq(Status.sync, SERVER_MAINTENANCE); do_check_eq(Svc.Prefs.get("firstSync"), "wipeRemote"); + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1467,6 +1526,9 @@ add_task(function test_sync_syncAndReportErrors_prolonged_server_maintenance_err Svc.Obs.remove("weave:ui:sync:error", onUIUpdate); do_check_eq(Status.service, SYNC_FAILED_PARTIAL); do_check_eq(Status.sync, SERVER_MAINTENANCE); + // syncAndReportErrors means dontIgnoreErrors, which means + // didReportProlongedError not touched. + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1502,6 +1564,9 @@ add_identity_test(this, function test_info_collections_login_syncAndReportErrors do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + // syncAndReportErrors means dontIgnoreErrors, which means + // didReportProlongedError not touched. + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1538,6 +1603,9 @@ add_identity_test(this, function test_meta_global_login_syncAndReportErrors_prol do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + // syncAndReportErrors means dontIgnoreErrors, which means + // didReportProlongedError not touched. + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1576,6 +1644,9 @@ add_identity_test(this, function test_download_crypto_keys_login_syncAndReportEr do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + // syncAndReportErrors means dontIgnoreErrors, which means + // didReportProlongedError not touched. + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1612,6 +1683,9 @@ add_identity_test(this, function test_upload_crypto_keys_login_syncAndReportErro do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + // syncAndReportErrors means dontIgnoreErrors, which means + // didReportProlongedError not touched. + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); @@ -1648,6 +1722,9 @@ add_identity_test(this, function test_wipeServer_login_syncAndReportErrors_prolo do_check_eq(backoffInterval, 42); do_check_eq(Status.service, LOGIN_FAILED); do_check_eq(Status.login, SERVER_MAINTENANCE); + // syncAndReportErrors means dontIgnoreErrors, which means + // didReportProlongedError not touched. + do_check_false(errorHandler.didReportProlongedError); clean(); server.stop(deferred.resolve); From 3b50567e9c09ef8d8911aa0191fde3872ea4d8b4 Mon Sep 17 00:00:00 2001 From: Jared Wein Date: Wed, 16 Apr 2014 23:15:05 -0400 Subject: [PATCH 11/15] Bug 996121 - Bouncing unicorn easter egg (in hamburger menu) briefly triggers scrollbar each time it hits the bottom of its bounce area when the font-size is not 12px. r=mconley --- browser/themes/shared/customizableui/panelUIOverlay.inc.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/themes/shared/customizableui/panelUIOverlay.inc.css b/browser/themes/shared/customizableui/panelUIOverlay.inc.css index 3cc511e395a..f5db4dfaa9b 100644 --- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css +++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css @@ -55,7 +55,7 @@ } @keyframes moveY { /* These values are adjusted for the padding and height of the panel. */ - from { margin-top: -6px; } to { margin-top: 58px; } + from { margin-top: -.5em; } to { margin-top: calc(64px - .5em); } } #PanelUI-button { From 871b650eac82d657b34c7c6e5297880cae94e1fa Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Wed, 16 Apr 2014 22:42:15 -0700 Subject: [PATCH 12/15] Updated 7Zip moz modifications to sfx code - Bug 396050 - [7zip] Unnecessary Button provided by the 7-Zip SEA (self extracting archive). r=bbondy --- .../FileManager/Resource/ProgressDialog/ProgressDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/other-licenses/7zstub/src/7zip/FileManager/Resource/ProgressDialog/ProgressDialog.cpp b/other-licenses/7zstub/src/7zip/FileManager/Resource/ProgressDialog/ProgressDialog.cpp index 67d7b49b9e1..b421e8cceff 100644 --- a/other-licenses/7zstub/src/7zip/FileManager/Resource/ProgressDialog/ProgressDialog.cpp +++ b/other-licenses/7zstub/src/7zip/FileManager/Resource/ProgressDialog/ProgressDialog.cpp @@ -164,9 +164,9 @@ bool CProgressDialog::OnButtonClicked(int buttonID, HWND buttonHWND) ProgressSynch.SetPaused(true); int res = ::MessageBoxW(HWND(*this), L"Are you sure you want to cancel?", - _title, MB_YESNOCANCEL); + _title, MB_YESNO); ProgressSynch.SetPaused(paused); - if (res == IDCANCEL || res == IDNO) + if (res == IDNO) return true; break; } From a9cbd835c0219c9ad1d988cc8086b3f3ac118174 Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Wed, 16 Apr 2014 22:42:37 -0700 Subject: [PATCH 13/15] Updated 7Zip sfx - Bug 396050 - [7zip] Unnecessary Button provided by the 7-Zip SEA (self extracting archive). r=bbondy --- other-licenses/7zstub/firefox/7zSD.sfx | Bin 121856 -> 121856 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/other-licenses/7zstub/firefox/7zSD.sfx b/other-licenses/7zstub/firefox/7zSD.sfx index 75caa046e715eb63c24749cfaf688bf3efad0325..ffd53710cdd712896d65fea53b34f47e4f1df0a4 100644 GIT binary patch delta 16029 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 LB=dV>@9=*B94H{( delta 16004 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 From d65bc07000d1d276b6e0b3b32d6d5221d7ba4767 Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Wed, 16 Apr 2014 22:43:30 -0700 Subject: [PATCH 14/15] Updated 7Zip moz modifications to sfx code - Bug 995891 - Reduce stub installer install timeouts. r=tabraldes --- .../src/7zip/Bundles/SFXSetup-moz/Main.cpp | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/Main.cpp b/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/Main.cpp index 8f2727426b0..4f739c886a4 100644 --- a/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/Main.cpp +++ b/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/Main.cpp @@ -242,10 +242,16 @@ int APIENTRY WinMain( /* BEGIN Mozilla customizations */ bool showProgress = true; + bool extractOnly = false; if (switches.Left(3).CompareNoCase(UString(L"-ms")) == 0 || - switches.Left(4).CompareNoCase(UString(L"/INI")) == 0 || - switches.Left(2).CompareNoCase(UString(L"/S")) == 0) + switches.Left(4).CompareNoCase(UString(L"/ini")) == 0 || + switches.Left(2).CompareNoCase(UString(L"/s")) == 0) { showProgress = false; + } else if (switches.Left(12).CompareNoCase(UString(L"/extractdir=")) == 0) { + assumeYes = true; + showProgress = false; + extractOnly = true; + } /* END Mozilla customizations */ AString config; @@ -290,16 +296,20 @@ int APIENTRY WinMain( } NFile::NDirectory::CTempDirectory tempDir; - if (!tempDir.Create(kTempDirPrefix)) + /* Mozilla customizations - Added !extractOnly */ + if (!extractOnly && !tempDir.Create(kTempDirPrefix)) { if (!assumeYes) MyMessageBox(L"Can not create temp folder archive"); return 1; } + /* BEGIN Mozilla customizations */ + UString tempDirPath = (extractOnly ? switches.Mid(12) : GetUnicodeString(tempDir.GetPath())); + /* END Mozilla customizations */ + COpenCallbackGUI openCallback; - UString tempDirPath = GetUnicodeString(tempDir.GetPath()); bool isCorrupt = false; UString errorMessage; HRESULT result = ExtractArchive(fullPath, tempDirPath, &openCallback, showProgress, @@ -320,6 +330,13 @@ int APIENTRY WinMain( return 1; } + /* BEGIN Mozilla customizations */ + // The code immediately above handles the error case for extraction. + if (extractOnly) { + return 0; + } + /* END Mozilla customizations */ + CCurrentDirRestorer currentDirRestorer; if (!SetCurrentDirectory(tempDir.GetPath())) From 12e256a0d2cc33a493595bc1f388f3b09bfd5dd0 Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Wed, 16 Apr 2014 22:44:08 -0700 Subject: [PATCH 15/15] Updated 7Zip sfxthat include moz modifications - Bug 995891 - Reduce stub installer install timeouts. r=me --- other-licenses/7zstub/firefox/7zSD.sfx | Bin 121856 -> 121856 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/other-licenses/7zstub/firefox/7zSD.sfx b/other-licenses/7zstub/firefox/7zSD.sfx index ffd53710cdd712896d65fea53b34f47e4f1df0a4..0243a84c873b939bcf190c0d02f47b869f5da899 100644 GIT binary patch delta 17453 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 QW*M8ch{J)I+NFE`7cig5m;e9(