From f2758cb3c248fd0854d2c5c6324c3eda7c5f32ec Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Thu, 23 Jul 2015 09:55:15 -0700 Subject: [PATCH 01/18] Bug 1178818 - package SystemMessage components in Fennec; r=esawin --- mobile/android/installer/package-manifest.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index 89160aa58c23..e7afddef9195 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -424,6 +424,11 @@ @BINPATH@/components/ActivityWrapper.js @BINPATH@/components/ActivityMessageConfigurator.js +@BINPATH@/components/SystemMessageInternal.js +@BINPATH@/components/SystemMessageManager.js +@BINPATH@/components/SystemMessageCache.js +@BINPATH@/components/SystemMessageManager.manifest + @BINPATH@/components/TCPSocket.js @BINPATH@/components/TCPSocketParentIntermediary.js @BINPATH@/components/TCPServerSocket.js From 4ec6e5acc82a4e52b25f63e03e9f043952d98f8d Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Thu, 23 Jul 2015 13:27:28 +0200 Subject: [PATCH 02/18] Bug 1186532 - Add RecyclerView library to Robocop builds. r=jonalmeida --HG-- extra : commitid : 11Mj6YuNxc7 extra : rebase_source : 8a9fccd3f2615964d0d2477e79f44690c19a9604 --- build/mobile/robocop/Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/mobile/robocop/Makefile.in b/build/mobile/robocop/Makefile.in index e7f5adcdb922..20f820fdaebb 100644 --- a/build/mobile/robocop/Makefile.in +++ b/build/mobile/robocop/Makefile.in @@ -67,7 +67,7 @@ tools:: $(ANDROID_APK_NAME).apk # out of sync with base's build config. jars_dir := $(DEPTH)/mobile/android/base stumbler_jars_dir := $(DEPTH)/mobile/android/stumbler -JAVA_BOOTCLASSPATH := $(JAVA_BOOTCLASSPATH):$(subst $(NULL) ,:,$(wildcard $(jars_dir)/*.jar)):$(subst $(NULL) ,:,$(wildcard $(stumbler_jars_dir)/*.jar)):$(ANDROID_COMPAT_LIB) +JAVA_BOOTCLASSPATH := $(JAVA_BOOTCLASSPATH):$(subst $(NULL) ,:,$(wildcard $(jars_dir)/*.jar)):$(subst $(NULL) ,:,$(wildcard $(stumbler_jars_dir)/*.jar)):$(ANDROID_COMPAT_LIB):$(ANDROID_RECYCLERVIEW_LIB) # We also want to re-compile classes.dex when the associated base # content changes. classes.dex: $(wildcard $(jars_dir)/*.jar) From 378c8cb558fa30cd0ba4cf61c69fc5afc3b372fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Thu, 23 Jul 2015 19:12:29 +0200 Subject: [PATCH 03/18] Bug 1173734 - Make the separator for the menu / hamburger button more solid. r=jaws --HG-- extra : rebase_source : 3b9738bb6e622a846366b43c60bd029433b5e89e --- browser/themes/linux/browser.css | 2 +- browser/themes/osx/browser.css | 2 +- .../customizableui/panelUIOverlay.inc.css | 18 ++++++------------ browser/themes/shared/devedition.inc.css | 8 -------- browser/themes/windows/browser.css | 2 +- 5 files changed, 9 insertions(+), 23 deletions(-) diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index e95f2a2dcd2f..24ee34a977da 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -614,7 +614,7 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba } #nav-bar #PanelUI-menu-button { - -moz-padding-start: 7px; + -moz-padding-start: 5px; -moz-padding-end: 5px; } diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index f5da5c9e5031..0a23cfd5aae6 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -724,7 +724,7 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic margin-bottom: 0; padding-top: 1px; padding-bottom: 1px; - -moz-margin-start: 9px; + -moz-margin-start: 7px; -moz-margin-end: 7px; } diff --git a/browser/themes/shared/customizableui/panelUIOverlay.inc.css b/browser/themes/shared/customizableui/panelUIOverlay.inc.css index 7852890a9b8e..0b0ff14df84f 100644 --- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css +++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css @@ -21,12 +21,6 @@ %include ../browser.inc :root { - --panel-ui-button-background-image: linear-gradient(to bottom, transparent, hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, transparent), - linear-gradient(to bottom, transparent, hsla(210,54%,20%,.3) 30%, hsla(210,54%,20%,.3) 70%, transparent), - linear-gradient(to bottom, transparent, hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, transparent); - --panel-ui-button-background-size: 1px calc(100% - 1px), 1px calc(100% - 1px), 1px calc(100% - 1px) !important; - --panel-ui-button-background-position: 0px 0px, 1px 0px, 2px 0px; - --panel-ui-button-background-repeat: no-repeat; --panel-ui-exit-subview-gutter-width: 38px; } @@ -97,14 +91,14 @@ } #PanelUI-button { - background-image: var(--panel-ui-button-background-image); - background-size: var(--panel-ui-button-background-size); - background-position: var(--panel-ui-button-background-position); - background-repeat: var(--panel-ui-button-background-repeat); + margin-inline-start: 2px; + border-inline-start: 1px solid; + border-image: linear-gradient(transparent, rgba(0,0,0,.1) 20%, rgba(0,0,0,.1) 80%, transparent); + border-image-slice: 1; } -#PanelUI-button:-moz-locale-dir(rtl) { - background-position: 100% 0, calc(100% - 1px) 0, calc(100% - 2px) 0; +#nav-bar[brighttext] > #PanelUI-button { + border-image-source: linear-gradient(transparent, rgba(100%,100%,100%,.2) 20%, rgba(100%,100%,100%,.2) 80%, transparent); } #PanelUI-menu-button[update-status="succeeded"] > .toolbarbutton-badge-stack > .toolbarbutton-badge { diff --git a/browser/themes/shared/devedition.inc.css b/browser/themes/shared/devedition.inc.css index e488c13db44c..c8e9193a764c 100644 --- a/browser/themes/shared/devedition.inc.css +++ b/browser/themes/shared/devedition.inc.css @@ -10,8 +10,6 @@ --tab-toolbar-navbar-overlap: 0px; --space-above-tabbar: 0px; --toolbarbutton-text-shadow: none; - --panel-ui-button-background-size: 1px calc(100% - 1px); - --panel-ui-button-background-position: 1px 0px; } :root[devtoolstheme="dark"] { @@ -60,9 +58,6 @@ --urlbar-dropmarker-2x-region: rect(0px, 11px, 14px, 0px); --urlbar-dropmarker-active-2x-region: rect(0px, 22px, 14px, 11px); --search-button-image: url("chrome://browser/skin/devedition/search.svg#search-icon-inverted"); - - /* Menu button separator */ - --panel-ui-button-background-image: linear-gradient(to bottom, transparent, #5F6670 30%, #5F6670 70%, transparent); } :root[devtoolstheme="dark"] #identity-box { @@ -115,9 +110,6 @@ /* Url and search bars */ --search-button-image: url("chrome://browser/skin/devedition/search.svg#search-icon"); - - /* Menu button separator */ - --panel-ui-button-background-image: linear-gradient(to bottom, transparent, rgba(0,0,0,0.1) 30%, rgba(0,0,0,0.1) 70%, transparent); } /* Give some space to drag the window around while customizing diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index 934b87e2596b..3709470eae28 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -655,7 +655,7 @@ toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker { } #nav-bar #PanelUI-menu-button { - -moz-padding-start: 7px; + -moz-padding-start: 5px; -moz-padding-end: 5px; } From 63e3fa8967175e743573812a72036e33e4dc5d29 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Thu, 23 Jul 2015 10:41:57 -0700 Subject: [PATCH 04/18] Bug 1131368 - test plugin.allowed_types pref; r=josh --- dom/plugins/base/nsPluginHost.cpp | 5 + dom/plugins/test/unit/test_allowed_types.js | 140 ++++++++++++++++++++ dom/plugins/test/unit/xpcshell.ini | 3 + 3 files changed, 148 insertions(+) create mode 100644 dom/plugins/test/unit/test_allowed_types.js diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index 0a4f76eeb6d2..df5105ea7afe 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -247,6 +247,11 @@ IsTypeInList(nsCString &aMimeType, nsCString aTypeList) commaSeparated += aMimeType; commaSeparated.Append(','); + // Lower-case the search string and MIME type to properly handle a mixed-case + // type, as MIME types are case insensitive. + ToLowerCase(searchStr); + ToLowerCase(commaSeparated); + return FindInReadable(commaSeparated, start, end); } diff --git a/dom/plugins/test/unit/test_allowed_types.js b/dom/plugins/test/unit/test_allowed_types.js new file mode 100644 index 000000000000..d610e5d1cb3a --- /dev/null +++ b/dom/plugins/test/unit/test_allowed_types.js @@ -0,0 +1,140 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +Components.utils.import("resource://gre/modules/Services.jsm"); + +function run_test() { + const pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); + const pluginDefaultState = Services.prefs.getIntPref("plugin.default.state"); + + function reload_plugins_with_allowed_types(allowed_types) { + if (typeof allowed_types === "undefined") { + // If we didn't get an allowed_types string, then unset the pref, + // so the caller can test the behavior when the pref isn't set. + Services.prefs.clearUserPref("plugin.allowed_types"); + } else { + Services.prefs.setCharPref("plugin.allowed_types", allowed_types); + } + pluginHost.reloadPlugins(); + } + + function get_status_for_type(type) { + try { + return pluginHost.getStateForType(type); + } catch(ex) { + // If the type is not allowed, then nsIPluginHost.getStateForType throws + // NS_ERROR_NOT_AVAILABLE, for which we return undefined to make it easier + // to write assertions about the API. + if (ex.result === Cr.NS_ERROR_NOT_AVAILABLE) { + return undefined; + } + throw ex; + } + } + + // If allowed_types isn't set, then all plugin types are enabled. + reload_plugins_with_allowed_types(); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + // If allowed_types is set to the empty string, then all plugin types are enabled. + reload_plugins_with_allowed_types(""); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + // If allowed_types is set to anything other than the empty string, + // then only types that exactly match its comma-separated entries are enabled, + // so a single space disables all types. + reload_plugins_with_allowed_types(" "); + do_check_eq(get_status_for_type("application/x-test"), undefined); + do_check_eq(get_status_for_type("application/x-Second-Test"), undefined); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + // The rest of the assertions test various values of allowed_types to ensure + // that the correct types are enabled. + + reload_plugins_with_allowed_types("application/x-test"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), undefined); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-Second-Test"); + do_check_eq(get_status_for_type("application/x-test"), undefined); + do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-nonexistent"); + do_check_eq(get_status_for_type("application/x-test"), undefined); + do_check_eq(get_status_for_type("application/x-Second-Test"), undefined); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-test,application/x-Second-Test"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-Second-Test,application/x-test"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-test,application/x-nonexistent"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), undefined); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-nonexistent,application/x-test"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), undefined); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-test,application/x-Second-Test,application/x-nonexistent"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-test,application/x-nonexistent,application/x-Second-Test"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-Second-Test,application/x-test,application/x-nonexistent"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-Second-Test,application/x-nonexistent,application/x-test"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-nonexistent,application/x-test,application/x-Second-Test"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + reload_plugins_with_allowed_types("application/x-nonexistent,application/x-Second-Test,application/x-test"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState); + do_check_eq(get_status_for_type("application/x-nonexistent"), undefined); + + // Plugin types are case-insensitive, so matching should be too. + reload_plugins_with_allowed_types("APPLICATION/X-TEST"); + do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState); + reload_plugins_with_allowed_types("application/x-test"); + do_check_eq(get_status_for_type("APPLICATION/X-TEST"), pluginDefaultState); + + // Types must match exactly, so supersets should not enable subset types. + reload_plugins_with_allowed_types("application/x-test-superset"); + do_check_eq(get_status_for_type("application/x-test"), undefined); + reload_plugins_with_allowed_types("superset-application/x-test"); + do_check_eq(get_status_for_type("application/x-test"), undefined); + + // Clean up. + Services.prefs.clearUserPref("plugin.allowed_types"); + Services.prefs.clearUserPref("plugin.importedState"); +} diff --git a/dom/plugins/test/unit/xpcshell.ini b/dom/plugins/test/unit/xpcshell.ini index 4cf68010267a..648aa782be43 100644 --- a/dom/plugins/test/unit/xpcshell.ini +++ b/dom/plugins/test/unit/xpcshell.ini @@ -3,6 +3,9 @@ skip-if = buildapp == 'mulet' || toolkit == 'android' || toolkit == 'gonk' head = head_plugins.js tail = +[test_allowed_types.js] +skip-if = appname == "thunderbird" +reason = plugins are disabled by default in Thunderbird [test_bug455213.js] # Bug 676953: test fails consistently on Android fail-if = os == "android" From 2badacb231b86be003a9ca0cac7104e624019f39 Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Wed, 10 Jun 2015 15:56:19 +0200 Subject: [PATCH 05/18] Bug 1147664 - Detailed mode for PerformanceStats (low-level). r=jandem --HG-- extra : rebase_source : 0c9393e854c6999c2278b0ef66bc8626b9ba1a75 extra : source : 83fa8af8140b4a3ab275313b77f4dee26312b7fa --- js/src/jsapi.cpp | 48 ++- js/src/jsapi.h | 46 ++- js/src/vm/Interpreter.cpp | 274 ++++++++++++------ js/src/vm/Runtime.cpp | 67 ++++- js/src/vm/Runtime.h | 59 ++-- .../perfmonitoring/nsPerformanceStats.cpp | 18 ++ 6 files changed, 365 insertions(+), 147 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 2305230a04b1..6451c6989f79 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -317,16 +317,21 @@ IterPerformanceStats(JSContext* cx, } JSRuntime* rt = JS_GetRuntime(cx); - for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) { + + // First report the shared groups + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { JSCompartment* compartment = c.get(); - if (!compartment->performanceMonitoring.isLinked()) { + if (!c->principals()) { + // Compartments without principals could show up here, but + // reporting them doesn't really make sense. + continue; + } + if (!c->performanceMonitoring.hasSharedGroup()) { // Don't report compartments that do not even have a PerformanceGroup. continue; } - js::AutoCompartment autoCompartment(cx, compartment); - PerformanceGroup* group = compartment->performanceMonitoring.getGroup(cx); - + PerformanceGroup* group = compartment->performanceMonitoring.getSharedGroup(cx); if (group->data.ticks == 0) { // Don't report compartments that have never been used. continue; @@ -338,7 +343,9 @@ IterPerformanceStats(JSContext* cx, continue; } - if (!(*walker)(cx, group->data, group->uid, closure)) { + if (!(*walker)(cx, + group->data, group->uid, nullptr, + closure)) { // Issue in callback return false; } @@ -347,6 +354,35 @@ IterPerformanceStats(JSContext* cx, return false; } } + + // Then report the own groups + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + JSCompartment* compartment = c.get(); + if (!c->principals()) { + // Compartments without principals could show up here, but + // reporting them doesn't really make sense. + continue; + } + if (!c->performanceMonitoring.hasOwnGroup()) { + // Don't report compartments that do not even have a PerformanceGroup. + continue; + } + js::AutoCompartment autoCompartment(cx, compartment); + PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx); + if (ownGroup->data.ticks == 0) { + // Don't report compartments that have never been used. + continue; + } + PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx); + if (!(*walker)(cx, + ownGroup->data, ownGroup->uid, &sharedGroup->uid, + closure)) { + // Issue in callback + return false; + } + } + + // Finally, report the process stats *processStats = rt->stopwatch.performance; return true; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 7b13c55438a9..e0f77cf733b1 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5409,7 +5409,7 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp) namespace js { -struct AutoStopwatch; +class AutoStopwatch; // Container for performance data // All values are monotonic. @@ -5540,21 +5540,30 @@ private: }; // -// Indirection towards a PerformanceGroup. -// This structure handles reference counting for instances of PerformanceGroup. +// Each PerformanceGroupHolder handles: +// - a reference-counted indirection towards a PerformanceGroup shared +// by several compartments +// - a owned PerformanceGroup representing the performance of a single +// compartment. // struct PerformanceGroupHolder { - // Get the group. + // Get the shared group. // On first call, this causes a single Hashtable lookup. // Successive calls do not require further lookups. - js::PerformanceGroup* getGroup(JSContext*); + js::PerformanceGroup* getSharedGroup(JSContext*); - // `true` if the this holder is currently associated to a + // Get the own group. + js::PerformanceGroup* getOwnGroup(JSContext*); + + // `true` if the this holder is currently associated to a shared // PerformanceGroup, `false` otherwise. Use this method to avoid // instantiating a PerformanceGroup if you only need to get // available performance data. - inline bool isLinked() const { - return group_ != nullptr; + inline bool hasSharedGroup() const { + return sharedGroup_ != nullptr; + } + inline bool hasOwnGroup() const { + return ownGroup_ != nullptr; } // Remove the link to the PerformanceGroup. This method is designed @@ -5564,10 +5573,12 @@ struct PerformanceGroupHolder { explicit PerformanceGroupHolder(JSRuntime* runtime) : runtime_(runtime) - , group_(nullptr) + , sharedGroup_(nullptr) + , ownGroup_(nullptr) { } ~PerformanceGroupHolder(); -private: + + private: // Return the key representing this PerformanceGroup in // Runtime::Stopwatch. // Do not deallocate the key. @@ -5575,10 +5586,11 @@ private: JSRuntime *runtime_; - // The PerformanceGroup held by this object. - // Initially set to `nullptr` until the first cal to `getGroup`. + // The PerformanceGroups held by this object. + // Initially set to `nullptr` until the first call to `getGroup`. // May be reset to `nullptr` by a call to `unlink`. - js::PerformanceGroup* group_; + js::PerformanceGroup* sharedGroup_; + js::PerformanceGroup* ownGroup_; }; /** @@ -5604,6 +5616,10 @@ extern JS_PUBLIC_API(bool) SetStopwatchIsMonitoringJank(JSRuntime*, bool); extern JS_PUBLIC_API(bool) GetStopwatchIsMonitoringJank(JSRuntime*); +extern JS_PUBLIC_API(bool) +SetStopwatchIsMonitoringPerCompartment(JSRuntime*, bool); +extern JS_PUBLIC_API(bool) +GetStopwatchIsMonitoringPerCompartment(JSRuntime*); extern JS_PUBLIC_API(bool) IsStopwatchActive(JSRuntime*); @@ -5615,7 +5631,9 @@ extern JS_PUBLIC_API(PerformanceData*) GetPerformanceData(JSRuntime*); typedef bool -(PerformanceStatsWalker)(JSContext* cx, const PerformanceData& stats, uint64_t uid, void* closure); +(PerformanceStatsWalker)(JSContext* cx, + const PerformanceData& stats, uint64_t uid, + const uint64_t* parentId, void* closure); /** * Extract the performance statistics. diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index e467935df62b..682e12417194 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -372,15 +372,49 @@ ExecuteState::pushInterpreterFrame(JSContext* cx) scopeChain_, type_, evalInFrame_); } namespace js { - // Implementation of per-performance group performance measurement. // // // All mutable state is stored in `Runtime::stopwatch` (per-process // performance stats and logistics) and in `PerformanceGroup` (per // group performance stats). -struct AutoStopwatch final +class AutoStopwatch final { + // The context with which this object was initialized. + // Non-null. + JSContext* const cx_; + + // An indication of the number of times we have entered the event + // loop. Used only for comparison. + uint64_t iteration_; + + // `true` if this object is currently used to monitor performance + // for a shared PerformanceGroup, `false` otherwise, i.e. if the + // stopwatch mechanism is off or if another stopwatch is already + // in charge of monitoring for the same PerformanceGroup. + bool isMonitoringForGroup_; + + // `true` if this object is currently used to monitor performance + // for a single compartment, `false` otherwise, i.e. if the + // stopwatch mechanism is off or if another stopwatch is already + // in charge of monitoring for the same PerformanceGroup. + bool isMonitoringForSelf_; + + // `true` if this stopwatch is the topmost stopwatch on the stack + // for this event, `false` otherwise. + bool isMonitoringForTop_; + + // `true` if we are monitoring jank, `false` otherwise. + bool isMonitoringJank_; + // `true` if we are monitoring CPOW, `false` otherwise. + bool isMonitoringCPOW_; + + // Timestamps captured while starting the stopwatch. + uint64_t userTimeStart_; + uint64_t systemTimeStart_; + uint64_t CPOWTimeStart_; + + public: // If the stopwatch is active, constructing an instance of // AutoStopwatch causes it to become the current owner of the // stopwatch. @@ -389,120 +423,198 @@ struct AutoStopwatch final explicit inline AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : cx_(cx) , iteration_(0) - , isActive_(false) - , isTop_(false) + , isMonitoringForGroup_(false) + , isMonitoringForSelf_(false) + , isMonitoringForTop_(false) + , isMonitoringJank_(false) + , isMonitoringCPOW_(false) , userTimeStart_(0) , systemTimeStart_(0) , CPOWTimeStart_(0) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; - JSRuntime* runtime = JS_GetRuntime(cx_); - if (!runtime->stopwatch.isMonitoringJank()) - return; - JSCompartment* compartment = cx_->compartment(); if (compartment->scheduledForDestruction) return; + JSRuntime* runtime = cx_->runtime(); iteration_ = runtime->stopwatch.iteration; - PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx); - MOZ_ASSERT(group); - - if (group->hasStopwatch(iteration_)) { - // Someone is already monitoring this group during this - // tick, no need for further monitoring. + PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx); + if (!sharedGroup) { + // Either this Runtime is not configured for Performance Monitoring, or we couldn't + // allocate the group, or there was a problem with the hashtable. return; } - // Start the stopwatch. - if (!this->getTimes(runtime, &userTimeStart_, &systemTimeStart_)) - return; - isActive_ = true; - CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime; + if (!sharedGroup->hasStopwatch(iteration_)) { + // We are now in charge of monitoring this group for the tick, + // until destruction of `this` or until we enter a nested event + // loop and `iteration_` is incremented. + sharedGroup->acquireStopwatch(iteration_, this); + isMonitoringForGroup_ = true; + } - // We are now in charge of monitoring this group for the tick, - // until destruction of `this` or until we enter a nested event - // loop and `iteration_` is incremented. - group->acquireStopwatch(iteration_, this); + PerformanceGroup* ownGroup = nullptr; + if (runtime->stopwatch.isMonitoringPerCompartment()) { + // As above, but for the group representing just this compartment. + ownGroup = compartment->performanceMonitoring.getOwnGroup(cx); + if (!ownGroup->hasStopwatch(iteration_)) { + ownGroup->acquireStopwatch(iteration_, this); + isMonitoringForSelf_ = true; + } + } if (runtime->stopwatch.isEmpty) { // This is the topmost stopwatch on the stack. // It will be in charge of updating the per-process // performance data. runtime->stopwatch.isEmpty = false; - runtime->stopwatch.performance.ticks++; - isTop_ = true; + isMonitoringForTop_ = true; + + MOZ_ASSERT(isMonitoringForGroup_); } - } - inline ~AutoStopwatch() { - if (!isActive_) { + + if (!isMonitoringForGroup_ && !isMonitoringForSelf_) { // We are not in charge of monitoring anything. + // (isMonitoringForTop_ implies isMonitoringForGroup_, + // so we do not need to check it) + return; + } + + enter(); + } + ~AutoStopwatch() { + if (!isMonitoringForGroup_ && !isMonitoringForSelf_) { + // We are not in charge of monitoring anything. + // (isMonitoringForTop_ implies isMonitoringForGroup_, + // so we do not need to check it) return; } - JSRuntime* runtime = JS_GetRuntime(cx_); JSCompartment* compartment = cx_->compartment(); - - MOZ_ASSERT(!compartment->scheduledForDestruction); - - if (!runtime->stopwatch.isMonitoringJank()) { - // Monitoring has been stopped while we were - // executing the code. Drop everything. + if (compartment->scheduledForDestruction) return; - } + JSRuntime* runtime = cx_->runtime(); if (iteration_ != runtime->stopwatch.iteration) { // We have entered a nested event loop at some point. // Any information we may have is obsolete. return; } - PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx_); - MOZ_ASSERT(group); + // Finish and commit measures + exit(); - // Compute time spent. - group->releaseStopwatch(iteration_, this); - uint64_t userTimeEnd, systemTimeEnd; - if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd)) - return; + // Now release groups. + if (isMonitoringForGroup_) { + PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_); + MOZ_ASSERT(sharedGroup); + sharedGroup->releaseStopwatch(iteration_, this); + } - uint64_t userTimeDelta = userTimeEnd - userTimeStart_; - uint64_t systemTimeDelta = systemTimeEnd - systemTimeStart_; - uint64_t CPOWTimeDelta = runtime->stopwatch.performance.totalCPOWTime - CPOWTimeStart_; - group->data.totalUserTime += userTimeDelta; - group->data.totalSystemTime += systemTimeDelta; - group->data.totalCPOWTime += CPOWTimeDelta; + if (isMonitoringForSelf_) { + PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx_); + MOZ_ASSERT(ownGroup); + ownGroup->releaseStopwatch(iteration_, this); + } - uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta; - updateDurations(totalTimeDelta, group->data.durations); - group->data.ticks++; - - if (isTop_) { - // This is the topmost stopwatch on the stack. - // Record the timing information. - runtime->stopwatch.performance.totalUserTime = userTimeEnd; - runtime->stopwatch.performance.totalSystemTime = systemTimeEnd; - updateDurations(totalTimeDelta, runtime->stopwatch.performance.durations); + if (isMonitoringForTop_) runtime->stopwatch.isEmpty = true; + } + private: + void enter() { + JSRuntime* runtime = cx_->runtime(); + + if (runtime->stopwatch.isMonitoringCPOW()) { + CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime; + isMonitoringCPOW_ = true; + } + + if (runtime->stopwatch.isMonitoringJank()) { + if (this->getTimes(runtime, &userTimeStart_, &systemTimeStart_)) { + isMonitoringJank_ = true; + } + } + + } + + void exit() { + JSRuntime* runtime = cx_->runtime(); + + uint64_t userTimeDelta = 0; + uint64_t systemTimeDelta = 0; + if (isMonitoringJank_ && runtime->stopwatch.isMonitoringJank()) { + // We were monitoring jank when we entered and we still are. + uint64_t userTimeEnd, systemTimeEnd; + if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd)) { + // We make no attempt to recover from this error. If + // we bail out here, we lose nothing of value, plus + // I'm nearly sure that this error cannot happen in + // practice. + return; + } + userTimeDelta = userTimeEnd - userTimeStart_; + systemTimeDelta = systemTimeEnd - systemTimeStart_; + } + + uint64_t CPOWTimeDelta = 0; + if (isMonitoringCPOW_ && runtime->stopwatch.isMonitoringCPOW()) { + // We were monitoring CPOW when we entered and we still are. + CPOWTimeDelta = runtime->stopwatch.performance.totalCPOWTime - CPOWTimeStart_; + + } + commitDeltasToGroups(userTimeDelta, systemTimeDelta, CPOWTimeDelta); + } + + void commitDeltasToGroups(uint64_t userTimeDelta, + uint64_t systemTimeDelta, + uint64_t CPOWTimeDelta) + { + JSCompartment* compartment = cx_->compartment(); + + PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_); + MOZ_ASSERT(sharedGroup); + applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, sharedGroup->data); + + if (isMonitoringForSelf_) { + PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx_); + MOZ_ASSERT(ownGroup); + applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, ownGroup->data); + } + + if (isMonitoringForTop_) { + JSRuntime* runtime = cx_->runtime(); + applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, runtime->stopwatch.performance); } } - private: - // Update an array containing the number of times we have missed - // at least 2^0 successive ms, 2^1 successive ms, ... - // 2^i successive ms. - template - void updateDurations(uint64_t totalTimeDelta, uint64_t (&array)[N]) const { + void applyDeltas(uint64_t userTimeDelta, + uint64_t systemTimeDelta, + uint64_t CPOWTimeDelta, + PerformanceData& data) const { + + data.ticks++; + + uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta; + data.totalUserTime += userTimeDelta; + data.totalSystemTime += systemTimeDelta; + data.totalCPOWTime += CPOWTimeDelta; + + // Update an array containing the number of times we have missed + // at least 2^0 successive ms, 2^1 successive ms, ... + // 2^i successive ms. + // Duration of one frame, i.e. 16ms in museconds size_t i = 0; uint64_t duration = 1000; for (i = 0, duration = 1000; - i < N && duration < totalTimeDelta; - ++i, duration *= 2) { - array[i]++; + i < ArrayLength(data.durations) && duration < totalTimeDelta; + ++i, duration *= 2) + { + data.durations[i]++; } } @@ -585,31 +697,9 @@ struct AutoStopwatch final return true; } - private: - // The context with which this object was initialized. - // Non-null. - JSContext* const cx_; - // An indication of the number of times we have entered the event - // loop. Used only for comparison. - uint64_t iteration_; - - // `true` if this object is currently used to monitor performance, - // `false` otherwise, i.e. if the stopwatch mechanism is off or if - // another stopwatch is already in charge of monitoring for the - // same PerformanceGroup. - bool isActive_; - - // `true` if this stopwatch is the topmost stopwatch on the stack - // for this event, `false` otherwise. - bool isTop_; - - // Timestamps captured while starting the stopwatch. - uint64_t userTimeStart_; - uint64_t systemTimeStart_; - uint64_t CPOWTimeStart_; - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +private: + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; }; } // namespace js diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 15e7c7b93019..522835c32092 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -899,6 +899,17 @@ js::GetStopwatchIsMonitoringCPOW(JSRuntime* rt) return rt->stopwatch.isMonitoringCPOW(); } +bool +js::SetStopwatchIsMonitoringPerCompartment(JSRuntime* rt, bool value) +{ + return rt->stopwatch.setIsMonitoringPerCompartment(value); +} +bool +js::GetStopwatchIsMonitoringPerCompartment(JSRuntime* rt) +{ + return rt->stopwatch.isMonitoringPerCompartment(); +} + js::PerformanceGroupHolder::~PerformanceGroupHolder() { unlink(); @@ -918,13 +929,18 @@ js::PerformanceGroupHolder::getHashKey(JSContext* cx) void js::PerformanceGroupHolder::unlink() { - if (!group_) { + if (ownGroup_) { + js_delete(ownGroup_); + ownGroup_ = nullptr; + } + + if (!sharedGroup_) { // The group has never been instantiated. return; } - js::PerformanceGroup* group = group_; - group_ = nullptr; + js::PerformanceGroup* group = sharedGroup_; + sharedGroup_ = nullptr; if (group->decRefCount() > 0) { // The group has at least another owner. @@ -933,32 +949,51 @@ js::PerformanceGroupHolder::unlink() JSRuntime::Stopwatch::Groups::Ptr ptr = - runtime_->stopwatch.groups_.lookup(group->key_); + runtime_->stopwatch.groups().lookup(group->key_); MOZ_ASSERT(ptr); - runtime_->stopwatch.groups_.remove(ptr); + runtime_->stopwatch.groups().remove(ptr); js_delete(group); } PerformanceGroup* -js::PerformanceGroupHolder::getGroup(JSContext* cx) +js::PerformanceGroupHolder::getOwnGroup(JSContext* cx) { - if (group_) - return group_; + if (ownGroup_) + return ownGroup_; + + ownGroup_ = runtime_->new_(cx, nullptr); + return ownGroup_; +} + +PerformanceGroup* +js::PerformanceGroupHolder::getSharedGroup(JSContext* cx) +{ + if (sharedGroup_) + return sharedGroup_; + + if (!runtime_->stopwatch.groups().initialized()) + return nullptr; void* key = getHashKey(cx); - JSRuntime::Stopwatch::Groups::AddPtr ptr = - runtime_->stopwatch.groups_.lookupForAdd(key); + JSRuntime::Stopwatch::Groups::AddPtr ptr = runtime_->stopwatch.groups().lookupForAdd(key); if (ptr) { - group_ = ptr->value(); - MOZ_ASSERT(group_); + sharedGroup_ = ptr->value(); + MOZ_ASSERT(sharedGroup_); } else { - group_ = runtime_->new_(cx, key); - runtime_->stopwatch.groups_.add(ptr, key, group_); + sharedGroup_ = runtime_->new_(cx, key); + if (!sharedGroup_) + return nullptr; + + if (!runtime_->stopwatch.groups().add(ptr, key, sharedGroup_)) { + js_delete(sharedGroup_); + sharedGroup_ = nullptr; + return nullptr; + } } - group_->incRefCount(); + sharedGroup_->incRefCount(); - return group_; + return sharedGroup_; } PerformanceData* diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 0e72361fd48e..36f9375c5f30 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1483,6 +1483,29 @@ struct JSRuntime : public JS::shadow::Runtime, Performance measurements ------------------------------------------ */ struct Stopwatch { + /** + * A map used to collapse compartments belonging to the same + * add-on (respectively to the same webpage, to the platform) + * into a single group. + * + * Keys: for system compartments, a `JSAddonId*` (which may be + * `nullptr`), and for webpages, a `JSPrincipals*` (which may + * not). Note that compartments may start as non-system + * compartments and become compartments later during their + * lifetime, which requires an invalidation. + * + * This map is meant to be accessed only by instances of + * PerformanceGroupHolder, which handle both reference-counting + * of the values and invalidation of the key/value pairs. + */ + typedef js::HashMap, + js::SystemAllocPolicy> Groups; + + Groups& groups() { + return groups_; + } + /** * The number of times we have entered the event loop. * Used to reset counters whenever we enter the loop, @@ -1523,6 +1546,7 @@ struct JSRuntime : public JS::shadow::Runtime, , currentPerfGroupCallback(nullptr) , isMonitoringJank_(false) , isMonitoringCPOW_(false) + , isMonitoringPerCompartment_(false) , idCounter_(0) { } @@ -1564,6 +1588,21 @@ struct JSRuntime : public JS::shadow::Runtime, return isMonitoringJank_; } + bool setIsMonitoringPerCompartment(bool value) { + if (isMonitoringPerCompartment_ != value) + reset(); + + if (value && !groups_.initialized()) { + if (!groups_.init(128)) + return false; + } + + isMonitoringPerCompartment_ = value; + return true; + } + bool isMonitoringPerCompartment() const { + return isMonitoringPerCompartment_; + } /** * Activate/deactivate stopwatch measurement of CPOW. @@ -1607,25 +1646,6 @@ struct JSRuntime : public JS::shadow::Runtime, MonotonicTimeStamp userTimeFix; private: - /** - * A map used to collapse compartments belonging to the same - * add-on (respectively to the same webpage, to the platform) - * into a single group. - * - * Keys: for system compartments, a `JSAddonId*` (which may be - * `nullptr`), and for webpages, a `JSPrincipals*` (which may - * not). Note that compartments may start as non-system - * compartments and become compartments later during their - * lifetime, which requires an invalidation. - * - * This map is meant to be accessed only by instances of - * PerformanceGroupHolder, which handle both reference-counting - * of the values and invalidation of the key/value pairs. - */ - typedef js::HashMap, - js::SystemAllocPolicy> Groups; - Groups groups_; friend struct js::PerformanceGroupHolder; @@ -1634,6 +1654,7 @@ struct JSRuntime : public JS::shadow::Runtime, */ bool isMonitoringJank_; bool isMonitoringCPOW_; + bool isMonitoringPerCompartment_; /** * A counter used to generate unique identifiers for groups. diff --git a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp index 960b238ab045..0d94913caf20 100644 --- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp +++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp @@ -234,6 +234,24 @@ nsPerformanceSnapshot::GetWindowData(JSContext* cx, doc->GetTitle(title); *windowId = ptop->WindowID(); + + *memoryUsed = -1; + int64_t jsObjectsSize; + int64_t jsStringsSize; + int64_t jsOtherSize; + int64_t domSize; + int64_t styleSize; + int64_t otherSize; + double jsMilliseconds; + double nonJSMilliseconds; + nsCOMPtr manager = nsMemoryReporterManager::GetOrCreate(); + if (!manager) { + return; + } + nsresult rv = manager->GetSizeOfTab(top, &jsObjectsSize, &jsStringsSize, &jsOtherSize, &domSize, &styleSize, memoryUsed); + if (NS_FAILED(rv)) { + *memoryUsed = -1; + } } /* static */ void From 9b23c4163e3cfa72df118db70d0442fca0325913 Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Fri, 12 Jun 2015 21:52:06 +0200 Subject: [PATCH 06/18] Bug 1147664 - Detailed mode for PerformanceStats (high-level). r=felipe --HG-- extra : rebase_source : 06488b342c9f672820f7d8303a05746e8f436364 --- .../perfmonitoring/PerformanceStats.jsm | 81 +++++++++-- .../perfmonitoring/nsIPerformanceStats.idl | 23 +++- .../perfmonitoring/nsPerformanceStats.cpp | 129 +++++++++++++----- .../tests/browser/browser_compartments.js | 26 +++- .../tests/xpcshell/test_compartments.js | 51 +++++-- 5 files changed, 250 insertions(+), 60 deletions(-) diff --git a/toolkit/components/perfmonitoring/PerformanceStats.jsm b/toolkit/components/perfmonitoring/PerformanceStats.jsm index 6b2ca3e57db2..776ba7b70c8f 100644 --- a/toolkit/components/perfmonitoring/PerformanceStats.jsm +++ b/toolkit/components/perfmonitoring/PerformanceStats.jsm @@ -146,6 +146,16 @@ Probe.prototype = { return this._impl.subtract(a, b); }, + importChildCompartments: function(parent, children) { + if (!Array.isArray(children)) { + throw new TypeError(); + } + if (!parent || !(parent instanceof PerformanceData)) { + throw new TypeError(); + } + return this._impl.importChildCompartments(parent, children); + }, + /** * The name of the probe. */ @@ -228,7 +238,8 @@ let Probes = { } result.longestDuration = lastNonZero(result.durations); return result; - } + }, + importChildCompartments: function() { /* nothing to do */ }, }), /** @@ -258,7 +269,8 @@ let Probes = { return { totalCPOWTime: a.totalCPOWTime - b.totalCPOWTime }; - } + }, + importChildCompartments: function() { /* nothing to do */ }, }), /** @@ -287,7 +299,8 @@ let Probes = { return { ticks: a.ticks - b.ticks }; - } + }, + importChildCompartments: function() { /* nothing to do */ }, }), "jank-content": new Probe("jank-content", { @@ -311,7 +324,8 @@ let Probes = { }, subtract: function(a, b) { return null; - } + }, + importChildCompartments: function() { /* nothing to do */ }, }), "cpow-content": new Probe("cpow-content", { @@ -335,15 +349,22 @@ let Probes = { }, subtract: function(a, b) { return null; - } + }, + importChildCompartments: function() { /* nothing to do */ }, }), "ticks-content": new Probe("ticks-content", { + _isActive: false, set isActive(x) { - // Ignore: This probe is always active. + this._isActive = x; + if (x) { + Process.broadcast("acquire", ["ticks"]); + } else { + Process.broadcast("release", ["ticks"]); + } }, get isActive() { - return true; + return this._isActive; }, extract: function(xpcom) { return {}; @@ -353,8 +374,30 @@ let Probes = { }, subtract: function(a, b) { return null; - } + }, + importChildCompartments: function() { /* nothing to do */ }, }), + + compartments: new Probe("compartments", { + set isActive(x) { + performanceStatsService.isMonitoringPerCompartment = x; + }, + get isActive() { + return performanceStatsService.isMonitoringPerCompartment; + }, + extract: function(xpcom) { + return null; + }, + isEqual: function(a, b) { + return true; + }, + subtract: function(a, b) { + return true; + }, + importChildCompartments: function(parent, children) { + parent.children = children; + }, + }) }; @@ -657,16 +700,32 @@ function PerformanceDiff(current, old = null) { function Snapshot({xpcom, childProcesses, probes}) { this.componentsData = []; - // Parent process + // Current process if (xpcom) { + let children = new Map(); let enumeration = xpcom.getComponentsData().enumerate(); while (enumeration.hasMoreElements()) { let xpcom = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats); - this.componentsData.push(new PerformanceData({xpcom, probes})); + let stat = new PerformanceData({xpcom, probes}); + if (!stat.parentId) { + this.componentsData.push(stat); + } else { + let siblings = children.get(stat.parentId); + if (!siblings) { + children.set(stat.parentId, (siblings = [])); + } + siblings.push(stat); + } + } + + for (let parent of this.componentsData) { + for (let probe of probes) { + probe.importChildCompartments(parent, children.get(parent.groupId) || []); + } } } - // Content processes + // Child processes if (childProcesses) { for (let {componentsData} of childProcesses) { // We are only interested in `componentsData` for the time being. diff --git a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl index 272178e253d5..dc1d11ba84a0 100644 --- a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl +++ b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl @@ -17,12 +17,12 @@ /** * Snapshot of the performance of a component, e.g. an add-on, a web - * page, system built-ins, or the entire process itself. + * page, system built-ins, a module or the entire process itself. * * All values are monotonic and are updated only when * `nsIPerformanceStatsService.isStopwatchActive` is `true`. */ -[scriptable, uuid(47f8d36d-1d67-43cb-befd-d2f4720ac568)] +[scriptable, uuid(1bc2d016-e9ae-4186-97c6-9478eddda245)] interface nsIPerformanceStats: nsISupports { /** * An identifier unique to the component. @@ -32,6 +32,16 @@ interface nsIPerformanceStats: nsISupports { */ readonly attribute AString groupId; + /** + * If this component is part of a larger component, the larger + * component. Otherwise, null. + * + * As of this writing, there can be at most two levels of components: + * - compartments (a single module, iframe, etc.); + * - groups (an entire add-on, an entire webpage, etc.). + */ + readonly attribute AString parentId; + /** * The name of the component: * - for the process itself, ""; @@ -112,7 +122,7 @@ interface nsIPerformanceSnapshot: nsISupports { nsIPerformanceStats getProcessData(); }; -[scriptable, builtinclass, uuid(0469e6af-95c3-4961-a385-4bc009128939)] +[scriptable, builtinclass, uuid(60973d54-13e2-455c-a3c6-84dea5dfc8b9)] interface nsIPerformanceStatsService : nsISupports { /** * `true` if we should monitor CPOW, `false` otherwise. @@ -124,6 +134,13 @@ interface nsIPerformanceStatsService : nsISupports { */ [implicit_jscontext] attribute bool isMonitoringJank; + /** + * `true` if all compartments need to be monitored individually, + * `false` if only performance groups (i.e. entire add-ons, entire + * webpages, etc.) need to be monitored. + */ + [implicit_jscontext] attribute bool isMonitoringPerCompartment; + /** * Capture a snapshot of the performance data. */ diff --git a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp index 0d94913caf20..16ddfe28ae31 100644 --- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp +++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp @@ -30,6 +30,7 @@ class nsPerformanceStats: public nsIPerformanceStats { public: nsPerformanceStats(const nsAString& aName, + nsIPerformanceStats* aParent, const nsAString& aGroupId, const nsAString& aAddonId, const nsAString& aTitle, @@ -44,6 +45,10 @@ public: , mIsSystem(aIsSystem) , mPerformanceData(aPerformanceData) { + if (aParent) { + mozilla::DebugOnly rv = aParent->GetGroupId(mParentId); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } } explicit nsPerformanceStats() {} @@ -61,6 +66,12 @@ public: return NS_OK; }; + /* readonly attribute AString parentId; */ + NS_IMETHOD GetParentId(nsAString& aParentId) override { + aParentId.Assign(mParentId); + return NS_OK; + }; + /* readonly attribute AString addonId; */ NS_IMETHOD GetAddonId(nsAString& aAddonId) override { aAddonId.Assign(mAddonId); @@ -124,6 +135,7 @@ public: private: nsString mName; + nsString mParentId; nsString mGroupId; nsString mAddonId; nsString mTitle; @@ -159,13 +171,18 @@ private: * entire process, rather than the statistics for a specific set of * compartments. */ - already_AddRefed ImportStats(JSContext* cx, const js::PerformanceData& data, uint64_t uid); + already_AddRefed ImportStats(JSContext* cx, const js::PerformanceData& data, uint64_t uid, nsIPerformanceStats* parent); /** * Callbacks for iterating through the `PerformanceStats` of a runtime. */ - bool IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats, uint64_t uid); - static bool IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, uint64_t uid, void* self); + bool IterPerformanceStatsCallbackInternal(JSContext* cx, + const js::PerformanceData& ownStats, const uint64_t ownId, + const uint64_t* parentId); + static bool IterPerformanceStatsCallback(JSContext* cx, + const js::PerformanceData& ownStats, const uint64_t ownId, + const uint64_t* parentId, + void* self); // If the context represents a window, extract the title and window ID. // Otherwise, extract "" and 0. @@ -175,6 +192,11 @@ private: void GetGroupId(JSContext*, uint64_t uid, nsString& groupId); + + static void GetName(JSContext*, + JS::Handle global, + nsString& name); + // If the context presents an add-on, extract the addon ID. // Otherwise, extract "". static void GetAddonId(JSContext*, @@ -188,6 +210,7 @@ private: private: nsCOMArray mComponentsData; nsCOMPtr mProcessData; + nsBaseHashtable, nsCOMPtr > mCachedStats; uint64_t mProcessId; }; @@ -234,24 +257,6 @@ nsPerformanceSnapshot::GetWindowData(JSContext* cx, doc->GetTitle(title); *windowId = ptop->WindowID(); - - *memoryUsed = -1; - int64_t jsObjectsSize; - int64_t jsStringsSize; - int64_t jsOtherSize; - int64_t domSize; - int64_t styleSize; - int64_t otherSize; - double jsMilliseconds; - double nonJSMilliseconds; - nsCOMPtr manager = nsMemoryReporterManager::GetOrCreate(); - if (!manager) { - return; - } - nsresult rv = manager->GetSizeOfTab(top, &jsObjectsSize, &jsStringsSize, &jsOtherSize, &domSize, &styleSize, memoryUsed); - if (NS_FAILED(rv)) { - *memoryUsed = -1; - } } /* static */ void @@ -291,8 +296,44 @@ nsPerformanceSnapshot::GetIsSystem(JSContext*, return nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global)); } +/* static */ +void +nsPerformanceSnapshot::GetName(JSContext* cx, + JS::Handle global, + nsString& name) +{ + nsAutoCString cname; + do { + // Attempt to use the URL as name. + nsCOMPtr principal = nsContentUtils::ObjectPrincipal(global); + if (!principal) { + break; + } + + nsCOMPtr uri; + nsresult rv = principal->GetURI(getter_AddRefs(uri)); + if (NS_FAILED(rv) || !uri) { + break; + } + + rv = uri->GetSpec(cname); + if (NS_FAILED(rv)) { + break; + } + + name.Assign(NS_ConvertUTF8toUTF16(cname)); + return; + } while(false); + xpc::GetCurrentCompartmentName(cx, cname); + name.Assign(NS_ConvertUTF8toUTF16(cname)); +} + already_AddRefed -nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance, const uint64_t uid) { +nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance, const uint64_t uid, nsIPerformanceStats* parent) { + if (performance.ticks == 0) { + // Ignore compartments with no activity. + return nullptr; + } JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); if (!global) { @@ -311,28 +352,34 @@ nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& per uint64_t windowId; GetWindowData(cx, title, &windowId); - nsAutoString name; - nsAutoCString cname; - xpc::GetCurrentCompartmentName(cx, cname); - name.Assign(NS_ConvertUTF8toUTF16(cname)); + nsString name; + GetName(cx, global, name); bool isSystem = GetIsSystem(cx, global); nsCOMPtr result = - new nsPerformanceStats(name, groupId, addonId, title, windowId, isSystem, performance); + new nsPerformanceStats(name, parent, groupId, addonId, title, windowId, isSystem, performance); return result.forget(); } /*static*/ bool -nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, const uint64_t uid, void* self) { - return reinterpret_cast(self)->IterPerformanceStatsCallbackInternal(cx, stats, uid); +nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx, + const js::PerformanceData& stats, const uint64_t id, + const uint64_t* parentId, + void* self) { + return reinterpret_cast(self)->IterPerformanceStatsCallbackInternal(cx, stats, id, parentId); } bool -nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats, const uint64_t uid) { - nsCOMPtr result = ImportStats(cx, stats, uid); +nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx, + const js::PerformanceData& stats, const uint64_t id, + const uint64_t* parentId) { + + nsCOMPtr parent = parentId ? mCachedStats.Get(*parentId) : nullptr; + nsCOMPtr result = ImportStats(cx, stats, id, parent); if (result) { mComponentsData.AppendElement(result); + mCachedStats.Put(id, result); } return true; @@ -346,8 +393,12 @@ nsPerformanceSnapshot::Init(JSContext* cx, uint64_t processId) { return NS_ERROR_UNEXPECTED; } + nsAutoString processGroupId; + processGroupId.AssignLiteral("process: "); + processGroupId.AppendInt(processId); mProcessData = new nsPerformanceStats(NS_LITERAL_STRING(""), // name - NS_LITERAL_STRING(""), // group id + nullptr, // parent + processGroupId, // group id NS_LITERAL_STRING(""), // add-on id NS_LITERAL_STRING(""), // title 0, // window id @@ -423,6 +474,20 @@ NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringJank(JSContext* cx, bool } return NS_OK; } +NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringPerCompartment(JSContext* cx, bool *aIsStopwatchActive) +{ + JSRuntime *runtime = JS_GetRuntime(cx); + *aIsStopwatchActive = js::GetStopwatchIsMonitoringPerCompartment(runtime); + return NS_OK; +} +NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringPerCompartment(JSContext* cx, bool aIsStopwatchActive) +{ + JSRuntime *runtime = JS_GetRuntime(cx); + if (!js::SetStopwatchIsMonitoringPerCompartment(runtime, aIsStopwatchActive)) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} /* readonly attribute nsIPerformanceSnapshot snapshot; */ NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot) diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js index 360b6a1f9b64..4492a4c55cf2 100644 --- a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js +++ b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js @@ -28,7 +28,7 @@ function frameScript() { getService(Ci.nsIPerformanceStatsService); // Make sure that the stopwatch is now active. - let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks"]); + let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks", "compartments"]); addMessageListener("compartments-test:getStatistics", () => { try { @@ -158,8 +158,10 @@ function monotinicity_tester(source, testName) { ["jank", "totalSystemTime"], ["cpow", "totalCPOWTime"] ]) { - SilentAssert.leq(item[probe][k], snapshot.processData[probe][k], - `Sanity check (${testName}): component has a lower ${k} than process`); + // Note that we cannot expect components data to be always smaller + // than process data, as `getrusage` & co are not monotonic. + SilentAssert.leq(item[probe][k], 2 * snapshot.processData[probe][k], + `Sanity check (${testName}): ${k} of component is not impossibly larger than that of process`); } let key = item.groupId; @@ -169,6 +171,24 @@ function monotinicity_tester(source, testName) { } map.set(key, item); } + for (let item of snapshot.componentsData) { + if (!item.parentId) { + continue; + } + let parent = map.get(item.parentId); + SilentAssert.ok(parent, `The parent exists ${item.parentId}`); + + for (let [probe, k] of [ + ["jank", "totalUserTime"], + ["jank", "totalSystemTime"], + ["cpow", "totalCPOWTime"] + ]) { + // Note that we cannot expect components data to be always smaller + // than parent data, as `getrusage` & co are not monotonic. + SilentAssert.leq(item[probe][k], 2 * parent[probe][k], + `Sanity check (${testName}): ${k} of component is not impossibly larger than that of parent`); + } + } for (let [key, item] of map) { sanityCheck(previous.componentsMap.get(key), item); previous.componentsMap.set(key, item); diff --git a/toolkit/components/perfmonitoring/tests/xpcshell/test_compartments.js b/toolkit/components/perfmonitoring/tests/xpcshell/test_compartments.js index 44a10a08ba3c..f4c7d562f38b 100644 --- a/toolkit/components/perfmonitoring/tests/xpcshell/test_compartments.js +++ b/toolkit/components/perfmonitoring/tests/xpcshell/test_compartments.js @@ -19,10 +19,12 @@ let promiseStatistics = Task.async(function*(name) { let componentsData = []; let componentsEnum = snapshot.getComponentsData().enumerate(); while (componentsEnum.hasMoreElements()) { - componentsData.push(componentsEnum.getNext().QueryInterface(Ci.nsIPerformanceStats)); + let data = componentsEnum.getNext().QueryInterface(Ci.nsIPerformanceStats); + let normalized = JSON.parse(JSON.stringify(data)); + componentsData.push(data); } return { - processData: snapshot.getProcessData(), + processData: JSON.parse(JSON.stringify(snapshot.getProcessData())), componentsData }; }); @@ -35,11 +37,19 @@ let promiseSetMonitoring = Task.async(function*(to) { yield Promise.resolve(); }); +let promiseSetPerCompartment = Task.async(function*(to) { + let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"]. + getService(Ci.nsIPerformanceStatsService); + service.isMonitoringPerCompartment = to; + yield Promise.resolve(); +}); + function getBuiltinStatistics(name, snapshot) { let stats = snapshot.componentsData.find(stats => stats.isSystem && !stats.addonId ); do_print(`Built-in statistics for ${name} were ${stats?"":"not "}found`); + do_print(JSON.stringify(snapshot.componentsData, null, "\t")); return stats; } @@ -57,17 +67,25 @@ function burnCPU(ms) { } function ensureEquals(snap1, snap2, name) { - Assert.equal( - JSON.stringify(snap1.processData), - JSON.stringify(snap2.processData), - "Same process data: " + name); + for (let k of Object.keys(snap1.processData)) { + if (k == "ticks") { + // Ticks monitoring cannot be deactivated + continue; + } + Assert.equal(snap1.processData[k], snap2.processData[k], `Same process data value ${k} (${name})`) + } let stats1 = snap1.componentsData.sort((a, b) => a.name <= b.name); let stats2 = snap2.componentsData.sort((a, b) => a.name <= b.name); - Assert.equal( - JSON.stringify(stats1), - JSON.stringify(stats2), - "Same components data: " + name - ); + Assert.equal(stats1.length, stats2.length, `Same number of components (${name})`); + for (let i = 0; i < stats1.length; ++i) { + for (let k of Object.keys(stats1[i])) { + if (k == "ticks") { + // Ticks monitoring cannot be deactivated + continue; + } + Assert.equal(stats1[i][k], stats1[i][k], `Same component data value ${i} ${k} (${name})`) + } + } } function hasLowPrecision() { @@ -88,6 +106,7 @@ function hasLowPrecision() { add_task(function* test_measure() { let skipPrecisionTests = hasLowPrecision(); + yield promiseSetPerCompartment(false); do_print("Burn CPU without the stopwatch"); yield promiseSetMonitoring(false); @@ -137,4 +156,14 @@ add_task(function* test_measure() { Assert.equal(builtin2.totalCPOWTime, builtin1.totalCPOWTime, "No CPOW for built-in statistics"); Assert.equal(builtin4.totalUserTime, builtin3.totalUserTime, "After deactivating the stopwatch, we didn't count any time for the built-in"); Assert.equal(builtin4.totalCPOWTime, builtin3.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time for the built-in"); + + // Ideally, we should be able to look for test_compartments.js, but + // it doesn't have its own compartment. + for (let stats of [stats1, stats2, stats3, stats4]) { + Assert.ok(!stats.componentsData.find(x => x.name.includes("Task.jsm")), "At this stage, Task.jsm doesn't show up in the components data"); + } + yield promiseSetPerCompartment(true); + burnCPU(300); + let stats5 = yield promiseStatistics("With per-compartment monitoring"); + Assert.ok(stats5.componentsData.find(x => x.name.includes("Task.jsm")), "With per-compartment monitoring, test_compartments.js shows up"); }); From 71319ef830cad44f2a79c3d28a3d0800e47325b9 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Thu, 23 Jul 2015 13:36:33 -0700 Subject: [PATCH 07/18] Bug 1184192 - Prevent unnecessary hideBoxModel request r=bgrins --- browser/devtools/framework/toolbox-highlighter-utils.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/browser/devtools/framework/toolbox-highlighter-utils.js b/browser/devtools/framework/toolbox-highlighter-utils.js index e418d2f1d0db..4b96011a9ef5 100644 --- a/browser/devtools/framework/toolbox-highlighter-utils.js +++ b/browser/devtools/framework/toolbox-highlighter-utils.js @@ -44,6 +44,10 @@ exports.getHighlighterUtils = function(toolbox) { // Is the highlighter currently in pick mode let isPicking = false; + // Is the box model already displayed, used to prevent dispatching + // unnecessary requests, especially during toolbox shutdown + let isNodeFrontHighlighted = false; + /** * Release this utils, nullifying the references to the toolbox */ @@ -203,6 +207,7 @@ exports.getHighlighterUtils = function(toolbox) { return; } + isNodeFrontHighlighted = true; if (isRemoteHighlightable()) { yield toolbox.highlighter.showBoxModel(nodeFront, options); } else { @@ -256,7 +261,8 @@ exports.getHighlighterUtils = function(toolbox) { // Note that if isRemoteHighlightable is true, there's no need to hide the // highlighter as the walker uses setTimeout to hide it after some time - if (forceHide && toolbox.highlighter && isRemoteHighlightable()) { + if (isNodeFrontHighlighted && forceHide && toolbox.highlighter && isRemoteHighlightable()) { + isNodeFrontHighlighted = false; yield toolbox.highlighter.hideBoxModel(); } From 658f6f15dfe4cfc3d5641ff7d1bf1033d52ba89e Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Thu, 23 Jul 2015 13:36:33 -0700 Subject: [PATCH 08/18] Bug 1184192 - Fix pending showBoxModel request when running browser_markupview_keybindings_04.js. r=bgrins --- .../markupview/test/browser_markupview_keybindings_04.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/browser/devtools/markupview/test/browser_markupview_keybindings_04.js b/browser/devtools/markupview/test/browser_markupview_keybindings_04.js index 98a99bcb1fa4..881363d922cf 100644 --- a/browser/devtools/markupview/test/browser_markupview_keybindings_04.js +++ b/browser/devtools/markupview/test/browser_markupview_keybindings_04.js @@ -12,7 +12,7 @@ const TEST_URL = "data:text/html;charset=utf8,
test element
"; add_task(function*() { - let {inspector} = yield addTab(TEST_URL).then(openInspector); + let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector); info("Select the test node with the browser ctx menu"); yield selectWithBrowserMenu(inspector); @@ -20,8 +20,10 @@ add_task(function*() { info("Press arrowUp to focus " + "(which works if the node was focused properly)"); + let onNodeHighlighted = toolbox.once("node-highlight"); EventUtils.synthesizeKey("VK_UP", {}); yield waitForChildrenUpdated(inspector); + yield onNodeHighlighted; assertNodeSelected(inspector, "body"); info("Select the test node with the element picker"); @@ -30,8 +32,10 @@ add_task(function*() { info("Press arrowUp to focus " + "(which works if the node was focused properly)"); + onNodeHighlighted = toolbox.once("node-highlight"); EventUtils.synthesizeKey("VK_UP", {}); yield waitForChildrenUpdated(inspector); + yield onNodeHighlighted; assertNodeSelected(inspector, "body"); }); From f8675f433b810d031cb6c7e714d3919a64831ec4 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Thu, 23 Jul 2015 13:36:34 -0700 Subject: [PATCH 09/18] Bug 1184192 - Fix pending request issue while running browser_ruleview_edit-selector_03.js r=bgrins --- browser/devtools/styleinspector/rule-view.js | 3 +++ .../styleinspector/test/browser_ruleview_edit-selector_03.js | 2 ++ 2 files changed, 5 insertions(+) diff --git a/browser/devtools/styleinspector/rule-view.js b/browser/devtools/styleinspector/rule-view.js index 67809a6a6167..ac6853cf671d 100644 --- a/browser/devtools/styleinspector/rule-view.js +++ b/browser/devtools/styleinspector/rule-view.js @@ -2744,6 +2744,9 @@ RuleEditor.prototype = { let {ruleProps, isMatching} = response; if (!ruleProps) { + // Notify for changes, even when nothing changes, + // just to allow tests being able to track end of this request. + ruleView.emit("ruleview-invalid-selector"); return; } diff --git a/browser/devtools/styleinspector/test/browser_ruleview_edit-selector_03.js b/browser/devtools/styleinspector/test/browser_ruleview_edit-selector_03.js index b824295e03ac..03d047e41c89 100644 --- a/browser/devtools/styleinspector/test/browser_ruleview_edit-selector_03.js +++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-selector_03.js @@ -36,7 +36,9 @@ function* testEditSelector(view, name) { info("Entering a new selector name and committing"); editor.input.value = name; + let onRuleViewChanged = once(view, "ruleview-invalid-selector"); EventUtils.synthesizeKey("VK_RETURN", {}); + yield onRuleViewChanged; is(view._elementStyle.rules.length, 2, "Should have 2 rules."); is(getRuleViewRule(view, name), undefined, From 15d13d435fe1b08880c7a86cf0c49b2008b53d88 Mon Sep 17 00:00:00 2001 From: Brian Grinstead Date: Thu, 23 Jul 2015 13:55:32 -0700 Subject: [PATCH 10/18] Bug 1175858 - Tracking Protection shield should be animated in when content is blocked on the page;r=MattN --HG-- extra : commitid : ALRSvWjuFe7 --- .../content/browser-trackingprotection.js | 10 +- browser/base/content/browser.js | 16 ++- browser/base/content/tabbrowser.xml | 5 +- browser/base/content/test/general/browser.ini | 2 + .../test/general/browser_trackingUI_1.js | 3 +- .../test/general/browser_trackingUI_4.js | 111 ++++++++++++++++++ .../identity-block/identity-block.inc.css | 13 +- 7 files changed, 153 insertions(+), 7 deletions(-) create mode 100644 browser/base/content/test/general/browser_trackingUI_4.js diff --git a/browser/base/content/browser-trackingprotection.js b/browser/base/content/browser-trackingprotection.js index 1ef08364d0f7..933c60dd73c0 100644 --- a/browser/base/content/browser-trackingprotection.js +++ b/browser/base/content/browser-trackingprotection.js @@ -53,11 +53,19 @@ let TrackingProtection = { return Services.telemetry.getHistogramById("TRACKING_PROTECTION_EVENTS"); }, - onSecurityChange(state) { + onSecurityChange(state, isSimulated) { if (!this.enabled) { return; } + // Only animate the shield if the event was not fired directly from + // the tabbrowser (due to a browser change). + if (isSimulated) { + this.icon.removeAttribute("animate"); + } else { + this.icon.setAttribute("animate", "true"); + } + let { STATE_BLOCKED_TRACKING_CONTENT, STATE_LOADED_TRACKING_CONTENT } = Ci.nsIWebProgressListener; diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 172808b4e31d..9359a41d6c92 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -4009,7 +4009,7 @@ var XULBrowserWindow = { init: function () { // Initialize the security button's state and tooltip text. var securityUI = gBrowser.securityUI; - this.onSecurityChange(null, null, securityUI.state); + this.onSecurityChange(null, null, securityUI.state, true); }, setJSStatus: function () { @@ -4357,7 +4357,13 @@ var XULBrowserWindow = { _state: null, _lastLocation: null, - onSecurityChange: function (aWebProgress, aRequest, aState) { + // This is called in multiple ways: + // 1. Due to the nsIWebProgressListener.onSecurityChange notification. + // 2. Called by tabbrowser.xml when updating the current browser. + // 3. Called directly during this object's initializations. + // aRequest will be null always in case 2 and 3, and sometimes in case 1 (for + // instance, there won't be a request when STATE_BLOCKED_TRACKING_CONTENT is observed). + onSecurityChange: function (aWebProgress, aRequest, aState, aIsSimulated) { // Don't need to do anything if the data we use to update the UI hasn't // changed let uri = gBrowser.currentURI; @@ -4368,6 +4374,10 @@ var XULBrowserWindow = { this._state = aState; this._lastLocation = spec; + if (typeof(aIsSimulated) != "boolean" && typeof(aIsSimulated) != "undefined") { + throw "onSecurityChange: aIsSimulated receieved an unexpected type"; + } + // aState is defined as a bitmask that may be extended in the future. // We filter out any unknown bits before testing for known values. const wpl = Components.interfaces.nsIWebProgressListener; @@ -4403,7 +4413,7 @@ var XULBrowserWindow = { uri = Services.uriFixup.createExposableURI(uri); } catch (e) {} gIdentityHandler.checkIdentity(this._state, uri); - TrackingProtection.onSecurityChange(this._state); + TrackingProtection.onSecurityChange(this._state, aIsSimulated); }, // simulate all change notifications after switching tabs diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 11364a47c801..327efa54b8c5 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1100,8 +1100,11 @@ false); if (securityUI) { + // Include the true final argument to indicate that this event is + // simulated (instead of being observed by the webProgressListener). this._callProgressListeners(null, "onSecurityChange", - [webProgress, null, securityUI.state], true, false); + [webProgress, null, securityUI.state, true], + true, false); } var listener = this.mTabListeners[this.tabContainer.selectedIndex] || null; diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 01ab61254665..ed0f6c8b7952 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -424,6 +424,8 @@ support-files = benignPage.html [browser_trackingUI_3.js] tags = trackingprotection +[browser_trackingUI_4.js] +tags = trackingprotection support-files = trackingPage.html benignPage.html diff --git a/browser/base/content/test/general/browser_trackingUI_1.js b/browser/base/content/test/general/browser_trackingUI_1.js index ead4b5739cf1..3f10edc451c9 100644 --- a/browser/base/content/test/general/browser_trackingUI_1.js +++ b/browser/base/content/test/general/browser_trackingUI_1.js @@ -33,7 +33,8 @@ function hidden(sel) { let win = browser.ownerGlobal; let el = win.document.querySelector(sel); let display = win.getComputedStyle(el).getPropertyValue("display", null); - return display === "none"; + let opacity = win.getComputedStyle(el).getPropertyValue("opacity", null); + return display === "none" || opacity === "0"; } function clickButton(sel) { diff --git a/browser/base/content/test/general/browser_trackingUI_4.js b/browser/base/content/test/general/browser_trackingUI_4.js new file mode 100644 index 000000000000..d8acb77de5ed --- /dev/null +++ b/browser/base/content/test/general/browser_trackingUI_4.js @@ -0,0 +1,111 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Test that the Tracking Protection icon is properly animated in the identity +// block when loading tabs and switching between tabs. +// See also Bug 1175858. + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; +const PREF = "privacy.trackingprotection.enabled"; +const PB_PREF = "privacy.trackingprotection.pbmode.enabled"; +const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/benignPage.html"; +const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/trackingPage.html"; +let TrackingProtection = null; +let browser = null; + +let {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {}); + +registerCleanupFunction(function() { + TrackingProtection = browser = null; + UrlClassifierTestUtils.cleanupTestTrackers(); + Services.prefs.clearUserPref(PREF); + Services.prefs.clearUserPref(PB_PREF); + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } +}); + +function waitForSecurityChange(numChanges = 1) { + return new Promise(resolve => { + let n = 0; + let listener = { + onSecurityChange: function() { + n = n + 1; + info ("Recieved onSecurityChange event " + n + " of " + numChanges); + if (n >= numChanges) { + browser.removeProgressListener(listener); + resolve(); + } + } + }; + browser.addProgressListener(listener); + }); +} + +function* testTrackingProtectionAnimation() { + info("Load a test page not containing tracking elements"); + let benignTab = yield BrowserTestUtils.openNewForegroundTab(browser, BENIGN_PAGE); + + ok (!TrackingProtection.icon.hasAttribute("state"), "icon: no state"); + ok (TrackingProtection.icon.hasAttribute("animate"), "icon: animate"); + + info("Load a test page containing tracking elements"); + let trackingTab = yield BrowserTestUtils.openNewForegroundTab(browser, TRACKING_PAGE); + + ok (TrackingProtection.icon.hasAttribute("state"), "icon: state"); + ok (TrackingProtection.icon.hasAttribute("animate"), "icon: animate"); + + info("Switch from tracking -> benign tab"); + let securityChanged = waitForSecurityChange(); + browser.selectedTab = benignTab; + yield securityChanged; + + ok (!TrackingProtection.icon.hasAttribute("state"), "icon: no state"); + ok (!TrackingProtection.icon.hasAttribute("animate"), "icon: no animate"); + + info("Switch from benign -> tracking tab"); + securityChanged = waitForSecurityChange(); + browser.selectedTab = trackingTab; + yield securityChanged; + + ok (TrackingProtection.icon.hasAttribute("state"), "icon: state"); + ok (!TrackingProtection.icon.hasAttribute("animate"), "icon: no animate"); + + info("Reload tracking tab"); + securityChanged = waitForSecurityChange(2); + browser.reload(); + yield securityChanged; + + ok (TrackingProtection.icon.hasAttribute("state"), "icon: state"); + ok (TrackingProtection.icon.hasAttribute("animate"), "icon: animate"); +} + +add_task(function* testNormalBrowsing() { + yield UrlClassifierTestUtils.addTestTrackers(); + + browser = gBrowser; + + TrackingProtection = gBrowser.ownerGlobal.TrackingProtection; + ok (TrackingProtection, "TP is attached to the browser window"); + + Services.prefs.setBoolPref(PREF, true); + ok (TrackingProtection.enabled, "TP is enabled after setting the pref"); + + yield testTrackingProtectionAnimation(); +}); + +add_task(function* testPrivateBrowsing() { + let privateWin = yield promiseOpenAndLoadWindow({private: true}, true); + browser = privateWin.gBrowser; + + TrackingProtection = browser.ownerGlobal.TrackingProtection; + ok (TrackingProtection, "TP is attached to the private window"); + + Services.prefs.setBoolPref(PB_PREF, true); + ok (TrackingProtection.enabled, "TP is enabled after setting the pref"); + + yield testTrackingProtectionAnimation(); + + privateWin.close(); +}); diff --git a/browser/themes/shared/identity-block/identity-block.inc.css b/browser/themes/shared/identity-block/identity-block.inc.css index e3d982bbb62f..5ca8eb1b0cc4 100644 --- a/browser/themes/shared/identity-block/identity-block.inc.css +++ b/browser/themes/shared/identity-block/identity-block.inc.css @@ -27,6 +27,7 @@ font-size: .9em; padding: 2px 5px; margin-inline-end: 4px; + overflow: hidden; } #identity-box:hover, @@ -89,14 +90,24 @@ height: 16px; margin-inline-end: 2px; list-style-image: url(chrome://browser/skin/tracking-protection-16.svg); + margin-left: 0; + opacity: 1; } #tracking-protection-icon[state="loaded-tracking-content"] { list-style-image: url(chrome://browser/skin/tracking-protection-disabled-16.svg); } +#tracking-protection-icon[animate] { + transition: margin-left 200ms ease-out; +} + #tracking-protection-icon:not([state]) { - display: none; + margin-left: -16px; + pointer-events: none; + opacity: 0; + /* Only animate the shield in, when it disappears hide it immediately. */ + transition: none; } /* MAIN IDENTITY ICON */ From 59d21a5a5548f2c6531e0af1e72a18ad2cd72314 Mon Sep 17 00:00:00 2001 From: Brian Grinstead Date: Thu, 23 Jul 2015 13:55:34 -0700 Subject: [PATCH 11/18] Bug 1175858 - Style updates for browser tracking protection tests;r=MattN --HG-- extra : commitid : 6cmb6kUpgGr --- .../test/general/browser_trackingUI_1.js | 104 +++++++++--------- .../test/general/browser_trackingUI_2.js | 52 +++++---- .../test/general/browser_trackingUI_3.js | 45 ++++---- .../test/general/browser_trackingUI_4.js | 64 ++++++----- 4 files changed, 126 insertions(+), 139 deletions(-) diff --git a/browser/base/content/test/general/browser_trackingUI_1.js b/browser/base/content/test/general/browser_trackingUI_1.js index 3f10edc451c9..370ac1e51591 100644 --- a/browser/base/content/test/general/browser_trackingUI_1.js +++ b/browser/base/content/test/general/browser_trackingUI_1.js @@ -1,13 +1,11 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// Test that the Tracking Protection section is visible in the Control Center -// and has the correct state for the cases when: -// * A page with no tracking elements is loaded. -// * A page with tracking elements is loaded and they are blocked. -// * A page with tracking elements is loaded and they are not blocked. -// See also Bugs 1175327, 1043801, 1178985 +/* + * Test that the Tracking Protection section is visible in the Control Center + * and has the correct state for the cases when: + * 1) A page with no tracking elements is loaded. + * 2) A page with tracking elements is loaded and they are blocked. + * 3) A page with tracking elements is loaded and they are not blocked. + * See also Bugs 1175327, 1043801, 1178985 + */ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; const PREF = "privacy.trackingprotection.enabled"; @@ -15,12 +13,12 @@ const PB_PREF = "privacy.trackingprotection.pbmode.enabled"; const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/benignPage.html"; const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/trackingPage.html"; let TrackingProtection = null; -let browser = null; +let tabbrowser = null; let {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {}); registerCleanupFunction(function() { - TrackingProtection = browser = null; + TrackingProtection = tabbrowser = null; UrlClassifierTestUtils.cleanupTestTrackers(); Services.prefs.clearUserPref(PREF); Services.prefs.clearUserPref(PB_PREF); @@ -30,7 +28,7 @@ registerCleanupFunction(function() { }); function hidden(sel) { - let win = browser.ownerGlobal; + let win = tabbrowser.ownerGlobal; let el = win.document.querySelector(sel); let display = win.getComputedStyle(el).getPropertyValue("display", null); let opacity = win.getComputedStyle(el).getPropertyValue("opacity", null); @@ -38,37 +36,37 @@ function hidden(sel) { } function clickButton(sel) { - let win = browser.ownerGlobal; + let win = tabbrowser.ownerGlobal; let el = win.document.querySelector(sel); el.doCommand(); } function testBenignPage() { info("Non-tracking content must not be blocked"); - ok (!TrackingProtection.container.hidden, "The container is visible"); - ok (!TrackingProtection.content.hasAttribute("state"), "content: no state"); - ok (!TrackingProtection.icon.hasAttribute("state"), "icon: no state"); + ok(!TrackingProtection.container.hidden, "The container is visible"); + ok(!TrackingProtection.content.hasAttribute("state"), "content: no state"); + ok(!TrackingProtection.icon.hasAttribute("state"), "icon: no state"); - ok (hidden("#tracking-protection-icon"), "icon is hidden"); - ok (hidden("#tracking-action-block"), "blockButton is hidden"); - ok (hidden("#tracking-action-unblock"), "unblockButton is hidden"); + ok(hidden("#tracking-protection-icon"), "icon is hidden"); + ok(hidden("#tracking-action-block"), "blockButton is hidden"); + ok(hidden("#tracking-action-unblock"), "unblockButton is hidden"); // Make sure that the no tracking elements message appears - ok (!hidden("#tracking-not-detected"), "labelNoTracking is visible"); - ok (hidden("#tracking-loaded"), "labelTrackingLoaded is hidden"); - ok (hidden("#tracking-blocked"), "labelTrackingBlocked is hidden"); + ok(!hidden("#tracking-not-detected"), "labelNoTracking is visible"); + ok(hidden("#tracking-loaded"), "labelTrackingLoaded is hidden"); + ok(hidden("#tracking-blocked"), "labelTrackingBlocked is hidden"); } function testTrackingPage(window) { info("Tracking content must be blocked"); - ok (!TrackingProtection.container.hidden, "The container is visible"); - is (TrackingProtection.content.getAttribute("state"), "blocked-tracking-content", + ok(!TrackingProtection.container.hidden, "The container is visible"); + is(TrackingProtection.content.getAttribute("state"), "blocked-tracking-content", 'content: state="blocked-tracking-content"'); - is (TrackingProtection.icon.getAttribute("state"), "blocked-tracking-content", + is(TrackingProtection.icon.getAttribute("state"), "blocked-tracking-content", 'icon: state="blocked-tracking-content"'); - ok (!hidden("#tracking-protection-icon"), "icon is visible"); - ok (hidden("#tracking-action-block"), "blockButton is hidden"); + ok(!hidden("#tracking-protection-icon"), "icon is visible"); + ok(hidden("#tracking-action-block"), "blockButton is hidden"); if (PrivateBrowsingUtils.isWindowPrivate(window)) { @@ -80,27 +78,27 @@ function testTrackingPage(window) { } // Make sure that the blocked tracking elements message appears - ok (hidden("#tracking-not-detected"), "labelNoTracking is hidden"); - ok (hidden("#tracking-loaded"), "labelTrackingLoaded is hidden"); - ok (!hidden("#tracking-blocked"), "labelTrackingBlocked is visible"); + ok(hidden("#tracking-not-detected"), "labelNoTracking is hidden"); + ok(hidden("#tracking-loaded"), "labelTrackingLoaded is hidden"); + ok(!hidden("#tracking-blocked"), "labelTrackingBlocked is visible"); } function testTrackingPageUnblocked() { info("Tracking content must be white-listed and not blocked"); - ok (!TrackingProtection.container.hidden, "The container is visible"); - is (TrackingProtection.content.getAttribute("state"), "loaded-tracking-content", + ok(!TrackingProtection.container.hidden, "The container is visible"); + is(TrackingProtection.content.getAttribute("state"), "loaded-tracking-content", 'content: state="loaded-tracking-content"'); - is (TrackingProtection.icon.getAttribute("state"), "loaded-tracking-content", + is(TrackingProtection.icon.getAttribute("state"), "loaded-tracking-content", 'icon: state="loaded-tracking-content"'); - ok (!hidden("#tracking-protection-icon"), "icon is visible"); - ok (!hidden("#tracking-action-block"), "blockButton is visible"); - ok (hidden("#tracking-action-unblock"), "unblockButton is hidden"); + ok(!hidden("#tracking-protection-icon"), "icon is visible"); + ok(!hidden("#tracking-action-block"), "blockButton is visible"); + ok(hidden("#tracking-action-unblock"), "unblockButton is hidden"); // Make sure that the blocked tracking elements message appears - ok (hidden("#tracking-not-detected"), "labelNoTracking is hidden"); - ok (!hidden("#tracking-loaded"), "labelTrackingLoaded is visible"); - ok (hidden("#tracking-blocked"), "labelTrackingBlocked is hidden"); + ok(hidden("#tracking-not-detected"), "labelNoTracking is hidden"); + ok(!hidden("#tracking-loaded"), "labelTrackingLoaded is visible"); + ok(hidden("#tracking-blocked"), "labelTrackingBlocked is hidden"); } function* testTrackingProtectionForTab(tab) { @@ -128,40 +126,40 @@ function* testTrackingProtectionForTab(tab) { add_task(function* testNormalBrowsing() { yield UrlClassifierTestUtils.addTestTrackers(); - browser = gBrowser; - let tab = browser.selectedTab = browser.addTab(); + tabbrowser = gBrowser; + let tab = tabbrowser.selectedTab = tabbrowser.addTab(); TrackingProtection = gBrowser.ownerGlobal.TrackingProtection; - ok (TrackingProtection, "TP is attached to the browser window"); - is (TrackingProtection.enabled, Services.prefs.getBoolPref(PREF), + ok(TrackingProtection, "TP is attached to the browser window"); + is(TrackingProtection.enabled, Services.prefs.getBoolPref(PREF), "TP.enabled is based on the original pref value"); Services.prefs.setBoolPref(PREF, true); - ok (TrackingProtection.enabled, "TP is enabled after setting the pref"); + ok(TrackingProtection.enabled, "TP is enabled after setting the pref"); yield testTrackingProtectionForTab(tab); Services.prefs.setBoolPref(PREF, false); - ok (!TrackingProtection.enabled, "TP is disabled after setting the pref"); + ok(!TrackingProtection.enabled, "TP is disabled after setting the pref"); }); add_task(function* testPrivateBrowsing() { let privateWin = yield promiseOpenAndLoadWindow({private: true}, true); - browser = privateWin.gBrowser; - let tab = browser.selectedTab = browser.addTab(); + tabbrowser = privateWin.gBrowser; + let tab = tabbrowser.selectedTab = tabbrowser.addTab(); - TrackingProtection = browser.ownerGlobal.TrackingProtection; - ok (TrackingProtection, "TP is attached to the private window"); - is (TrackingProtection.enabled, Services.prefs.getBoolPref(PB_PREF), + TrackingProtection = tabbrowser.ownerGlobal.TrackingProtection; + ok(TrackingProtection, "TP is attached to the private window"); + is(TrackingProtection.enabled, Services.prefs.getBoolPref(PB_PREF), "TP.enabled is based on the pb pref value"); Services.prefs.setBoolPref(PB_PREF, true); - ok (TrackingProtection.enabled, "TP is enabled after setting the pref"); + ok(TrackingProtection.enabled, "TP is enabled after setting the pref"); yield testTrackingProtectionForTab(tab); Services.prefs.setBoolPref(PB_PREF, false); - ok (!TrackingProtection.enabled, "TP is disabled after setting the pref"); + ok(!TrackingProtection.enabled, "TP is disabled after setting the pref"); privateWin.close(); }); diff --git a/browser/base/content/test/general/browser_trackingUI_2.js b/browser/base/content/test/general/browser_trackingUI_2.js index c8a30e16419e..556d7193aa47 100644 --- a/browser/base/content/test/general/browser_trackingUI_2.js +++ b/browser/base/content/test/general/browser_trackingUI_2.js @@ -1,10 +1,8 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// Test that the Tracking Protection section is never visible in the -// Control Center when the feature is off. -// See also Bugs 1175327, 1043801, 1178985. +/* + * Test that the Tracking Protection section is never visible in the + * Control Center when the feature is off. + * See also Bugs 1175327, 1043801, 1178985. + */ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; const PREF = "privacy.trackingprotection.enabled"; @@ -12,12 +10,12 @@ const PB_PREF = "privacy.trackingprotection.pbmode.enabled"; const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/benignPage.html"; const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/trackingPage.html"; let TrackingProtection = null; -let browser = null; +let tabbrowser = null; let {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {}); registerCleanupFunction(function() { - TrackingProtection = browser = null; + TrackingProtection = tabbrowser = null; UrlClassifierTestUtils.cleanupTestTrackers(); Services.prefs.clearUserPref(PREF); Services.prefs.clearUserPref(PB_PREF); @@ -29,52 +27,52 @@ registerCleanupFunction(function() { add_task(function* testNormalBrowsing() { yield UrlClassifierTestUtils.addTestTrackers(); - browser = gBrowser; - let tab = browser.selectedTab = browser.addTab(); + tabbrowser = gBrowser; + let tab = tabbrowser.selectedTab = tabbrowser.addTab(); - TrackingProtection = browser.ownerGlobal.TrackingProtection; - ok (TrackingProtection, "TP is attached to the browser window"); - is (TrackingProtection.enabled, Services.prefs.getBoolPref(PREF), + TrackingProtection = tabbrowser.ownerGlobal.TrackingProtection; + ok(TrackingProtection, "TP is attached to the browser window"); + is(TrackingProtection.enabled, Services.prefs.getBoolPref(PREF), "TP.enabled is based on the original pref value"); Services.prefs.setBoolPref(PREF, true); - ok (TrackingProtection.enabled, "TP is enabled after setting the pref"); + ok(TrackingProtection.enabled, "TP is enabled after setting the pref"); Services.prefs.setBoolPref(PREF, false); - ok (!TrackingProtection.enabled, "TP is disabled after setting the pref"); + ok(!TrackingProtection.enabled, "TP is disabled after setting the pref"); info("Load a test page containing tracking elements"); yield promiseTabLoadEvent(tab, TRACKING_PAGE); - ok (TrackingProtection.container.hidden, "The container is hidden"); + ok(TrackingProtection.container.hidden, "The container is hidden"); info("Load a test page not containing tracking elements"); yield promiseTabLoadEvent(tab, BENIGN_PAGE); - ok (TrackingProtection.container.hidden, "The container is hidden"); + ok(TrackingProtection.container.hidden, "The container is hidden"); }); add_task(function* testPrivateBrowsing() { let privateWin = yield promiseOpenAndLoadWindow({private: true}, true); - browser = privateWin.gBrowser; - let tab = browser.selectedTab = browser.addTab(); + tabbrowser = privateWin.gBrowser; + let tab = tabbrowser.selectedTab = tabbrowser.addTab(); - TrackingProtection = browser.ownerGlobal.TrackingProtection; - ok (TrackingProtection, "TP is attached to the private window"); - is (TrackingProtection.enabled, Services.prefs.getBoolPref(PB_PREF), + TrackingProtection = tabbrowser.ownerGlobal.TrackingProtection; + ok(TrackingProtection, "TP is attached to the private window"); + is(TrackingProtection.enabled, Services.prefs.getBoolPref(PB_PREF), "TP.enabled is based on the pb pref value"); Services.prefs.setBoolPref(PB_PREF, true); - ok (TrackingProtection.enabled, "TP is enabled after setting the pref"); + ok(TrackingProtection.enabled, "TP is enabled after setting the pref"); Services.prefs.setBoolPref(PB_PREF, false); - ok (!TrackingProtection.enabled, "TP is disabled after setting the pref"); + ok(!TrackingProtection.enabled, "TP is disabled after setting the pref"); info("Load a test page containing tracking elements"); yield promiseTabLoadEvent(tab, TRACKING_PAGE); - ok (TrackingProtection.container.hidden, "The container is hidden"); + ok(TrackingProtection.container.hidden, "The container is hidden"); info("Load a test page not containing tracking elements"); yield promiseTabLoadEvent(tab, BENIGN_PAGE); - ok (TrackingProtection.container.hidden, "The container is hidden"); + ok(TrackingProtection.container.hidden, "The container is hidden"); privateWin.close(); }); diff --git a/browser/base/content/test/general/browser_trackingUI_3.js b/browser/base/content/test/general/browser_trackingUI_3.js index 2eb4c85207f5..63f8a13bc7c5 100644 --- a/browser/base/content/test/general/browser_trackingUI_3.js +++ b/browser/base/content/test/general/browser_trackingUI_3.js @@ -1,17 +1,13 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// Test that the Tracking Protection is correctly enabled / disabled -// in both normal and private windows given all possible states of the prefs: -// privacy.trackingprotection.enabled -// privacy.trackingprotection.pbmode.enabled -// See also Bug 1178985. +/* + * Test that the Tracking Protection is correctly enabled / disabled + * in both normal and private windows given all possible states of the prefs: + * privacy.trackingprotection.enabled + * privacy.trackingprotection.pbmode.enabled + * See also Bug 1178985. + */ const PREF = "privacy.trackingprotection.enabled"; const PB_PREF = "privacy.trackingprotection.pbmode.enabled"; -const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/benignPage.html"; -const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/trackingPage.html"; registerCleanupFunction(function() { Services.prefs.clearUserPref(PREF); @@ -19,41 +15,38 @@ registerCleanupFunction(function() { }); add_task(function* testNormalBrowsing() { - let browser = gBrowser; - let TrackingProtection = browser.ownerGlobal.TrackingProtection; - ok (TrackingProtection, "TP is attached to the browser window"); + let TrackingProtection = gBrowser.ownerGlobal.TrackingProtection; + ok(TrackingProtection, "TP is attached to the browser window"); Services.prefs.setBoolPref(PREF, true); Services.prefs.setBoolPref(PB_PREF, false); - ok (TrackingProtection.enabled, "TP is enabled (ENABLED=true,PB=false)"); + ok(TrackingProtection.enabled, "TP is enabled (ENABLED=true,PB=false)"); Services.prefs.setBoolPref(PB_PREF, true); - ok (TrackingProtection.enabled, "TP is enabled (ENABLED=true,PB=true)"); + ok(TrackingProtection.enabled, "TP is enabled (ENABLED=true,PB=true)"); Services.prefs.setBoolPref(PREF, false); Services.prefs.setBoolPref(PB_PREF, false); - ok (!TrackingProtection.enabled, "TP is disabled (ENABLED=false,PB=false)"); + ok(!TrackingProtection.enabled, "TP is disabled (ENABLED=false,PB=false)"); Services.prefs.setBoolPref(PB_PREF, true); - ok (!TrackingProtection.enabled, "TP is disabled (ENABLED=false,PB=true)"); + ok(!TrackingProtection.enabled, "TP is disabled (ENABLED=false,PB=true)"); }); add_task(function* testPrivateBrowsing() { let privateWin = yield promiseOpenAndLoadWindow({private: true}, true); - let browser = privateWin.gBrowser; - - let TrackingProtection = browser.ownerGlobal.TrackingProtection; - ok (TrackingProtection, "TP is attached to the browser window"); + let TrackingProtection = privateWin.gBrowser.ownerGlobal.TrackingProtection; + ok(TrackingProtection, "TP is attached to the browser window"); Services.prefs.setBoolPref(PREF, true); Services.prefs.setBoolPref(PB_PREF, false); - ok (TrackingProtection.enabled, "TP is enabled (ENABLED=true,PB=false)"); + ok(TrackingProtection.enabled, "TP is enabled (ENABLED=true,PB=false)"); Services.prefs.setBoolPref(PB_PREF, true); - ok (TrackingProtection.enabled, "TP is enabled (ENABLED=true,PB=true)"); + ok(TrackingProtection.enabled, "TP is enabled (ENABLED=true,PB=true)"); Services.prefs.setBoolPref(PREF, false); Services.prefs.setBoolPref(PB_PREF, false); - ok (!TrackingProtection.enabled, "TP is disabled (ENABLED=false,PB=false)"); + ok(!TrackingProtection.enabled, "TP is disabled (ENABLED=false,PB=false)"); Services.prefs.setBoolPref(PB_PREF, true); - ok (TrackingProtection.enabled, "TP is enabled (ENABLED=false,PB=true)"); + ok(TrackingProtection.enabled, "TP is enabled (ENABLED=false,PB=true)"); privateWin.close(); }); diff --git a/browser/base/content/test/general/browser_trackingUI_4.js b/browser/base/content/test/general/browser_trackingUI_4.js index d8acb77de5ed..d6716cdfa1e1 100644 --- a/browser/base/content/test/general/browser_trackingUI_4.js +++ b/browser/base/content/test/general/browser_trackingUI_4.js @@ -1,10 +1,8 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// Test that the Tracking Protection icon is properly animated in the identity -// block when loading tabs and switching between tabs. -// See also Bug 1175858. +/* + * Test that the Tracking Protection icon is properly animated in the identity + * block when loading tabs and switching between tabs. + * See also Bug 1175858. + */ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; const PREF = "privacy.trackingprotection.enabled"; @@ -12,12 +10,12 @@ const PB_PREF = "privacy.trackingprotection.pbmode.enabled"; const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/benignPage.html"; const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/trackingPage.html"; let TrackingProtection = null; -let browser = null; +let tabbrowser = null; let {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {}); registerCleanupFunction(function() { - TrackingProtection = browser = null; + TrackingProtection = tabbrowser = null; UrlClassifierTestUtils.cleanupTestTrackers(); Services.prefs.clearUserPref(PREF); Services.prefs.clearUserPref(PB_PREF); @@ -34,76 +32,76 @@ function waitForSecurityChange(numChanges = 1) { n = n + 1; info ("Recieved onSecurityChange event " + n + " of " + numChanges); if (n >= numChanges) { - browser.removeProgressListener(listener); + tabbrowser.removeProgressListener(listener); resolve(); } } }; - browser.addProgressListener(listener); + tabbrowser.addProgressListener(listener); }); } function* testTrackingProtectionAnimation() { info("Load a test page not containing tracking elements"); - let benignTab = yield BrowserTestUtils.openNewForegroundTab(browser, BENIGN_PAGE); + let benignTab = yield BrowserTestUtils.openNewForegroundTab(tabbrowser, BENIGN_PAGE); - ok (!TrackingProtection.icon.hasAttribute("state"), "icon: no state"); - ok (TrackingProtection.icon.hasAttribute("animate"), "icon: animate"); + ok(!TrackingProtection.icon.hasAttribute("state"), "icon: no state"); + ok(TrackingProtection.icon.hasAttribute("animate"), "icon: animate"); info("Load a test page containing tracking elements"); - let trackingTab = yield BrowserTestUtils.openNewForegroundTab(browser, TRACKING_PAGE); + let trackingTab = yield BrowserTestUtils.openNewForegroundTab(tabbrowser, TRACKING_PAGE); - ok (TrackingProtection.icon.hasAttribute("state"), "icon: state"); - ok (TrackingProtection.icon.hasAttribute("animate"), "icon: animate"); + ok(TrackingProtection.icon.hasAttribute("state"), "icon: state"); + ok(TrackingProtection.icon.hasAttribute("animate"), "icon: animate"); info("Switch from tracking -> benign tab"); let securityChanged = waitForSecurityChange(); - browser.selectedTab = benignTab; + tabbrowser.selectedTab = benignTab; yield securityChanged; - ok (!TrackingProtection.icon.hasAttribute("state"), "icon: no state"); - ok (!TrackingProtection.icon.hasAttribute("animate"), "icon: no animate"); + ok(!TrackingProtection.icon.hasAttribute("state"), "icon: no state"); + ok(!TrackingProtection.icon.hasAttribute("animate"), "icon: no animate"); info("Switch from benign -> tracking tab"); securityChanged = waitForSecurityChange(); - browser.selectedTab = trackingTab; + tabbrowser.selectedTab = trackingTab; yield securityChanged; - ok (TrackingProtection.icon.hasAttribute("state"), "icon: state"); - ok (!TrackingProtection.icon.hasAttribute("animate"), "icon: no animate"); + ok(TrackingProtection.icon.hasAttribute("state"), "icon: state"); + ok(!TrackingProtection.icon.hasAttribute("animate"), "icon: no animate"); info("Reload tracking tab"); securityChanged = waitForSecurityChange(2); - browser.reload(); + tabbrowser.reload(); yield securityChanged; - ok (TrackingProtection.icon.hasAttribute("state"), "icon: state"); - ok (TrackingProtection.icon.hasAttribute("animate"), "icon: animate"); + ok(TrackingProtection.icon.hasAttribute("state"), "icon: state"); + ok(TrackingProtection.icon.hasAttribute("animate"), "icon: animate"); } add_task(function* testNormalBrowsing() { yield UrlClassifierTestUtils.addTestTrackers(); - browser = gBrowser; + tabbrowser = gBrowser; TrackingProtection = gBrowser.ownerGlobal.TrackingProtection; - ok (TrackingProtection, "TP is attached to the browser window"); + ok(TrackingProtection, "TP is attached to the browser window"); Services.prefs.setBoolPref(PREF, true); - ok (TrackingProtection.enabled, "TP is enabled after setting the pref"); + ok(TrackingProtection.enabled, "TP is enabled after setting the pref"); yield testTrackingProtectionAnimation(); }); add_task(function* testPrivateBrowsing() { let privateWin = yield promiseOpenAndLoadWindow({private: true}, true); - browser = privateWin.gBrowser; + tabbrowser = privateWin.gBrowser; - TrackingProtection = browser.ownerGlobal.TrackingProtection; - ok (TrackingProtection, "TP is attached to the private window"); + TrackingProtection = tabbrowser.ownerGlobal.TrackingProtection; + ok(TrackingProtection, "TP is attached to the private window"); Services.prefs.setBoolPref(PB_PREF, true); - ok (TrackingProtection.enabled, "TP is enabled after setting the pref"); + ok(TrackingProtection.enabled, "TP is enabled after setting the pref"); yield testTrackingProtectionAnimation(); From 80fc6f5887ce870609e144cc58b891735c183e17 Mon Sep 17 00:00:00 2001 From: Allison Naaktgeboren Date: Thu, 23 Jul 2015 14:11:32 -0700 Subject: [PATCH 12/18] Bug 1101746 - Display message when no passwords are present.r=liuche --- mobile/android/chrome/content/aboutLogins.js | 24 +++++++++++++-- .../android/chrome/content/aboutLogins.xhtml | 9 +++++- .../locales/en-US/chrome/aboutLogins.dtd | 6 +++- mobile/android/themes/core/aboutLogins.css | 29 +++++++++++++++++++ .../themes/core/images/icon_key_emptypage.svg | 11 +++++++ mobile/android/themes/core/jar.mn | 1 + 6 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 mobile/android/themes/core/images/icon_key_emptypage.svg diff --git a/mobile/android/chrome/content/aboutLogins.js b/mobile/android/chrome/content/aboutLogins.js index 2b19b7c30125..4531fb45502d 100644 --- a/mobile/android/chrome/content/aboutLogins.js +++ b/mobile/android/chrome/content/aboutLogins.js @@ -45,7 +45,13 @@ let Logins = { _getLogins: function() { let logins; + let contentBody = document.getElementById("content-body"); + let emptyBody = document.getElementById("empty-body"); + let filterIcon = document.getElementById("filter-button"); + this._toggleListBody(true); + emptyBody.classList.add("hidden"); + try { logins = Services.logins.getAllLogins(); } catch(e) { @@ -54,20 +60,32 @@ let Logins = { logins = []; } this._toggleListBody(false); + + if (!logins.length) { + emptyBody.classList.remove("hidden"); + + filterIcon.classList.add("hidden"); + contentBody.classList.add("hidden"); + } else { + emptyBody.classList.add("hidden"); + + filterIcon.classList.remove("hidden"); + } + logins.sort((a, b) => a.hostname.localeCompare(b.hostname)); return this._logins = logins; }, _toggleListBody: function(isLoading) { - let nonemptyBody = document.getElementById("logins-list-nonempty-body"); + let contentBody = document.getElementById("content-body"); let loadingBody = document.getElementById("logins-list-loading-body"); if (isLoading) { - nonemptyBody.classList.add("hidden"); + contentBody.classList.add("hidden"); loadingBody.classList.remove("hidden"); } else { loadingBody.classList.add("hidden"); - nonemptyBody.classList.remove("hidden"); + contentBody.classList.remove("hidden"); } }, diff --git a/mobile/android/chrome/content/aboutLogins.xhtml b/mobile/android/chrome/content/aboutLogins.xhtml index 0bed9855a741..cd7309f36319 100644 --- a/mobile/android/chrome/content/aboutLogins.xhtml +++ b/mobile/android/chrome/content/aboutLogins.xhtml @@ -29,7 +29,7 @@
  • -
    +