author | Mark Banner <standard8@mozilla.com> |
Wed, 07 Dec 2016 13:45:56 +0000 | |
changeset 374181 | 99365c0b73478e57a5936c45ae2bebcf765fcd78 |
parent 374180 | c4fa2b29abe7220545789fa2706601338cbe90aa |
child 374182 | 8460203bc93b9667cea1bc00f9d9990a4b1a9474 |
child 374183 | e3eeaf868c156d86ab0b519fd6f0715376095d49 |
push id | 1419 |
push user | jlund@mozilla.com |
push date | Mon, 10 Apr 2017 20:44:07 +0000 |
treeherder | mozilla-release@5e6801b73ef6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mak |
bugs | 1308420 |
milestone | 53.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -358,86 +358,25 @@ BrowserGlue.prototype = { for (let addon of addons) { if (addon.type != "experiment") { this._notifyUnsignedAddonsDisabled(); break; } } }); break; - case "autocomplete-did-enter-text": - this._handleURLBarTelemetry(subject.QueryInterface(Ci.nsIAutoCompleteInput)); - break; case "test-initialize-sanitizer": this._sanitizer.onStartup(); break; case AddonWatcher.TOPIC_SLOW_ADDON_DETECTED: this._notifySlowAddon(data); break; } }, - _handleURLBarTelemetry(input) { - if (!input || - input.id != "urlbar" || - input.inPrivateContext || - input.popup.selectedIndex < 0) { - return; - } - let controller = - input.popup.view.QueryInterface(Ci.nsIAutoCompleteController); - let idx = input.popup.selectedIndex; - let value = controller.getValueAt(idx); - let action = input._parseActionUrl(value); - let actionType; - if (action) { - actionType = - action.type == "searchengine" && action.params.searchSuggestion ? - "searchsuggestion" : - action.type; - } - if (!actionType) { - let styles = new Set(controller.getStyleAt(idx).split(/\s+/)); - let style = ["autofill", "tag", "bookmark"].find(s => styles.has(s)); - actionType = style || "history"; - } - - Services.telemetry - .getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX") - .add(idx); - - // Ideally this would be a keyed histogram and we'd just add(actionType), - // but keyed histograms aren't currently shown on the telemetry dashboard - // (bug 1151756). - // - // You can add values but don't change any of the existing values. - // Otherwise you'll break our data. - let buckets = { - autofill: 0, - bookmark: 1, - history: 2, - keyword: 3, - searchengine: 4, - searchsuggestion: 5, - switchtab: 6, - tag: 7, - visiturl: 8, - remotetab: 9, - extension: 10, - }; - if (actionType in buckets) { - Services.telemetry - .getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE") - .add(buckets[actionType]); - } else { - Cu.reportError("Unknown FX_URLBAR_SELECTED_RESULT_TYPE type: " + - actionType); - } - }, - // initialization (called on application startup) _init: function BG__init() { let os = Services.obs; os.addObserver(this, "notifications-open-settings", false); os.addObserver(this, "prefservice:after-app-defaults", false); os.addObserver(this, "final-ui-startup", false); os.addObserver(this, "browser-delayed-startup-finished", false); os.addObserver(this, "sessionstore-windows-restored", false); @@ -464,17 +403,16 @@ BrowserGlue.prototype = { os.addObserver(this, "profile-before-change", false); if (AppConstants.MOZ_TELEMETRY_REPORTING) { os.addObserver(this, "keyword-search", false); } os.addObserver(this, "browser-search-engine-modified", false); os.addObserver(this, "restart-in-safe-mode", false); os.addObserver(this, "flash-plugin-hang", false); os.addObserver(this, "xpi-signature-changed", false); - os.addObserver(this, "autocomplete-did-enter-text", false); if (AppConstants.NIGHTLY_BUILD) { os.addObserver(this, AddonWatcher.TOPIC_SLOW_ADDON_DETECTED, false); } this._flashHangCount = 0; this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve); @@ -519,17 +457,16 @@ BrowserGlue.prototype = { os.removeObserver(this, "handle-xul-text-link"); os.removeObserver(this, "profile-before-change"); if (AppConstants.MOZ_TELEMETRY_REPORTING) { os.removeObserver(this, "keyword-search"); } os.removeObserver(this, "browser-search-engine-modified"); os.removeObserver(this, "flash-plugin-hang"); os.removeObserver(this, "xpi-signature-changed"); - os.removeObserver(this, "autocomplete-did-enter-text"); }, _onAppDefaults: function BG__onAppDefaults() { // apply distribution customizations (prefs) // other customizations are applied in _finalUIStartup() this._distributionCustomizer.applyPrefDefaults(); },
--- a/browser/modules/BrowserUsageTelemetry.jsm +++ b/browser/modules/BrowserUsageTelemetry.jsm @@ -1,16 +1,16 @@ /* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -this.EXPORTED_SYMBOLS = ["BrowserUsageTelemetry"]; +this.EXPORTED_SYMBOLS = ["BrowserUsageTelemetry", "URLBAR_SELECTED_RESULT_TYPES"]; const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); @@ -18,16 +18,17 @@ XPCOMUtils.defineLazyModuleGetter(this, // The upper bound for the count of the visited unique domain names. const MAX_UNIQUE_VISITED_DOMAINS = 100; // Observed topic names. const WINDOWS_RESTORED_TOPIC = "sessionstore-windows-restored"; const TAB_RESTORING_TOPIC = "SSTabRestoring"; const TELEMETRY_SUBSESSIONSPLIT_TOPIC = "internal-telemetry-after-subsession-split"; const DOMWINDOW_OPENED_TOPIC = "domwindowopened"; +const AUTOCOMPLETE_ENTER_TEXT_TOPIC = "autocomplete-did-enter-text"; // Probe names. const MAX_TAB_COUNT_SCALAR_NAME = "browser.engagement.max_concurrent_tab_count"; const MAX_WINDOW_COUNT_SCALAR_NAME = "browser.engagement.max_concurrent_window_count"; const TAB_OPEN_EVENT_COUNT_SCALAR_NAME = "browser.engagement.tab_open_event_count"; const WINDOW_OPEN_EVENT_COUNT_SCALAR_NAME = "browser.engagement.window_open_event_count"; const UNIQUE_DOMAINS_COUNT_SCALAR_NAME = "browser.engagement.unique_domains_count"; const TOTAL_URI_COUNT_SCALAR_NAME = "browser.engagement.total_uri_count"; @@ -43,16 +44,34 @@ const KNOWN_SEARCH_SOURCES = [ ]; const KNOWN_ONEOFF_SOURCES = [ "oneoff-urlbar", "oneoff-searchbar", "unknown", // Edge case: this is the searchbar (see bug 1195733 comment 7). ]; +/** + * The buckets used for logging telemetry to the FX_URLBAR_SELECTED_RESULT_TYPE + * histogram. + */ +const URLBAR_SELECTED_RESULT_TYPES = { + autofill: 0, + bookmark: 1, + history: 2, + keyword: 3, + searchengine: 4, + searchsuggestion: 5, + switchtab: 6, + tag: 7, + visiturl: 8, + remotetab: 9, + extension: 10, +}; + function getOpenTabsAndWinsCounts() { let tabCount = 0; let winCount = 0; let browserEnum = Services.wm.getEnumerator("navigator:browser"); while (browserEnum.hasMoreElements()) { let win = browserEnum.getNext(); winCount++; @@ -182,19 +201,92 @@ let URICountListener = { reset() { this._domainSet.clear(); }, QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]), }; +let urlbarListener = { + init() { + Services.obs.addObserver(this, AUTOCOMPLETE_ENTER_TEXT_TOPIC, true); + }, + + uninit() { + Services.obs.removeObserver(this, AUTOCOMPLETE_ENTER_TEXT_TOPIC, true); + }, + + observe(subject, topic, data) { + switch (topic) { + case AUTOCOMPLETE_ENTER_TEXT_TOPIC: + this._handleURLBarTelemetry(subject.QueryInterface(Ci.nsIAutoCompleteInput)); + break; + } + }, + + /** + * Used to log telemetry when the user enters text in the urlbar. + * + * @param {nsIAutoCompleteInput} input The autocomplete element where the + * text was entered. + */ + _handleURLBarTelemetry(input) { + if (!input || + input.id != "urlbar" || + input.inPrivateContext || + input.popup.selectedIndex < 0) { + return; + } + let controller = + input.popup.view.QueryInterface(Ci.nsIAutoCompleteController); + let idx = input.popup.selectedIndex; + let value = controller.getValueAt(idx); + let action = input._parseActionUrl(value); + let actionType; + if (action) { + actionType = + action.type == "searchengine" && action.params.searchSuggestion ? + "searchsuggestion" : + action.type; + } + if (!actionType) { + let styles = new Set(controller.getStyleAt(idx).split(/\s+/)); + let style = ["autofill", "tag", "bookmark"].find(s => styles.has(s)); + actionType = style || "history"; + } + + Services.telemetry + .getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX") + .add(idx); + + // Ideally this would be a keyed histogram and we'd just add(actionType), + // but keyed histograms aren't currently shown on the telemetry dashboard + // (bug 1151756). + // + // You can add values but don't change any of the existing values. + // Otherwise you'll break our data. + if (actionType in URLBAR_SELECTED_RESULT_TYPES) { + Services.telemetry + .getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE") + .add(URLBAR_SELECTED_RESULT_TYPES[actionType]); + } else { + Cu.reportError("Unknown FX_URLBAR_SELECTED_RESULT_TYPE type: " + + actionType); + } + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, + Ci.nsISupportsWeakReference]), +}; + let BrowserUsageTelemetry = { init() { Services.obs.addObserver(this, WINDOWS_RESTORED_TOPIC, false); + urlbarListener.init(); }, /** * Handle subsession splits in the parent process. */ afterSubsessionSplit() { // Scalars just got cleared due to a subsession split. We need to set the maximum // concurrent tab and window counts so that they reflect the correct value for the @@ -206,16 +298,17 @@ let BrowserUsageTelemetry = { // Reset the URI counter. URICountListener.reset(); }, uninit() { Services.obs.removeObserver(this, DOMWINDOW_OPENED_TOPIC, false); Services.obs.removeObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC, false); Services.obs.removeObserver(this, WINDOWS_RESTORED_TOPIC, false); + urlbarListener.uninit(); }, observe(subject, topic, data) { switch (topic) { case WINDOWS_RESTORED_TOPIC: this._setupAfterRestore(); break; case DOMWINDOW_OPENED_TOPIC:
--- a/browser/modules/test/browser_UsageTelemetry_content.js +++ b/browser/modules/test/browser_UsageTelemetry_content.js @@ -28,16 +28,17 @@ add_task(function* setup() { ["toolkit.telemetry.enabled", true] // And Extended Telemetry to be enabled. ]}); // Make sure to restore the engine once we're done. registerCleanupFunction(function* () { Services.search.currentEngine = originalEngine; Services.search.removeEngine(engineDefault); Services.search.removeEngine(engineOneOff); + yield PlacesTestUtils.clearHistory(); }); }); add_task(function* test_context_menu() { // Let's reset the Telemetry data. Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); let search_hist = getSearchCountsHistogram();
--- a/browser/modules/test/browser_UsageTelemetry_content_aboutHome.js +++ b/browser/modules/test/browser_UsageTelemetry_content_aboutHome.js @@ -30,16 +30,17 @@ add_task(function* setup() { // Enable Extended Telemetry. yield SpecialPowers.pushPrefEnv({"set": [["toolkit.telemetry.enabled", true]]}); // Make sure to restore the engine once we're done. registerCleanupFunction(function* () { Services.search.currentEngine = originalEngine; Services.search.removeEngine(engineDefault); Services.search.removeEngine(engineOneOff); + yield PlacesTestUtils.clearHistory(); }); }); add_task(function* test_abouthome_simpleQuery() { // Let's reset the counts. Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); let search_hist = getSearchCountsHistogram();
--- a/browser/modules/test/browser_UsageTelemetry_urlbar.js +++ b/browser/modules/test/browser_UsageTelemetry_urlbar.js @@ -3,16 +3,31 @@ const SCALAR_URLBAR = "browser.engagement.navigation.urlbar"; // The preference to enable suggestions in the urlbar. const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches"; // The name of the search engine used to generate suggestions. const SUGGESTION_ENGINE_NAME = "browser_UsageTelemetry usageTelemetrySearchSuggestions.xml"; const ONEOFF_URLBAR_PREF = "browser.urlbar.oneOffSearches"; +XPCOMUtils.defineLazyModuleGetter(this, "URLBAR_SELECTED_RESULT_TYPES", + "resource:///modules/BrowserUsageTelemetry.jsm"); + +function checkHistogramResults(resultIndexes, expected, histogram) { + for (let i = 0; i < resultIndexes.counts.length; i++) { + if (i == expected) { + Assert.equal(resultIndexes.counts[i], 1, + `expected counts should match for ${histogram} index ${i}`); + } else { + Assert.equal(resultIndexes.counts[i], 0, + `unexpected counts should be zero for ${histogram} index ${i}`); + } + } +} + let searchInAwesomebar = Task.async(function* (inputText, win = window) { yield new Promise(r => waitForFocus(r, win)); // Write the search query in the urlbar. win.gURLBar.focus(); win.gURLBar.value = inputText; win.gURLBar.controller.startSearch(inputText); // Wait for the popup to show. yield BrowserTestUtils.waitForEvent(win.gURLBar.popup, "popupshown"); @@ -57,29 +72,40 @@ add_task(function* setup() { Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true); // Enable the urlbar one-off buttons. Services.prefs.setBoolPref(ONEOFF_URLBAR_PREF, true); // Enable Extended Telemetry. yield SpecialPowers.pushPrefEnv({"set": [["toolkit.telemetry.enabled", true]]}); + // Enable local telemetry recording for the duration of the tests. + let oldCanRecord = Services.telemetry.canRecordExtended; + Services.telemetry.canRecordExtended = true; + // Make sure to restore the engine once we're done. registerCleanupFunction(function* () { + Services.telemetry.canRecordExtended = oldCanRecord; Services.search.currentEngine = originalEngine; Services.search.removeEngine(engine); Services.prefs.clearUserPref(SUGGEST_URLBAR_PREF, true); Services.prefs.clearUserPref(ONEOFF_URLBAR_PREF); + yield PlacesTestUtils.clearHistory(); }); }); add_task(function* test_simpleQuery() { // Let's reset the counts. Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); + let resultIndexHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX"); + let resultTypeHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE"); + resultIndexHist.clear(); + resultTypeHist.clear(); + let search_hist = getSearchCountsHistogram(); let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"); info("Simulate entering a simple search."); let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser); yield searchInAwesomebar("simple query"); EventUtils.sendKey("return"); @@ -95,23 +121,37 @@ add_task(function* test_simpleQuery() { // Make sure SEARCH_COUNTS contains identical values. checkKeyedHistogram(search_hist, 'other-MozSearch.urlbar', 1); // Also check events. let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); events = events.filter(e => e[1] == "navigation" && e[2] == "search"); checkEvents(events, [["navigation", "search", "urlbar", "enter", {engine: "other-MozSearch"}]]); + // Check the histograms as well. + let resultIndexes = resultIndexHist.snapshot(); + checkHistogramResults(resultIndexes, 0, "FX_URLBAR_SELECTED_RESULT_INDEX"); + + let resultTypes = resultTypeHist.snapshot(); + checkHistogramResults(resultTypes, + URLBAR_SELECTED_RESULT_TYPES.searchengine, + "FX_URLBAR_SELECTED_RESULT_TYPE"); + yield BrowserTestUtils.removeTab(tab); }); add_task(function* test_searchAlias() { // Let's reset the counts. Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); + let resultIndexHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX"); + let resultTypeHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE"); + resultIndexHist.clear(); + resultTypeHist.clear(); + let search_hist = getSearchCountsHistogram(); let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"); info("Search using a search alias."); let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser); yield searchInAwesomebar("mozalias query"); EventUtils.sendKey("return"); @@ -127,23 +167,37 @@ add_task(function* test_searchAlias() { // Make sure SEARCH_COUNTS contains identical values. checkKeyedHistogram(search_hist, 'other-MozSearch.urlbar', 1); // Also check events. let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); events = events.filter(e => e[1] == "navigation" && e[2] == "search"); checkEvents(events, [["navigation", "search", "urlbar", "alias", {engine: "other-MozSearch"}]]); + // Check the histograms as well. + let resultIndexes = resultIndexHist.snapshot(); + checkHistogramResults(resultIndexes, 0, "FX_URLBAR_SELECTED_RESULT_INDEX"); + + let resultTypes = resultTypeHist.snapshot(); + checkHistogramResults(resultTypes, + URLBAR_SELECTED_RESULT_TYPES.searchengine, + "FX_URLBAR_SELECTED_RESULT_TYPE"); + yield BrowserTestUtils.removeTab(tab); }); add_task(function* test_oneOff() { // Let's reset the counts. Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); + let resultIndexHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX"); + let resultTypeHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE"); + resultIndexHist.clear(); + resultTypeHist.clear(); + let search_hist = getSearchCountsHistogram(); let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"); info("Perform a one-off search using the first engine."); let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser); yield searchInAwesomebar("query"); @@ -162,23 +216,37 @@ add_task(function* test_oneOff() { // Make sure SEARCH_COUNTS contains identical values. checkKeyedHistogram(search_hist, 'other-MozSearch.urlbar', 1); // Also check events. let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); events = events.filter(e => e[1] == "navigation" && e[2] == "search"); checkEvents(events, [["navigation", "search", "urlbar", "oneoff", {engine: "other-MozSearch"}]]); + // Check the histograms as well. + let resultIndexes = resultIndexHist.snapshot(); + checkHistogramResults(resultIndexes, 0, "FX_URLBAR_SELECTED_RESULT_INDEX"); + + let resultTypes = resultTypeHist.snapshot(); + checkHistogramResults(resultTypes, + URLBAR_SELECTED_RESULT_TYPES.searchengine, + "FX_URLBAR_SELECTED_RESULT_TYPE"); + yield BrowserTestUtils.removeTab(tab); }); add_task(function* test_suggestion() { // Let's reset the counts. Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); + let resultIndexHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX"); + let resultTypeHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE"); + resultIndexHist.clear(); + resultTypeHist.clear(); + let search_hist = getSearchCountsHistogram(); // Create an engine to generate search suggestions and add it as default // for this test. const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml"; let suggestionEngine = yield new Promise((resolve, reject) => { Services.search.addEngine(url, null, "", false, { onSuccess(engine) { resolve(engine) }, @@ -209,12 +277,21 @@ add_task(function* test_suggestion() { let searchEngineId = 'other-' + suggestionEngine.name; checkKeyedHistogram(search_hist, searchEngineId + '.urlbar', 1); // Also check events. let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); events = events.filter(e => e[1] == "navigation" && e[2] == "search"); checkEvents(events, [["navigation", "search", "urlbar", "suggestion", {engine: searchEngineId}]]); + // Check the histograms as well. + let resultIndexes = resultIndexHist.snapshot(); + checkHistogramResults(resultIndexes, 3, "FX_URLBAR_SELECTED_RESULT_INDEX"); + + let resultTypes = resultTypeHist.snapshot(); + checkHistogramResults(resultTypes, + URLBAR_SELECTED_RESULT_TYPES.searchsuggestion, + "FX_URLBAR_SELECTED_RESULT_TYPE"); + Services.search.currentEngine = previousEngine; Services.search.removeEngine(suggestionEngine); yield BrowserTestUtils.removeTab(tab); });
--- a/browser/modules/test/head.js +++ b/browser/modules/test/head.js @@ -1,10 +1,13 @@ Cu.import("resource://gre/modules/Promise.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", + "resource://testing-common/PlacesTestUtils.jsm"); + const SINGLE_TRY_TIMEOUT = 100; const NUMBER_OF_TRIES = 30; function waitForConditionPromise(condition, timeoutMsg, tryCount = NUMBER_OF_TRIES) { let defer = Promise.defer(); let tries = 0; function checkCondition() { if (tries >= tryCount) {
--- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -5468,17 +5468,17 @@ "description": "Firefox: The index of the selected result in the URL bar popup" }, "FX_URLBAR_SELECTED_RESULT_TYPE": { "alert_emails": ["firefox-dev@mozilla.org"], "expires_in_version": "never", "kind": "enumerated", "n_values": 14, "bug_numbers": [775825], - "description": "Firefox: The type of the selected result in the URL bar popup. See nsBrowserGlue.js::_handleURLBarTelemetry for the result types." + "description": "Firefox: The type of the selected result in the URL bar popup. See BrowserUsageTelemetry.jsm:URLBAR_SELECTED_RESULT_TYPES for the result types." }, "INNERWINDOWS_WITH_MUTATION_LISTENERS": { "expires_in_version": "never", "kind": "boolean", "description": "Deleted or to-be-reused innerwindow which has had mutation event listeners." }, "CHARSET_OVERRIDE_SITUATION": { "expires_in_version": "never",