diff --git a/browser/base/content/browser-fxaccounts.js b/browser/base/content/browser-fxaccounts.js index 95df6c6b5d7d..b3a170cb72b4 100644 --- a/browser/base/content/browser-fxaccounts.js +++ b/browser/base/content/browser-fxaccounts.js @@ -213,7 +213,6 @@ let gFxAccounts = { }, openSignInAgainPage: function () { - // FIXME: This should actually show the pre-filled username version of about:accounts? - switchToTabHavingURI("about:accounts?signin=true", true); + switchToTabHavingURI("about:accounts?action=reauth", true); } }; diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index adafd6a68133..7533c5abb122 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -173,6 +173,7 @@ toolbar[customizing] > .overflow-button { } %ifdef CAN_DRAW_IN_TITLEBAR +#main-window:not([chromemargin]) > #titlebar, #main-window[inFullscreen] > #titlebar, #main-window[inFullscreen] .titlebar-placeholder, #main-window:not([tabsintitlebar]) .titlebar-placeholder { diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 088b61e91703..ed5691b92ecc 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -4481,6 +4481,9 @@ var TabsInTitlebar = { // Try to avoid reflows in this code by calculating dimensions first and // then later set the properties affecting layout together in a batch. + // Get the full height of the tabs toolbar: + let tabsToolbar = $("TabsToolbar"); + let fullTabsHeight = rect(tabsToolbar).height; // Buttons first: let captionButtonsBoxWidth = rect($("titlebar-buttonbox")).width; #ifdef XP_MACOSX @@ -4495,11 +4498,9 @@ var TabsInTitlebar = { let menuHeight = rect(menubar).height; let menuStyles = window.getComputedStyle(menubar); let fullMenuHeight = verticalMargins(menuStyles) + menuHeight; -#endif - // Get the full height of the tabs toolbar: - let tabsToolbar = $("TabsToolbar"); let tabsStyles = window.getComputedStyle(tabsToolbar); - let fullTabsHeight = rect(tabsToolbar).height + verticalMargins(tabsStyles); + fullTabsHeight += verticalMargins(tabsStyles); +#endif // If the navbar overlaps the tabbar using negative margins, we need to take those into // account so we don't overlap it @@ -4509,16 +4510,6 @@ var TabsInTitlebar = { // And get the height of what's in the titlebar: let titlebarContentHeight = rect(titlebarContent).height; - // Padding surrounds the tab-view-deck when we are in customization mode, - // so take that into account: - let areCustomizing = document.documentElement.hasAttribute("customizing") || - document.documentElement.hasAttribute("customize-exiting"); - let customizePadding = 0; - if (areCustomizing) { - let deckStyle = window.getComputedStyle($("tab-view-deck")); - customizePadding = parseFloat(deckStyle.paddingTop); - } - // Begin setting CSS properties which will cause a reflow // If the menubar is around (menuHeight is non-zero), try to adjust @@ -4551,10 +4542,6 @@ var TabsInTitlebar = { // Next, we calculate how much we need to stretch the titlebar down to // go all the way to the bottom of the tab strip, if necessary. let tabAndMenuHeight = fullTabsHeight + fullMenuHeight; - // Oh, and don't forget customization mode: - if (areCustomizing) { - tabAndMenuHeight += customizePadding; - } if (tabAndMenuHeight > titlebarContentHeight) { // We need to increase the titlebar content's outer height (ie including margins) @@ -4566,12 +4553,6 @@ var TabsInTitlebar = { // On non-OSX, we can just use bottom margin: #ifndef XP_MACOSX titlebarContent.style.marginBottom = extraMargin + "px"; -#else - // Otherwise, center the content. This means taking the titlebar's - // padding into account: - let halfMargin = (extraMargin - titlebarPadding) / 2; - titlebarContent.style.marginTop = halfMargin + "px"; - titlebarContent.style.marginBottom = (titlebarPadding + halfMargin) + "px"; #endif titlebarContentHeight += extraMargin; } @@ -4606,6 +4587,7 @@ var TabsInTitlebar = { updateTitlebarDisplay(); // Reset the margins and padding that might have been modified: + titlebarContent.style.marginTop = ""; titlebarContent.style.marginBottom = ""; titlebar.style.marginBottom = ""; menubar.style.paddingBottom = ""; @@ -4630,16 +4612,37 @@ var TabsInTitlebar = { #ifdef CAN_DRAW_IN_TITLEBAR function updateTitlebarDisplay() { - document.getElementById("titlebar").hidden = !TabsInTitlebar.enabled; + +#ifdef XP_MACOSX + // OS X and the other platforms differ enough to necessitate this kind of + // special-casing. Like the other platforms where we CAN_DRAW_IN_TITLEBAR, + // we draw in the OS X titlebar when putting the tabs up there. However, OS X + // also draws in the titlebar when a lightweight theme is applied, regardless + // of whether or not the tabs are drawn in the titlebar. + if (TabsInTitlebar.enabled) { + document.documentElement.setAttribute("chromemargin-nonlwtheme", "0,-1,-1,-1"); + document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1"); + document.documentElement.removeAttribute("drawtitle"); + } else { + // We set chromemargin-nonlwtheme to "" instead of removing it as a way of + // making sure that LightweightThemeConsumer doesn't take it upon itself to + // detect this value again if and when we do a lwtheme state change. + document.documentElement.setAttribute("chromemargin-nonlwtheme", ""); + let isCustomizing = document.documentElement.hasAttribute("customizing"); + let hasLWTheme = document.documentElement.hasAttribute("lwtheme"); + if (!hasLWTheme || isCustomizing) { + document.documentElement.removeAttribute("chromemargin"); + } + document.documentElement.setAttribute("drawtitle", "true"); + } + +#else if (TabsInTitlebar.enabled) -#ifdef XP_WIN document.documentElement.setAttribute("chromemargin", "0,2,2,2"); -#else - document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1"); -#endif else document.documentElement.removeAttribute("chromemargin"); +#endif } #endif diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 5a1323dec682..90ec8a50aa8f 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -313,7 +313,8 @@ skip-if = true # disabled until the tree view is added [browser_tab_dragdrop.js] [browser_tab_dragdrop2.js] [browser_tabbar_big_widgets.js] -skip-if = os == "linux" # No tabs in titlebar on linux +skip-if = os == "linux" || os == "mac" # No tabs in titlebar on linux + # Disabled on OS X because of bug 967917 [browser_tabfocus.js] [browser_tabopen_reflows.js] [browser_tabs_isActive.js] diff --git a/browser/components/customizableui/src/CustomizableWidgets.jsm b/browser/components/customizableui/src/CustomizableWidgets.jsm index 01726c543e0b..0341c528dee9 100644 --- a/browser/components/customizableui/src/CustomizableWidgets.jsm +++ b/browser/components/customizableui/src/CustomizableWidgets.jsm @@ -123,9 +123,6 @@ const CustomizableWidgets = [{ item.setAttribute("label", title || uri); item.setAttribute("targetURI", uri); item.setAttribute("class", "subviewbutton"); - item.addEventListener("command", function (aEvent) { - onHistoryVisit(uri, aEvent, item); - }); item.addEventListener("click", function (aEvent) { onHistoryVisit(uri, aEvent, item); }); diff --git a/browser/devtools/markupview/test/browser.ini b/browser/devtools/markupview/test/browser.ini index 1ac25dc5b7e8..f7e35ffed831 100644 --- a/browser/devtools/markupview/test/browser.ini +++ b/browser/devtools/markupview/test/browser.ini @@ -13,6 +13,8 @@ support-files = # Bug 916763 - too many intermittent failures skip-if = true [browser_inspector_markup_edit.js] +# Bug 904953 - too many intermittent failures on Linux +skip-if = os == "linux" [browser_inspector_markup_edit_outerhtml.js] [browser_inspector_markup_edit_outerhtml2.js] [browser_inspector_markup_mutation.js] diff --git a/browser/metro/base/content/browser-ui.js b/browser/metro/base/content/browser-ui.js index d72dfa0d9e0d..282651bf928c 100644 --- a/browser/metro/base/content/browser-ui.js +++ b/browser/metro/base/content/browser-ui.js @@ -1143,8 +1143,7 @@ var BrowserUI = { confirmSanitizeDialog: function () { let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); let title = bundle.GetStringFromName("clearPrivateData.title2"); - let options = bundle.GetStringFromName("optionsCharm"); - let message = bundle.GetStringFromName("clearPrivateData.message2").replace("#1", options); + let message = bundle.GetStringFromName("clearPrivateData.message3"); let clearbutton = bundle.GetStringFromName("clearPrivateData.clearButton"); let prefsClearButton = document.getElementById("prefs-clear-data"); diff --git a/browser/metro/base/tests/mochitest/browser_ui_telemetry.js b/browser/metro/base/tests/mochitest/browser_ui_telemetry.js index f636ca99b7c6..7c5c3965d1a4 100644 --- a/browser/metro/base/tests/mochitest/browser_ui_telemetry.js +++ b/browser/metro/base/tests/mochitest/browser_ui_telemetry.js @@ -9,16 +9,18 @@ function test() { runTests(); } -function getSimpleMeasurementsFromTelemetryPing() { +function getTelemetryPayload() { return Cu.import("resource://gre/modules/TelemetryPing.jsm", {}). - TelemetryPing.getPayload().simpleMeasurements; + TelemetryPing.getPayload(); } gTests.push({ desc: "Test browser-ui telemetry", run: function testBrowserUITelemetry() { // startup should have registered simple measures function - let simpleMeasurements = getSimpleMeasurementsFromTelemetryPing(); + is(getTelemetryPayload().info.appName, "MetroFirefox"); + + let simpleMeasurements = getTelemetryPayload().simpleMeasurements; ok(simpleMeasurements, "simpleMeasurements are truthy"); ok(simpleMeasurements.UITelemetry["metro-ui"]["window-width"], "window-width measurement was captured"); ok(simpleMeasurements.UITelemetry["metro-ui"]["window-height"], "window-height measurement was captured"); diff --git a/browser/metro/locales/en-US/chrome/browser.properties b/browser/metro/locales/en-US/chrome/browser.properties index 029055229293..4f1d1cd0e122 100644 --- a/browser/metro/locales/en-US/chrome/browser.properties +++ b/browser/metro/locales/en-US/chrome/browser.properties @@ -39,8 +39,8 @@ contextAppbar2.clear=Clear selection # Clear private data clearPrivateData.clearButton=Clear clearPrivateData.title2=Clear private data -# LOCALIZATION NOTE (clearPrivateData.message2): #1 is optionsCharm -clearPrivateData.message2=This will permanently delete the private data you have selected in #1 +# LOCALIZATION NOTE (clearPrivateData.message3): "Options" is the optionsCharm. +clearPrivateData.message3=This will permanently delete the private data you have selected in "Options". # Settings Charms aboutCharm1=About diff --git a/browser/themes/linux/jar.mn b/browser/themes/linux/jar.mn index 96b7a5c89090..da4b4c219116 100644 --- a/browser/themes/linux/jar.mn +++ b/browser/themes/linux/jar.mn @@ -73,6 +73,7 @@ browser.jar: skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png) skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png) skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico) + skin/classic/browser/customizableui/menuPanel-customizeFinish.png (../shared/customizableui/menuPanel-customizeFinish.png) * skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css) skin/classic/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png) skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css) diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index 0f434c2d08c7..f0ae131cdf89 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -59,7 +59,7 @@ } } -#main-window[chromehidden~="toolbar"] > #titlebar { +#main-window[chromehidden~="toolbar"]:not(:-moz-lwtheme) > #titlebar { padding-top: 22px; } @@ -2669,7 +2669,7 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { box-shadow: @focusRingShadow@; } -#titlebar { +#main-window:not(:-moz-lwtheme) > #titlebar { padding-top: @spaceAboveTabbar@; min-height: @tabHeight@; } @@ -4104,13 +4104,26 @@ window > chatbox { %include ../shared/customizableui/customizeMode.inc.css -#main-window[customize-entered] #titlebar { +#main-window[customize-entered] > #titlebar { padding-top: 0; } -#main-window[tabsintitlebar][customize-entered] #titlebar-content { - margin-bottom: 0px !important; - margin-top: 11px !important; +#main-window[tabsintitlebar]:not([customizing]):not(:-moz-lwtheme) > #titlebar > #titlebar-content, +#main-window[tabsintitlebar][customize-entering] > #titlebar > #titlebar-content, +#main-window[tabsintitlebar][customize-exiting] > #titlebar > #titlebar-content { + margin-top: 2px; + margin-bottom: 11px; +} + +#main-window[tabsintitlebar][customize-entered] > #titlebar > #titlebar-content, +#main-window:not([tabsintitlebar]):not(:-moz-lwtheme) > #titlebar > #titlebar-content { + margin-top: 11px; + margin-bottom: 0px; +} + +#main-window[tabsintitlebar]:-moz-lwtheme > #titlebar > #titlebar-content { + margin-top: 11px; + margin-bottom: 11px; } #main-window[customize-entered] #tab-view-deck { @@ -4135,8 +4148,11 @@ window > chatbox { border-bottom-width: 0; } -#main-window[customize-entered] #TabsToolbar { +#main-window[tabsintitlebar][customize-entered] #TabsToolbar { margin-top: 9px; +} + +#main-window[customize-entered] #TabsToolbar { background-clip: padding-box; border-right: 3px solid transparent; border-left: 3px solid transparent; diff --git a/browser/themes/osx/customizableui/panelUIOverlay.css b/browser/themes/osx/customizableui/panelUIOverlay.css index 3b93f5cff889..a1f2915d872b 100644 --- a/browser/themes/osx/customizableui/panelUIOverlay.css +++ b/browser/themes/osx/customizableui/panelUIOverlay.css @@ -15,6 +15,10 @@ list-style-image: url(chrome://browser/skin/menuPanel-customize@2x.png); } + #main-window[customize-entered] #PanelUI-customize { + list-style-image: url(chrome://browser/skin/customizableui/menuPanel-customizeFinish@2x.png); + } + #PanelUI-help { list-style-image: url(chrome://browser/skin/menuPanel-help@2x.png); } diff --git a/browser/themes/osx/jar.mn b/browser/themes/osx/jar.mn index 411af517906a..209d78c4102b 100644 --- a/browser/themes/osx/jar.mn +++ b/browser/themes/osx/jar.mn @@ -120,6 +120,8 @@ browser.jar: skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png) skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png) skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png) + skin/classic/browser/customizableui/menuPanel-customizeFinish.png (../shared/customizableui/menuPanel-customizeFinish.png) + skin/classic/browser/customizableui/menuPanel-customizeFinish@2x.png (../shared/customizableui/menuPanel-customizeFinish@2x.png) skin/classic/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png) skin/classic/browser/customizableui/subView-arrow-back-inverted@2x.png (../shared/customizableui/subView-arrow-back-inverted@2x.png) * skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css) diff --git a/browser/themes/shared/customizableui/menuPanel-customizeFinish.png b/browser/themes/shared/customizableui/menuPanel-customizeFinish.png new file mode 100644 index 000000000000..0251ed747087 Binary files /dev/null and b/browser/themes/shared/customizableui/menuPanel-customizeFinish.png differ diff --git a/browser/themes/shared/customizableui/menuPanel-customizeFinish@2x.png b/browser/themes/shared/customizableui/menuPanel-customizeFinish@2x.png new file mode 100644 index 000000000000..593e1df43d30 Binary files /dev/null and b/browser/themes/shared/customizableui/menuPanel-customizeFinish@2x.png differ diff --git a/browser/themes/shared/customizableui/panelUIOverlay.inc.css b/browser/themes/shared/customizableui/panelUIOverlay.inc.css index 5c11f54fc688..63f4ba6ac888 100644 --- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css +++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css @@ -322,6 +322,10 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton { list-style-image: url(chrome://browser/skin/menuPanel-customize.png); } +#customization-panelHolder #PanelUI-customize { + list-style-image: url(chrome://browser/skin/customizableui/menuPanel-customizeFinish.png); +} + #PanelUI-help { list-style-image: url(chrome://browser/skin/menuPanel-help.png); } @@ -378,16 +382,15 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton { background-color: #ad3434; } -#main-window[customize-entered] #PanelUI-customize { +#customization-panelHolder #PanelUI-customize { color: white; - background-image: linear-gradient(rgb(41,123,204), rgb(40,133,203)); - box-shadow: inset 0 1px 1px rgba(0,0,0,0.5), 0 2px rgba(255,255,255,0.2); - text-shadow: 0 1px 0 rgba(0,0,0,0.4); + background-color: rgb(116,191,67); + text-shadow: none; } -#main-window[customize-entered] #PanelUI-customize:hover, -#main-window[customize-entered] #PanelUI-customize:hover:active { - background-image: linear-gradient(rgb(38,115,191), rgb(38,125,191)); +#customization-panelHolder #PanelUI-customize:hover, +#customization-panelHolder #PanelUI-customize:hover:active { + background-color: rgb(105,173,61); } #customization-palette .toolbarbutton-multiline-text, diff --git a/browser/themes/windows/jar.mn b/browser/themes/windows/jar.mn index 5e7c4d4f196a..2da7cf889eee 100644 --- a/browser/themes/windows/jar.mn +++ b/browser/themes/windows/jar.mn @@ -92,6 +92,7 @@ browser.jar: skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png) skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png) skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png) + skin/classic/browser/customizableui/menuPanel-customizeFinish.png (../shared/customizableui/menuPanel-customizeFinish.png) * skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css) skin/classic/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png) * skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css) @@ -404,6 +405,7 @@ browser.jar: skin/classic/aero/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png) skin/classic/aero/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png) skin/classic/aero/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png) + skin/classic/aero/browser/customizableui/menuPanel-customizeFinish.png (../shared/customizableui/menuPanel-customizeFinish.png) * skin/classic/aero/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css) skin/classic/aero/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png) * skin/classic/aero/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay-aero.css) diff --git a/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java b/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java index 87af864f93b9..b506d112a1f3 100644 --- a/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java +++ b/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java @@ -538,7 +538,7 @@ public class HealthReportDatabaseStorage implements HealthReportStorage { this.fieldID = integerQuery("named_fields", "field_id", "measurement_name = ? AND measurement_version = ? AND field_name = ?", new String[] {measurementName, measurementVersion, fieldName}, - -1); + UNKNOWN_TYPE_OR_FIELD_ID); if (this.fieldID == UNKNOWN_TYPE_OR_FIELD_ID) { throw new IllegalStateException("No field with name " + fieldName + " (" + measurementName + ", " + measurementVersion + ")"); @@ -552,7 +552,9 @@ public class HealthReportDatabaseStorage implements HealthReportStorage { // store differently stable kinds of data, hence type difference. // Note that we don't pre-populate the environment cache. We'll typically only // handle one per session. - private final ConcurrentHashMap envs = new ConcurrentHashMap(); + // + // protected for testing purposes only. + protected final ConcurrentHashMap envs = new ConcurrentHashMap(); /** * An {@link Environment} that knows how to persist to and from our database. @@ -855,6 +857,12 @@ public class HealthReportDatabaseStorage implements HealthReportStorage { private HashMap fields = new HashMap(); private boolean fieldsCacheUpdated = false; + private void invalidateFieldsCache() { + synchronized (this.fields) { + fieldsCacheUpdated = false; + } + } + private String getFieldKey(String mName, int mVersion, String fieldName) { return mVersion + "." + mName + "/" + fieldName; } @@ -1014,9 +1022,7 @@ public class HealthReportDatabaseStorage implements HealthReportStorage { notifyMeasurementVersionUpdated(measurement, version); // Let's be easy for now. - synchronized (fields) { - fieldsCacheUpdated = false; - } + invalidateFieldsCache(); } /** @@ -1152,10 +1158,16 @@ public class HealthReportDatabaseStorage implements HealthReportStorage { final SQLiteDatabase db = this.helper.getWritableDatabase(); putValue(v, value); - try { - db.insertOrThrow(table, null, v); - } catch (SQLiteConstraintException e) { - throw new IllegalStateException("Event did not reference existing an environment or field.", e); + + // Using SQLiteDatabase.insertOrThrow throws SQLiteConstraintException we cannot catch for + // unknown reasons (bug 961526 comment 13). We believe these are thrown because we attempt to + // record events using environment IDs removed from the database by the prune service. We + // invalidate the currentEnvironment ID after pruning, preventing further propagation, + // however, any event recording waiting for the prune service to complete on the background + // thread may carry an invalid ID: we expect an insertion failure and drop these events here. + final long res = db.insert(table, null, v); + if (res == -1) { + Logger.error(LOG_TAG, "Unable to record daily discrete event. Ignoring."); } } @@ -1516,6 +1528,11 @@ public class HealthReportDatabaseStorage implements HealthReportStorage { try { // Cascade will clear the rest. db.delete("measurements", null, null); + + // Clear measurements and fields cache, because some of their IDs are now invalid. + invalidateFieldsCache(); // Let it repopulate on its own. + populateMeasurementVersionsCache(db); // Performed at Storage init so repopulate now. + db.setTransactionSuccessful(); } finally { db.endTransaction(); @@ -1524,7 +1541,7 @@ public class HealthReportDatabaseStorage implements HealthReportStorage { /** * Prunes the given number of least-recently used environments. Note that orphaned environments - * are not removed. + * are not removed and the environment cache is cleared. */ public void pruneEnvironments(final int numToPrune) { final SQLiteDatabase db = this.helper.getWritableDatabase(); @@ -1538,6 +1555,9 @@ public class HealthReportDatabaseStorage implements HealthReportStorage { " LIMIT " + numToPrune + ")", null); db.setTransactionSuccessful(); + + // Clear environment cache, because some of their IDs are now invalid. + this.envs.clear(); } finally { db.endTransaction(); } diff --git a/mobile/android/base/background/healthreport/prune/PrunePolicyDatabaseStorage.java b/mobile/android/base/background/healthreport/prune/PrunePolicyDatabaseStorage.java index 2522b6ba0806..459130aaa92f 100644 --- a/mobile/android/base/background/healthreport/prune/PrunePolicyDatabaseStorage.java +++ b/mobile/android/base/background/healthreport/prune/PrunePolicyDatabaseStorage.java @@ -43,6 +43,11 @@ public class PrunePolicyDatabaseStorage implements PrunePolicyStorage { public void pruneEnvironments(final int count) { getStorage().pruneEnvironments(count); + + // Re-populate the DB and environment cache with the current environment in the unlikely event + // that it was deleted. + this.currentEnvironmentID = -1; + getCurrentEnvironmentID(); } /** diff --git a/mobile/android/base/health/BrowserHealthRecorder.java b/mobile/android/base/health/BrowserHealthRecorder.java index 503438b4ac0c..e49b3044e013 100644 --- a/mobile/android/base/health/BrowserHealthRecorder.java +++ b/mobile/android/base/health/BrowserHealthRecorder.java @@ -37,9 +37,13 @@ import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.nio.charset.Charset; +import java.util.Arrays; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.Scanner; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -758,11 +762,11 @@ public class BrowserHealthRecorder implements GeckoEventListener { public static final String MEASUREMENT_NAME_SEARCH_COUNTS = "org.mozilla.searches.counts"; public static final int MEASUREMENT_VERSION_SEARCH_COUNTS = 5; - public static final String[] SEARCH_LOCATIONS = { + public static final Set SEARCH_LOCATIONS = Collections.unmodifiableSet(new HashSet(Arrays.asList(new String[] { "barkeyword", "barsuggest", "bartext", - }; + }))); private void initializeSearchProvider() { this.storage.ensureMeasurementInitialized( @@ -771,7 +775,7 @@ public class BrowserHealthRecorder implements GeckoEventListener { new MeasurementFields() { @Override public Iterable getFields() { - ArrayList out = new ArrayList(SEARCH_LOCATIONS.length); + ArrayList out = new ArrayList(SEARCH_LOCATIONS.size()); for (String location : SEARCH_LOCATIONS) { // We're not using a counter, because the set of engine // identifiers is potentially unbounded, and thus our @@ -803,19 +807,31 @@ public class BrowserHealthRecorder implements GeckoEventListener { return; } + final int env = this.env; + + if (env == -1) { + Log.d(LOG_TAG, "No environment: not recording search."); + return; + } + if (location == null) { throw new IllegalArgumentException("location must be provided for search."); } + // Ensure that we don't throw when trying to look up the field for an + // unknown location. If you add a search location, you must extend the + // list of search locations *and update the measurement version*. + if (!SEARCH_LOCATIONS.contains(location)) { + throw new IllegalArgumentException("Unexpected location: " + location); + } + final int day = storage.getDay(); - final int env = this.env; final String key = (engineID == null) ? "other" : engineID; - final BrowserHealthRecorder self = this; ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { - final HealthReportDatabaseStorage storage = self.storage; + final HealthReportDatabaseStorage storage = BrowserHealthRecorder.this.storage; if (storage == null) { Log.d(LOG_TAG, "No storage: not recording search. Shutting down?"); return; diff --git a/mobile/android/tests/background/junit3/src/healthreport/MockHealthReportDatabaseStorage.java b/mobile/android/tests/background/junit3/src/healthreport/MockHealthReportDatabaseStorage.java index 14e6d19ee44c..5c5b50b5e56d 100644 --- a/mobile/android/tests/background/junit3/src/healthreport/MockHealthReportDatabaseStorage.java +++ b/mobile/android/tests/background/junit3/src/healthreport/MockHealthReportDatabaseStorage.java @@ -5,6 +5,7 @@ package org.mozilla.gecko.background.healthreport; import java.io.File; import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; import org.json.JSONObject; import org.mozilla.gecko.background.common.GlobalConstants; @@ -42,6 +43,10 @@ public class MockHealthReportDatabaseStorage extends HealthReportDatabaseStorage return now - numDays * GlobalConstants.MILLISECONDS_PER_DAY; } + public ConcurrentHashMap getEnvironmentCache() { + return this.envs; + } + public MockHealthReportDatabaseStorage(Context context, File fakeProfileDirectory) { super(context, fakeProfileDirectory); } diff --git a/mobile/android/tests/background/junit3/src/healthreport/TestHealthReportDatabaseStorage.java b/mobile/android/tests/background/junit3/src/healthreport/TestHealthReportDatabaseStorage.java index 0b4915569809..858de40e25ca 100644 --- a/mobile/android/tests/background/junit3/src/healthreport/TestHealthReportDatabaseStorage.java +++ b/mobile/android/tests/background/junit3/src/healthreport/TestHealthReportDatabaseStorage.java @@ -326,10 +326,6 @@ public class TestHealthReportDatabaseStorage extends FakeProfileTestCase { storage.incrementDailyCount(nonExistentEnvID, storage.getToday(), counterFieldID); fail("Should throw - event_integer(env) references environments(id), which is given as a non-existent value."); } catch (IllegalStateException e) { } - try { - storage.recordDailyDiscrete(nonExistentEnvID, storage.getToday(), discreteFieldID, "iu"); - fail("Should throw - event_textual(env) references environments(id), which is given as a non-existent value."); - } catch (IllegalStateException e) { } try { storage.recordDailyLast(nonExistentEnvID, storage.getToday(), discreteFieldID, "iu"); fail("Should throw - event_textual(env) references environments(id), which is given as a non-existent value."); @@ -339,14 +335,30 @@ public class TestHealthReportDatabaseStorage extends FakeProfileTestCase { storage.incrementDailyCount(envID, storage.getToday(), nonExistentFieldID); fail("Should throw - event_integer(field) references fields(id), which is given as a non-existent value."); } catch (IllegalStateException e) { } - try { - storage.recordDailyDiscrete(envID, storage.getToday(), nonExistentFieldID, "iu"); - fail("Should throw - event_textual(field) references fields(id), which is given as a non-existent value."); - } catch (IllegalStateException e) { } try { storage.recordDailyLast(envID, storage.getToday(), nonExistentFieldID, "iu"); fail("Should throw - event_textual(field) references fields(id), which is given as a non-existent value."); } catch (IllegalStateException e) { } + + // Test dropped events due to constraint violations that do not throw (see bug 961526). + final String eventValue = "a value not in the database"; + assertFalse(isEventInDB(db, eventValue)); // Better safe than sorry. + + storage.recordDailyDiscrete(nonExistentEnvID, storage.getToday(), discreteFieldID, eventValue); + assertFalse(isEventInDB(db, eventValue)); + + storage.recordDailyDiscrete(envID, storage.getToday(), nonExistentFieldID, "iu"); + assertFalse(isEventInDB(db, eventValue)); + } + + private static boolean isEventInDB(final SQLiteDatabase db, final String value) { + final Cursor c = db.query("events_textual", new String[] {"value"}, "value = ?", + new String[] {value}, null, null, null); + try { + return c.getCount() > 0; + } finally { + c.close(); + } } // Largely taken from testDeleteEnvAndEventsBefore and testDeleteOrphanedAddons. @@ -553,7 +565,10 @@ public class TestHealthReportDatabaseStorage extends FakeProfileTestCase { new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory, 2); final SQLiteDatabase db = storage.getDB(); assertEquals(5, DBHelpers.getRowCount(db, "environments")); + assertEquals(5, storage.getEnvironmentCache().size()); + storage.pruneEnvironments(1); + assertEquals(0, storage.getEnvironmentCache().size()); assertTrue(!getEnvAppVersions(db).contains("v3")); storage.pruneEnvironments(2); assertTrue(!getEnvAppVersions(db).contains("v2")); diff --git a/toolkit/components/telemetry/TelemetryPing.jsm b/toolkit/components/telemetry/TelemetryPing.jsm index 20864c43ae9b..54183cc2ca5d 100644 --- a/toolkit/components/telemetry/TelemetryPing.jsm +++ b/toolkit/components/telemetry/TelemetryPing.jsm @@ -457,6 +457,14 @@ let Impl = { revision: HISTOGRAMS_FILE_VERSION, locale: getLocale() }; + + // In order to share profile data, the appName used for Metro Firefox is "Firefox", + // (the same as desktop Firefox). We set it to "MetroFirefox" here in order to + // differentiate telemetry pings sent by desktop vs. metro Firefox. + if(Services.metro && Services.metro.immersive) { + ret.appName = "MetroFirefox"; + } + if (this._previousBuildID) { ret.previousBuildID = this._previousBuildID; } diff --git a/toolkit/modules/LightweightThemeConsumer.jsm b/toolkit/modules/LightweightThemeConsumer.jsm index 0e3f8fbd5940..611b640579b2 100644 --- a/toolkit/modules/LightweightThemeConsumer.jsm +++ b/toolkit/modules/LightweightThemeConsumer.jsm @@ -42,9 +42,7 @@ LightweightThemeConsumer.prototype = { _lastScreenWidth: null, _lastScreenHeight: null, _enabled: true, -#ifdef XP_MACOSX - _chromemarginDefault: undefined, -#endif + _active: false, enable: function() { this._enabled = true; @@ -100,8 +98,9 @@ LightweightThemeConsumer.prototype = { if (!this._enabled) return; - var root = this._doc.documentElement; - var active = !!aData.headerURL; + let root = this._doc.documentElement; + let active = !!aData.headerURL; + let stateChanging = (active != this._active); if (active) { root.style.color = aData.textcolor || "black"; @@ -117,6 +116,8 @@ LightweightThemeConsumer.prototype = { root.removeAttribute("lwtheme"); } + this._active = active; + _setImage(root, active, aData.headerURL); if (this._footerId) { let footer = this._doc.getElementById(this._footerId); @@ -129,20 +130,26 @@ LightweightThemeConsumer.prototype = { } #ifdef XP_MACOSX - // Sample whether or not we draw in the titlebar by default the first time we update. - // If the root has no chromemargin attribute, getAttribute will return null, and - // we'll remove the attribute when the lw-theme is deactivated. - if (this._chromemarginDefault === undefined) - this._chromemarginDefault = root.getAttribute("chromemargin"); + // On OS X, we extend the lightweight theme into the titlebar, which means setting + // the chromemargin attribute. Some XUL applications already draw in the titlebar, + // so we need to save the chromemargin value before we overwrite it with the value + // that lets us draw in the titlebar. We stash this value on the root attribute so + // that XUL applications have the ability to invalidate the saved value. + if (stateChanging) { + if (!root.hasAttribute("chromemargin-nonlwtheme")) { + root.setAttribute("chromemargin-nonlwtheme", root.getAttribute("chromemargin")); + } - if (active) { - root.setAttribute("chromemargin", "0,-1,-1,-1"); - } - else { - if (this._chromemarginDefault) - root.setAttribute("chromemargin", this._chromemarginDefault); - else - root.removeAttribute("chromemargin"); + if (active) { + root.setAttribute("chromemargin", "0,-1,-1,-1"); + } else { + let defaultChromemargin = root.getAttribute("chromemargin-nonlwtheme"); + if (defaultChromemargin) { + root.setAttribute("chromemargin", defaultChromemargin); + } else { + root.removeAttribute("chromemargin"); + } + } } #endif }