author | Wes Kocher <wkocher@mozilla.com> |
Mon, 19 Sep 2016 15:34:31 -0700 | |
changeset 357234 | 5ef7111ff596959cc54653f2eec975b249ab7775 |
parent 357222 | fd0564234eca242b7fb753a110312679020f8059 (current diff) |
parent 357233 | d1df1692dbb4b1f635ef52696233373309ba0b8c (diff) |
child 357250 | 80a9c7007243ac4e931a8c4352723cbf840aff03 |
push id | 6795 |
push user | jlund@mozilla.com |
push date | Mon, 23 Jan 2017 14:19:46 +0000 |
treeherder | mozilla-beta@76101b503191 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 52.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/base/content/browser-media.js +++ b/browser/base/content/browser-media.js @@ -291,17 +291,17 @@ let gDecoderDoctorHandler = { if (!formatsInPref) { Services.prefs.setCharPref(formatsPref, formats); histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SHOWN_FIRST); } else { // Split existing formats into an array of strings. let existing = formatsInPref.split(",").map(String.trim); // Keep given formats that were not already recorded. let newbies = formats.split(",").map(String.trim) - .filter(x => existing.includes(x)); + .filter(x => !existing.includes(x)); // And rewrite pref with the added new formats (if any). if (newbies.length) { Services.prefs.setCharPref(formatsPref, existing.concat(newbies).join(", ")); } } histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SHOWN);
--- a/browser/components/preferences/connection.xul +++ b/browser/components/preferences/connection.xul @@ -9,17 +9,22 @@ <?xml-stylesheet href="chrome://global/skin/"?> <prefwindow id="ConnectionsDialog" type="child" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title="&connectionsDialog.title;" dlgbuttons="accept,cancel,help" onbeforeaccept="return gConnectionsDialog.beforeAccept();" onload="gConnectionsDialog.checkForSystemProxy();" - ondialoghelp="openPrefsHelp()"> + ondialoghelp="openPrefsHelp()" +#ifdef XP_MACOSX + style="width: &window.macWidth2; !important;"> +#else + style="width: &window.width2; !important;"> +#endif <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> <prefpane id="ConnectionsDialogPane" class="largeDialogContainer" helpTopic="prefs-connection-settings"> <preferences>
--- a/browser/components/preferences/jar.mn +++ b/browser/components/preferences/jar.mn @@ -5,17 +5,17 @@ browser.jar: content/browser/preferences/applicationManager.xul content/browser/preferences/applicationManager.js content/browser/preferences/blocklists.xul content/browser/preferences/blocklists.js * content/browser/preferences/colors.xul * content/browser/preferences/cookies.xul content/browser/preferences/cookies.js - content/browser/preferences/connection.xul +* content/browser/preferences/connection.xul content/browser/preferences/connection.js content/browser/preferences/donottrack.xul * content/browser/preferences/fonts.xul content/browser/preferences/fonts.js content/browser/preferences/handlers.xml content/browser/preferences/handlers.css * content/browser/preferences/languages.xul content/browser/preferences/languages.js
--- a/browser/locales/en-US/chrome/browser/preferences/connection.dtd +++ b/browser/locales/en-US/chrome/browser/preferences/connection.dtd @@ -1,14 +1,16 @@ <!-- 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/. --> <!ENTITY connectionsDialog.title "Connection Settings"> +<!ENTITY window.width2 "49em"> +<!ENTITY window.macWidth2 "44em"> <!ENTITY proxyTitle.label "Configure Proxies to Access the Internet"> <!ENTITY noProxyTypeRadio.label "No proxy"> <!ENTITY noProxyTypeRadio.accesskey "y"> <!ENTITY systemTypeRadio.label "Use system proxy settings"> <!ENTITY systemTypeRadio.accesskey "u"> <!ENTITY WPADTypeRadio.label "Auto-detect proxy settings for this network"> <!ENTITY WPADTypeRadio.accesskey "w">
--- a/browser/modules/SitePermissions.jsm +++ b/browser/modules/SitePermissions.jsm @@ -19,29 +19,35 @@ this.SitePermissions = { /* Returns all custom permissions for a given URI, the return * type is a list of objects with the keys: * - id: the permissionId of the permission * - state: a constant representing the current permission state * (e.g. SitePermissions.ALLOW) * * To receive a more detailed, albeit less performant listing see * SitePermissions.getPermissionDetailsByURI(). + * + * install addon permission is excluded, check bug 1303108 */ getAllByURI: function (aURI) { let result = []; if (!this.isSupportedURI(aURI)) { return result; } let permissions = Services.perms.getAllForURI(aURI); while (permissions.hasMoreElements()) { let permission = permissions.getNext(); // filter out unknown permissions if (gPermissionObject[permission.type]) { + // XXX Bug 1303108 - Control Center should only show non-default permissions + if (permission.type == "install") { + continue; + } result.push({ id: permission.type, state: permission.capability, }); } } return result;
--- a/browser/modules/test/xpcshell/test_SitePermissions.js +++ b/browser/modules/test/xpcshell/test_SitePermissions.js @@ -40,16 +40,21 @@ add_task(function* testGetAllByURI() { Assert.deepEqual(SitePermissions.getAllByURI(uri), [ { id: "camera", state: SitePermissions.ALLOW }, { id: "desktop-notification", state: SitePermissions.BLOCK } ]); SitePermissions.remove(uri, "camera"); SitePermissions.remove(uri, "desktop-notification"); Assert.deepEqual(SitePermissions.getAllByURI(uri), []); + + // XXX Bug 1303108 - Control Center should only show non-default permissions + SitePermissions.set(uri, "addon", SitePermissions.BLOCK); + Assert.deepEqual(SitePermissions.getAllByURI(uri), []); + SitePermissions.remove(uri, "addon"); }); add_task(function* testGetPermissionDetailsByURI() { // check that it returns an empty array on an invalid URI // like a file URI, which doesn't support site permissions let wrongURI = Services.io.newURI("file:///example.js", null, null) Assert.deepEqual(SitePermissions.getPermissionDetailsByURI(wrongURI), []);
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Preferences.jsm +++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Preferences.jsm @@ -23,19 +23,21 @@ this.Preferences = { ["paneApplications", null], ["panePrivacy", null], ["panePrivacy", null, DNTDialog], ["paneSecurity", null], ["paneSync", null], ["paneAdvanced", "generalTab"], ["paneAdvanced", "dataChoicesTab"], ["paneAdvanced", "networkTab"], + ["paneAdvanced", "networkTab", connectionDialog], ["paneAdvanced", "updateTab"], ["paneAdvanced", "encryptionTab"], ["paneAdvanced", "encryptionTab", certManager], + ["paneAdvanced", "encryptionTab", deviceManager], ]; for (let [primary, advanced, customFn] of panes) { let configName = primary.replace(/^pane/, "prefs") + (advanced ? "-" + advanced : ""); if (customFn) { configName += "-" + customFn.name; } this.configurations[configName] = {}; this.configurations[configName].applyConfig = prefHelper.bind(null, primary, advanced, customFn); @@ -94,13 +96,25 @@ function paintPromise(browserWindow) { } function* DNTDialog(aBrowser) { yield ContentTask.spawn(aBrowser, null, function* () { content.document.getElementById("doNotTrackSettings").click(); }); } +function* connectionDialog(aBrowser) { + yield ContentTask.spawn(aBrowser, null, function* () { + content.document.getElementById("connectionSettings").click(); + }); +} + function* certManager(aBrowser) { yield ContentTask.spawn(aBrowser, null, function* () { content.document.getElementById("viewCertificatesButton").click(); }); } + +function* deviceManager(aBrowser) { + yield ContentTask.spawn(aBrowser, null, function* () { + content.document.getElementById("viewSecurityDevicesButton").click(); + }); +}
--- a/build/autoconf/android.m4 +++ b/build/autoconf/android.m4 @@ -216,18 +216,18 @@ if test -n "$MOZ_INSTALL_TRACKING"; then AC_SUBST(MOZ_INSTALL_TRACKING) MOZ_ANDROID_AAR(play-services-ads, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) MOZ_ANDROID_AAR(play-services-basement, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) fi ]) dnl Configure an Android SDK. -dnl Arg 1: target SDK version, like 22. -dnl Arg 2: build tools version, like 22.0.1. +dnl Arg 1: target SDK version, like 23. +dnl Arg 2: list of build-tools versions, like "23.0.3 23.0.1". AC_DEFUN([MOZ_ANDROID_SDK], [ MOZ_ARG_WITH_STRING(android-sdk, [ --with-android-sdk=DIR location where the Android SDK can be found (like ~/.mozbuild/android-sdk-linux)], android_sdk_root=$withval) @@ -249,22 +249,30 @@ case "$target" in android_target_sdk=$1 AC_MSG_CHECKING([for Android SDK platform version $android_target_sdk]) android_sdk=$android_sdk_root/platforms/android-$android_target_sdk if ! test -e "$android_sdk/source.properties" ; then AC_MSG_ERROR([You must download Android SDK platform version $android_target_sdk. Try |mach bootstrap|. (Looked for $android_sdk)]) fi AC_MSG_RESULT([$android_sdk]) - android_build_tools="$android_sdk_root"/build-tools/$2 - AC_MSG_CHECKING([for Android build-tools version $2]) - if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then - AC_MSG_RESULT([$android_build_tools]) - else - AC_MSG_ERROR([You must install the Android build-tools version $2. Try |mach bootstrap|. (Looked for $android_build_tools)]) + AC_MSG_CHECKING([for Android build-tools]) + android_build_tools_base="$android_sdk_root"/build-tools + android_build_tools_version="" + for version in $2; do + android_build_tools="$android_build_tools_base"/$version + if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then + android_build_tools_version=$version + AC_MSG_RESULT([$android_build_tools]) + break + fi + done + if test "$android_build_tools_version" == ""; then + version=$(echo $2 | cut -d" " -f1) + AC_MSG_ERROR([You must install the Android build-tools version $version. Try |mach bootstrap|. (Looked for "$android_build_tools_base"/$version)]) fi MOZ_PATH_PROG(ZIPALIGN, zipalign, :, [$android_build_tools]) MOZ_PATH_PROG(DX, dx, :, [$android_build_tools]) MOZ_PATH_PROG(AAPT, aapt, :, [$android_build_tools]) MOZ_PATH_PROG(AIDL, aidl, :, [$android_build_tools]) if test -z "$ZIPALIGN" -o "$ZIPALIGN" = ":"; then AC_MSG_ERROR([The program zipalign was not found. Try |mach bootstrap|.]) @@ -304,17 +312,17 @@ case "$target" in if test -z "$EMULATOR" -o "$EMULATOR" = ":"; then AC_MSG_ERROR([The program emulator was not found. Try |mach bootstrap|.]) fi ANDROID_TARGET_SDK="${android_target_sdk}" ANDROID_SDK="${android_sdk}" ANDROID_SDK_ROOT="${android_sdk_root}" ANDROID_TOOLS="${android_tools}" - ANDROID_BUILD_TOOLS_VERSION="$2" + ANDROID_BUILD_TOOLS_VERSION="$android_build_tools_version" AC_DEFINE_UNQUOTED(ANDROID_TARGET_SDK,$ANDROID_TARGET_SDK) AC_SUBST(ANDROID_TARGET_SDK) AC_SUBST(ANDROID_SDK_ROOT) AC_SUBST(ANDROID_SDK) AC_SUBST(ANDROID_TOOLS) AC_SUBST(ANDROID_BUILD_TOOLS_VERSION) MOZ_ANDROID_AAR(customtabs, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
--- a/devtools/client/responsive.html/manager.js +++ b/devtools/client/responsive.html/manager.js @@ -300,18 +300,19 @@ ResponsiveUI.prototype = { * to ensure all in-page state is preserved, just like when you move a tab to * a new window. * * For more details, see /devtools/docs/responsive-design-mode.md. */ init: Task.async(function* () { let ui = this; - // Watch for tab close so we can clean up RDM synchronously + // Watch for tab close and window close so we can clean up RDM synchronously this.tab.addEventListener("TabClose", this); + this.browserWindow.addEventListener("unload", this); // Swap page content from the current tab into a viewport within RDM this.swap = swapToInnerBrowser({ tab: this.tab, containerURL: TOOL_URL, getInnerBrowser: Task.async(function* (containerBrowser) { let toolWindow = ui.toolWindow = containerBrowser.contentWindow; toolWindow.addEventListener("message", ui); @@ -343,24 +344,26 @@ ResponsiveUI.prototype = { if (this.destroying) { return false; } this.destroying = true; // If our tab is about to be closed, there's not enough time to exit // gracefully, but that shouldn't be a problem since the tab will go away. // So, skip any yielding when we're about to close the tab. - let isTabClosing = options && options.reason == "TabClose"; + let isWindowClosing = options && options.reason === "unload"; + let isTabClosing = (options && options.reason === "TabClose") || isWindowClosing; // Ensure init has finished before starting destroy if (!isTabClosing) { yield this.inited; } this.tab.removeEventListener("TabClose", this); + this.browserWindow.removeEventListener("unload", this); this.toolWindow.removeEventListener("message", this); if (!isTabClosing) { // Stop the touch event simulator if it was running yield this.emulationFront.clearTouchEventsOverride(); // Notify the inner browser to stop the frame script yield message.request(this.toolWindow, "stop-frame-script"); @@ -376,18 +379,20 @@ ResponsiveUI.prototype = { // Close the debugger client used to speak with emulation actor let clientClosed = this.client.close(); if (!isTabClosing) { yield clientClosed; } this.client = this.emulationFront = null; - // Undo the swap and return the content back to a normal tab - swap.stop(); + if (!isWindowClosing) { + // Undo the swap and return the content back to a normal tab + swap.stop(); + } this.destroyed = true; return true; }), connectToServer: Task.async(function* () { if (!DebuggerServer.initialized) { @@ -402,16 +407,17 @@ ResponsiveUI.prototype = { handleEvent(event) { let { browserWindow, tab } = this; switch (event.type) { case "message": this.handleMessage(event); break; + case "unload": case "TabClose": ResponsiveUIManager.closeIfNeeded(browserWindow, tab, { reason: event.type, }); break; } },
--- a/devtools/client/responsive.html/test/browser/browser.ini +++ b/devtools/client/responsive.html/test/browser/browser.ini @@ -3,16 +3,17 @@ tags = devtools subsuite = devtools # !e10s: RDM only works for remote tabs skip-if = !e10s support-files = devices.json doc_page_state.html geolocation.html head.js + touch.html !/devtools/client/commandline/test/helpers.js !/devtools/client/framework/test/shared-head.js !/devtools/client/framework/test/shared-redux-head.js !/devtools/client/inspector/test/shared-head.js !/devtools/client/shared/test/test-actor.js !/devtools/client/shared/test/test-actor-registry.js [browser_device_change.js] @@ -31,8 +32,9 @@ support-files = [browser_resize_cmd.js] [browser_screenshot_button.js] [browser_shutdown_close_sync.js] [browser_toolbox_computed_view.js] [browser_toolbox_rule_view.js] [browser_toolbox_swap_browsers.js] [browser_touch_simulation.js] [browser_viewport_basics.js] +[browser_window_close.js]
--- a/devtools/client/responsive.html/test/browser/browser_touch_simulation.js +++ b/devtools/client/responsive.html/test/browser/browser_touch_simulation.js @@ -1,25 +1,240 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; // Test global touch simulation button -const TEST_URL = "data:text/html;charset=utf-8,"; +const TEST_URL = `${URL_ROOT}touch.html`; +const PREF_DOM_META_VIEWPORT_ENABLED = "dom.meta-viewport.enabled"; addRDMTask(TEST_URL, function* ({ ui }) { - let { store, document } = ui.toolWindow; - let touchButton = document.querySelector("#global-touch-simulation-button"); + yield waitBootstrap(ui); + yield testWithNoTouch(ui); + yield enableTouchSimulation(ui); + yield testWithTouch(ui); + yield testWithMetaViewportEnabled(ui); + yield testWithMetaViewportDisabled(ui); + testTouchButton(ui); +}); + +function* testWithNoTouch(ui) { + yield injectEventUtils(ui); + yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () { + let { EventUtils } = content; + + let div = content.document.querySelector("div"); + let x = 0, y = 0; + + info("testWithNoTouch: Initial test parameter and mouse mouse outside div"); + x = -1; y = -1; + yield EventUtils.synthesizeMouse(div, x, y, + { type: "mousemove", isSynthesized: false }, content); + div.style.transform = "none"; + div.style.backgroundColor = ""; + + info("testWithNoTouch: Move mouse into the div element"); + yield EventUtils.synthesizeMouseAtCenter(div, + { type: "mousemove", isSynthesized: false }, content); + is(div.style.backgroundColor, "red", "mouseenter or mouseover should work"); + + info("testWithNoTouch: Drag the div element"); + yield EventUtils.synthesizeMouseAtCenter(div, + { type: "mousedown", isSynthesized: false }, content); + x = 100; y = 100; + yield EventUtils.synthesizeMouse(div, x, y, + { type: "mousemove", isSynthesized: false }, content); + is(div.style.transform, "none", "touchmove shouldn't work"); + yield EventUtils.synthesizeMouse(div, x, y, + { type: "mouseup", isSynthesized: false }, content); + + info("testWithNoTouch: Move mouse out of the div element"); + x = -1; y = -1; + yield EventUtils.synthesizeMouse(div, x, y, + { type: "mousemove", isSynthesized: false }, content); + is(div.style.backgroundColor, "blue", "mouseout or mouseleave should work"); + + info("testWithNoTouch: Click the div element"); + yield EventUtils.synthesizeClick(div); + is(div.dataset.isDelay, "false", + "300ms delay between touch events and mouse events should not work"); + }); +} + +function* testWithTouch(ui) { + yield injectEventUtils(ui); + + yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () { + let { EventUtils } = content; + + let div = content.document.querySelector("div"); + let x = 0, y = 0; + + info("testWithTouch: Initial test parameter and mouse mouse outside div"); + x = -1; y = -1; + yield EventUtils.synthesizeMouse(div, x, y, + { type: "mousemove", isSynthesized: false }, content); + div.style.transform = "none"; + div.style.backgroundColor = ""; + + info("testWithTouch: Move mouse into the div element"); + yield EventUtils.synthesizeMouseAtCenter(div, + { type: "mousemove", isSynthesized: false }, content); + isnot(div.style.backgroundColor, "red", + "mouseenter or mouseover should not work"); - // Wait until the viewport has been added - yield waitUntilState(store, state => state.viewports.length == 1); - yield waitForFrameLoad(ui, TEST_URL); + info("testWithTouch: Drag the div element"); + yield EventUtils.synthesizeMouseAtCenter(div, + { type: "mousedown", isSynthesized: false }, content); + x = 100; y = 100; + yield EventUtils.synthesizeMouse(div, x, y, + { type: "mousemove", isSynthesized: false }, content); + isnot(div.style.transform, "none", "touchmove should work"); + yield EventUtils.synthesizeMouse(div, x, y, + { type: "mouseup", isSynthesized: false }, content); + + info("testWithTouch: Move mouse out of the div element"); + x = -1; y = -1; + yield EventUtils.synthesizeMouse(div, x, y, + { type: "mousemove", isSynthesized: false }, content); + isnot(div.style.backgroundColor, "blue", + "mouseout or mouseleave should not work"); + }); +} + +function* testWithMetaViewportEnabled(ui) { + yield SpecialPowers.pushPrefEnv({set: [[PREF_DOM_META_VIEWPORT_ENABLED, true]]}); + + yield injectEventUtils(ui); + + yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () { + let { synthesizeClick } = content.EventUtils; + + let meta = content.document.querySelector("meta[name=viewport]"); + let div = content.document.querySelector("div"); + div.dataset.isDelay = "false"; + + info("testWithMetaViewportEnabled: " + + "click the div element with <meta name='viewport'>"); + meta.content = ""; + yield synthesizeClick(div); + is(div.dataset.isDelay, "true", + "300ms delay between touch events and mouse events should work"); + + info("testWithMetaViewportEnabled: " + + "click the div element with " + + "<meta name='viewport' content='user-scalable=no'>"); + meta.content = "user-scalable=no"; + yield synthesizeClick(div); + is(div.dataset.isDelay, "false", + "300ms delay between touch events and mouse events should not work"); + + info("testWithMetaViewportEnabled: " + + "click the div element with " + + "<meta name='viewport' content='minimum-scale=maximum-scale'>"); + meta.content = "minimum-scale=maximum-scale"; + yield synthesizeClick(div); + is(div.dataset.isDelay, "false", + "300ms delay between touch events and mouse events should not work"); + + info("testWithMetaViewportEnabled: " + + "click the div element with " + + "<meta name='viewport' content='width=device-width'>"); + meta.content = "width=device-width"; + yield synthesizeClick(div); + is(div.dataset.isDelay, "false", + "300ms delay between touch events and mouse events should not work"); + }); +} + +function* testWithMetaViewportDisabled(ui) { + yield SpecialPowers.pushPrefEnv({set: [[PREF_DOM_META_VIEWPORT_ENABLED, false]]}); + + yield injectEventUtils(ui); + + yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () { + let { synthesizeClick } = content.EventUtils; + + let meta = content.document.querySelector("meta[name=viewport]"); + let div = content.document.querySelector("div"); + div.dataset.isDelay = "false"; + + info("testWithMetaViewportDisabled: click the div with <meta name='viewport'>"); + meta.content = ""; + yield synthesizeClick(div); + is(div.dataset.isDelay, "true", + "300ms delay between touch events and mouse events should work"); + }); +} + +function testTouchButton(ui) { + let { document } = ui.toolWindow; + let touchButton = document.querySelector("#global-touch-simulation-button"); ok(!touchButton.classList.contains("active"), "Touch simulation is not active by default."); touchButton.click(); ok(touchButton.classList.contains("active"), "Touch simulation is started on click."); -}); + + touchButton.click(); + + ok(!touchButton.classList.contains("active"), + "Touch simulation is stopped on click."); +} + +function* waitBootstrap(ui) { + let { store } = ui.toolWindow; + + yield waitUntilState(store, state => state.viewports.length == 1); + yield waitForFrameLoad(ui, TEST_URL); +} + +function* injectEventUtils(ui) { + yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () { + if ("EventUtils" in content) { + return; + } + + let EventUtils = content.EventUtils = {}; + + EventUtils.window = {}; + EventUtils.parent = EventUtils.window; + /* eslint-disable camelcase */ + EventUtils._EU_Ci = Components.interfaces; + EventUtils._EU_Cc = Components.classes; + /* eslint-enable camelcase */ + // EventUtils' `sendChar` function relies on the navigator to synthetize events. + EventUtils.navigator = content.navigator; + EventUtils.KeyboardEvent = content.KeyboardEvent; + + EventUtils.synthesizeClick = element => new Promise(resolve => { + element.addEventListener("click", function onClick() { + element.removeEventListener("click", onClick); + resolve(); + }); + + EventUtils.synthesizeMouseAtCenter(element, + { type: "mousedown", isSynthesized: false }, content); + EventUtils.synthesizeMouseAtCenter(element, + { type: "mouseup", isSynthesized: false }, content); + }); + + Services.scriptloader.loadSubScript( + "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils); + }); +} + +const enableTouchSimulation = ui => new Promise( + Task.async(function* (resolve) { + let browser = ui.getViewportBrowser(); + + browser.addEventListener("mozbrowserloadend", function onLoad() { + browser.removeEventListener("mozbrowserloadend", onLoad); + resolve(); + }); + + yield ui.updateTouchSimulation(true); + }));
new file mode 100644 --- /dev/null +++ b/devtools/client/responsive.html/test/browser/browser_window_close.js @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(function* () { + let newWindowPromise = BrowserTestUtils.waitForNewWindow(); + window.open("data:text/html;charset=utf-8,", "_blank"); + let newWindow = yield newWindowPromise; + + newWindow.focus(); + yield once(newWindow.gBrowser, "load", true); + + let tab = newWindow.gBrowser.selectedTab; + yield openRDM(tab); + + // Close the window on a tab with an active responsive design UI and + // wait for the UI to gracefully shutdown. This has leaked the window + // in the past. + ok(ResponsiveUIManager.isActiveForTab(tab), + "ResponsiveUI should be active for tab when the window is closed"); + let offPromise = once(ResponsiveUIManager, "off"); + yield BrowserTestUtils.closeWindow(newWindow); + yield offPromise; +});
new file mode 100644 --- /dev/null +++ b/devtools/client/responsive.html/test/browser/touch.html @@ -0,0 +1,86 @@ +<!DOCTYPE html> + +<meta charset="utf-8" /> +<meta name="viewport" /> +<title>test</title> + + +<style> + div { + border :1px solid red; + width: 100px; height: 100px; + } +</style> + +<div data-is-delay="false"></div> + +<script type="text/javascript;version=1.8"> + "use strict"; + let div = document.querySelector("div"); + let initX, initY; + let previousEvent = "", touchendTime = 0; + let updatePreviousEvent = function (e) { + previousEvent = e.type; + }; + + div.style.transform = "none"; + div.style.backgroundColor = ""; + + div.addEventListener("touchstart", function (evt) { + let touch = evt.changedTouches[0]; + initX = touch.pageX; + initY = touch.pageY; + updatePreviousEvent(evt); + }, true); + + div.addEventListener("touchmove", function (evt) { + let touch = evt.changedTouches[0]; + let deltaX = touch.pageX - initX; + let deltaY = touch.pageY - initY; + div.style.transform = "translate(" + deltaX + "px, " + deltaY + "px)"; + updatePreviousEvent(evt); + }, true); + + div.addEventListener("touchend", function (evt) { + if (!evt.touches.length) { + div.style.transform = "none"; + } + touchendTime = performance.now(); + updatePreviousEvent(evt); + }, true); + + div.addEventListener("mouseenter", function (evt) { + div.style.backgroundColor = "red"; + updatePreviousEvent(evt); + }, true); + div.addEventListener("mouseover", function(evt) { + div.style.backgroundColor = "red"; + updatePreviousEvent(evt); + }, true); + + div.addEventListener("mouseout", function (evt) { + div.style.backgroundColor = "blue"; + updatePreviousEvent(evt); + }, true); + + div.addEventListener("mouseleave", function (evt) { + div.style.backgroundColor = "blue"; + updatePreviousEvent(evt); + }, true); + + div.addEventListener("mousedown", function (evt) { + if (previousEvent === "touchend" && touchendTime !== 0) { + let now = performance.now(); + div.dataset.isDelay = ((now - touchendTime) >= 300); + } else { + div.dataset.isDelay = false; + } + updatePreviousEvent(evt); + }, true); + + div.addEventListener("mousemove", updatePreviousEvent, true); + + div.addEventListener("mouseup", updatePreviousEvent, true); + + div.addEventListener("click", updatePreviousEvent, true); +</script>
--- a/gfx/skia/skia/include/core/SkTypes.h +++ b/gfx/skia/skia/include/core/SkTypes.h @@ -4,16 +4,29 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkTypes_DEFINED #define SkTypes_DEFINED // IWYU pragma: begin_exports + +// In at least two known scenarios when using GCC with libc++: +// * GCC 4.8 targeting ARMv7 with NEON +// * GCC 4.9 targeting ARMv8 64 bit +// we need to typedef float float32_t (or include <arm_neon.h> which does that) +// before #including <memory>. This makes no sense. I'm not very interested in +// understanding why... these are old, bizarre platform configuration that we +// should just let die. +#if defined(MOZ_B2G) && defined(__GNUC__) && __GNUC__ == 4 + typedef float float32_t; + #include <memory> +#endif + #include "SkPreConfig.h" #include "SkUserConfig.h" #include "SkPostConfig.h" #include <stddef.h> #include <stdint.h> #if defined(SK_ARM_HAS_NEON) #include <arm_neon.h>
--- a/old-configure.in +++ b/old-configure.in @@ -2487,17 +2487,17 @@ AC_SUBST(MOZ_B2G_VERSION) dnl ======================================================== dnl Ensure Android SDK and build-tools versions depending on dnl mobile target. dnl ======================================================== if test -z "$gonkdir" ; then case "$MOZ_BUILD_APP" in mobile/android) - MOZ_ANDROID_SDK(23, 23.0.3) + MOZ_ANDROID_SDK(23, 23.0.3 23.0.1) ;; esac fi dnl ======================================================== dnl = dnl = Toolkit Options dnl =
--- a/security/manager/locales/en-US/chrome/pippki/deviceManager.dtd +++ b/security/manager/locales/en-US/chrome/pippki/deviceManager.dtd @@ -1,17 +1,17 @@ <!-- 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/. --> <!ENTITY devmgr.title "Device Manager"> -<!-- LOCALIZATION NOTE (devmgr.style): This is CSS style for Device Manager +<!-- LOCALIZATION NOTE (devmgr.style2): This is CSS style for Device Manager window size. Don't translate "width" nor "height". Adjust the numbers to make window contents fit. --> -<!ENTITY devmgr.style "width: 52em; height: 32em;"> +<!ENTITY devmgr.style2 "width: 67em; height: 32em;"> <!ENTITY devmgr.devlist.label "Security Modules and Devices"> <!ENTITY devmgr.details.title "Details"> <!ENTITY devmgr.details.title2 "Value"> <!ENTITY devmgr.button.login.label "Log In"> <!ENTITY devmgr.button.login.accesskey "n"> <!ENTITY devmgr.button.logout.label "Log Out">
--- a/security/manager/pki/resources/content/device_manager.xul +++ b/security/manager/pki/resources/content/device_manager.xul @@ -9,81 +9,81 @@ <!ENTITY % deviceManangerDTD SYSTEM "chrome://pippki/locale/deviceManager.dtd"> %deviceManangerDTD; <!ENTITY % pippkiDTD SYSTEM "chrome://pippki/locale/pippki.dtd" > %pippkiDTD; ]> <dialog id="devicemanager" windowtype="mozilla:devicemanager" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title="&devmgr.title;" - style="&devmgr.style;" + style="&devmgr.style2;" persist="screenX screenY width height" onload="LoadModules();" onunload="DeregisterSmartCardObservers();" buttons="accept"> <stringbundleset id="stringbundleset"> <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/> <stringbundle id="pipnss_bundle" src="chrome://pipnss/locale/pipnss.properties"/> </stringbundleset> <script type="application/javascript" src="chrome://pippki/content/device_manager.js"/> <grid flex="1" style="margin:5px"> <columns> - <column flex="1"/> - <column flex="3"/> + <column flex="1"/> + <column flex="3"/> <column/> </columns> <rows> <row flex="1"> <vbox> <!-- List of devices --> <tree id="device_tree" seltype="single" onselect="enableButtons();" hidecolumnpicker="true" flex="1" style="min-width: 15em"> - <treecols> + <treecols> <treecol id="deviceCol" flex="1" primary="true" label="&devmgr.devlist.label;"/> </treecols> <treechildren id="device_list"/> </tree> </vbox> <!-- / List of devices --> <vbox> <!-- Device status --> <tree id="info_tree" seltype="single" hidecolumnpicker="true" flex="1" style="min-width: 10em"> - <treecols> - <treecol id="title1Col" flex="5" primary="true" label="&devmgr.details.title;"/> - <treecol id="title2Col" flex="7" label="&devmgr.details.title2;"/> + <treecols> + <treecol id="title1Col" flex="5" primary="true" label="&devmgr.details.title;"/> + <treecol id="title2Col" flex="7" label="&devmgr.details.title2;"/> </treecols> <treechildren id="info_list"/> </tree> </vbox> <!-- / Device status --> <vbox> <!-- Buttons for manipulating devices --> - <button id="login_button" + <button id="login_button" label="&devmgr.button.login.label;" accesskey="&devmgr.button.login.accesskey;" - oncommand="doLogin();" disabled="true"/> - <button id="logout_button" + oncommand="doLogin();" disabled="true"/> + <button id="logout_button" label="&devmgr.button.logout.label;" accesskey="&devmgr.button.logout.accesskey;" - oncommand="doLogout();" disabled="true"/> - <button id="change_pw_button" + oncommand="doLogout();" disabled="true"/> + <button id="change_pw_button" label="&devmgr.button.changepw.label;" accesskey="&devmgr.button.changepw.accesskey;" - oncommand="changePassword();" disabled="true"/> - <button id="load_button" + oncommand="changePassword();" disabled="true"/> + <button id="load_button" label="&devmgr.button.load.label;" accesskey="&devmgr.button.load.accesskey;" - oncommand="doLoad();"/> - <button id="unload_button" + oncommand="doLoad();"/> + <button id="unload_button" label="&devmgr.button.unload.label;" accesskey="&devmgr.button.unload.accesskey;" - oncommand="doUnload();" disabled="true"/> - <button id="fipsbutton" + oncommand="doUnload();" disabled="true"/> + <button id="fipsbutton" label="" accesskey="&devmgr.button.fips.accesskey;" oncommand="toggleFIPS();"/> </vbox> <!-- / Buttons for manipulating devices --> </row> </rows> </grid>
--- a/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl +++ b/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl @@ -10,21 +10,25 @@ interface nsIDOMElement; interface nsIDOMWindow; [scriptable, uuid(425f73b9-b2db-4e8a-88c5-9ac2512934ce)] interface nsILoginManagerPrompter : nsISupports { /** * Initialize the prompter. Must be called before using other interfaces. * * @param aWindow - * The in which the user is doing some login-related action that's + * The window in which the user is doing some login-related action that's * resulting in a need to prompt them for something. The prompt * will be associated with this window (or, if a notification bar * is being used, topmost opener in some cases). * + * aWindow can be null if there is no associated window, e.g. in a JSM + * or Sandbox. In this case there will be no checkbox to save the login + * since the window is needed to know if this is a private context. + * * If this window is a content window, the corresponding window and browser * elements will be calculated. If this window is a chrome window, the * corresponding browser element needs to be set using setBrowser. */ void init(in nsIDOMWindow aWindow); /** * The browser this prompter is being created for.
--- a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js +++ b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js @@ -264,16 +264,17 @@ LoginManagerPrompter.prototype = { if (this._chromeWindow) { return PrivateBrowsingUtils.isWindowPrivate(this._chromeWindow); } // If we don't that we're in private browsing mode if the caller did // not provide a window. The callers which really care about this // will indeed pass down a window to us, and for those who don't, // we can just assume that we don't want to save the entered login // information. + this.log("We have no chromeWindow so assume we're in a private context"); return true; }, /* ---------- nsIAuthPrompt prompts ---------- */ @@ -687,18 +688,22 @@ LoginManagerPrompter.prototype = { }, /* ---------- nsILoginManagerPrompter prompts ---------- */ - init : function (aWindow, aFactory) { - if (aWindow instanceof Ci.nsIDOMChromeWindow) { + init : function (aWindow = null, aFactory = null) { + if (!aWindow) { + // There may be no applicable window e.g. in a Sandbox or JSM. + this._chromeWindow = null; + this._browser = null; + } else if (aWindow instanceof Ci.nsIDOMChromeWindow) { this._chromeWindow = aWindow; // needs to be set explicitly using setBrowser this._browser = null; } else { let {win, browser} = this._getChromeWindow(aWindow); this._chromeWindow = win; this._browser = browser; }
--- a/toolkit/components/passwordmgr/test/mochitest/mochitest.ini +++ b/toolkit/components/passwordmgr/test/mochitest/mochitest.ini @@ -44,14 +44,16 @@ skip-if = toolkit == 'android' # autocom [test_input_events.html] [test_input_events_for_identical_values.html] [test_maxlength.html] [test_passwords_in_type_password.html] [test_prompt.html] skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts [test_prompt_http.html] skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts +[test_prompt_noWindow.html] +skip-if = e10s || toolkit == 'android' # Tests desktop prompts. e10s: bug 1217876 [test_prompt_promptAuth.html] skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts [test_prompt_promptAuth_proxy.html] skip-if = e10s || os == "linux" || toolkit == 'android' # Tests desktop prompts [test_recipe_login_fields.html] [test_xhr_2.html]
new file mode 100644 --- /dev/null +++ b/toolkit/components/passwordmgr/test/mochitest/test_prompt_noWindow.html @@ -0,0 +1,81 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test HTTP auth prompts by loading authenticate.sjs with no window</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script> + <script type="text/javascript" src="pwmgr_common.js"></script> + <script type="text/javascript" src="prompt_common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> + +<div id="content" style="display: none"> +</div> + +<pre id="test"> +<script class="testbody" type="text/javascript"> + +// Force parent to not look for tab-modal prompts, as they're not used for auth prompts. +isTabModal = false; + +let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js")); + +runInParent(() => { + const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + Cu.import("resource://gre/modules/Services.jsm"); + + let login = Cc["@mozilla.org/login-manager/loginInfo;1"]. + createInstance(Ci.nsILoginInfo); + login.init("http://mochi.test:8888", null, "mochitest", + "mochiuser1", "mochipass1", "", ""); + Services.logins.addLogin(login); +}); + +add_task(function* test_sandbox_xhr() { + let state = { + msg : "http://mochi.test:8888 is requesting your username and password. The site says: “mochitest”", + title : "Authentication Required", + textValue : "mochiuser1", + passValue : "mochipass1", + iconClass : "authentication-icon question-icon", + titleHidden : true, + textHidden : false, + passHidden : false, + checkHidden : true, + checkMsg : "", + checked : false, + focused : "textField", + defButton : "button0", + }; + let action = { + buttonClick : "ok", + }; + let promptDone = handlePrompt(state, action); + + let url = new URL("authenticate.sjs?user=mochiuser1&pass=mochipass1", window.location.href); + let sandboxConstructor = SpecialPowers.Cu.Sandbox; + let sandbox = new sandboxConstructor(this, {wantXrays: true}); + function sandboxedRequest(url) { + let req = SpecialPowers.Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(SpecialPowers.Ci.nsIXMLHttpRequest); + req.open("GET", url, true); + req.send(null); + } + + let loginModifiedPromise = promiseStorageChanged(["modifyLogin"]); + sandbox.sandboxedRequest = sandboxedRequest(url); + info("send the XHR request in the sandbox"); + SpecialPowers.Cu.evalInSandbox("sandboxedRequest;", sandbox); + + yield promptDone; + info("prompt shown, waiting for metadata updates"); + // Ensure the timeLastUsed and timesUsed metadata are updated. + yield loginModifiedPromise; +}); +</script> +</pre> +</body> +</html>
--- a/toolkit/content/tests/chrome/findbar_window.xul +++ b/toolkit/content/tests/chrome/findbar_window.xul @@ -147,16 +147,17 @@ yield openFindbar(); yield testFailedStringReset(); gFindBar.close(); yield testQuickFindClose(); // TODO: This doesn't seem to work when the findbar is connected to a // remote browser element. if (!gBrowser.hasAttribute("remote")) yield testFindAgainNotFound(); + yield testToggleEntireWord(); } function* testFindbarSelection() { function checkFindbarState(aTestName, aExpSelection) { ok(!gFindBar.hidden, "testFindbarSelection: failed to open findbar: " + aTestName); ok(document.commandDispatcher.focusedElement == gFindBar._findField.inputField, "testFindbarSelection: find field is not focused: " + aTestName); if (!gHasFindClipboard) { @@ -230,19 +231,19 @@ }; setTimeout(_delayedCheckStatusText, 100); }); } function promiseFindResult() { return new Promise(resolve => { let listener = { - onFindResult: function() { + onFindResult: function(result) { gFindBar.browser.finder.removeResultListener(listener); - resolve(); + resolve(result); } }; gFindBar.browser.finder.addResultListener(listener); }); } function promiseMatchesCountResult() { return new Promise(resolve => { @@ -645,16 +646,34 @@ yield enterStringIntoFindField(SEARCH_TEXT); gFindBar.close(); ok(gFindBar.hidden, "The findbar is closed."); promise = promiseFindResult(); gFindBar.onFindAgainCommand(); yield promise; ok(gFindBar.hidden, "Successful Find Again leaves the find bar closed."); } + + function* testToggleEntireWord() { + yield openFindbar(); + let promise = promiseFindResult(); + yield enterStringIntoFindField("Tex", false); + let result = yield promise; + is(result.result, Ci.nsITypeAheadFind.FIND_FOUND, "Text should be found"); + + yield new Promise(resolve => setTimeout(resolve, ITERATOR_TIMEOUT)); + promise = promiseFindResult(); + let check = gFindBar.getElement("find-entire-word"); + check.click(); + result = yield promise; + is(result.result, Ci.nsITypeAheadFind.FIND_NOTFOUND, "Text should NOT be found"); + + check.click(); + gFindBar.close(true); + } ]]></script> <commandset> <command id="cmd_find" oncommand="document.getElementById('FindToolbar').onFindCommand();"/> </commandset> <browser type="content-primary" flex="1" id="content" src="about:blank"/> <browser type="content-primary" flex="1" id="content-remote" remote="true" src="about:blank"/> <findbar id="FindToolbar" browserid="content"/>
--- a/toolkit/content/widgets/findbar.xml +++ b/toolkit/content/widgets/findbar.xml @@ -194,17 +194,17 @@ oncommand="_setCaseSensitivity(this.checked ? 1 : 0);" type="checkbox" xbl:inherits="accesskey=matchcaseaccesskey"/> <xul:toolbarbutton anonid="find-entire-word" class="findbar-entire-word findbar-button tabbable" label="&entireWord.label;" accesskey="&entireWord.accesskey;" tooltiptext="&entireWord.tooltiptext;" - oncommand="_setEntireWord(this.checked);" + oncommand="toggleEntireWord(this.checked);" type="checkbox" xbl:inherits="accesskey=entirewordaccesskey"/> <xul:label anonid="match-case-status" class="findbar-find-fast"/> <xul:label anonid="entire-word-status" class="findbar-find-fast"/> <xul:label anonid="found-matches" class="findbar-find-fast found-matches" hidden="true"/> <xul:image anonid="find-status-icon" class="findbar-find-fast find-status-icon"/> <xul:description anonid="find-status" control="findbar-textbox" @@ -338,19 +338,17 @@ case "accessibility.typeaheadfind.linksonly": this._self._typeAheadLinksOnly = prefsvc.getBoolPref(aPrefName); break; case "accessibility.typeaheadfind.casesensitive": this._self._setCaseSensitivity(prefsvc.getIntPref(aPrefName)); break; case "findbar.entireword": this._self._entireWord = prefsvc.getBoolPref(aPrefName); - this._self._updateEntireWord(); - // Update the matches count. - this._updateMatchesCount(this.nsITypeAheadFind.FIND_FOUND); + this._self.toggleEntireWord(this._self._entireWord, true); break; case "findbar.highlightAll": this._self.toggleHighlight(prefsvc.getBoolPref(aPrefName), true); break; case "findbar.modalHighlight": this._self._useModalHighlight = prefsvc.getBoolPref(aPrefName); if (this._self.browser.finder) this._self.browser.finder.onModalHighlightChange(this._self._useModalHighlight); @@ -633,17 +631,17 @@ this._dispatchFindEvent("casesensitivitychange"); ]]></body> </method> <!-- - Updates the entire-word mode of the findbar and its UI. --> - <method name="_updateEntireWord"> + <method name="_setEntireWord"> <body><![CDATA[ let entireWord = this._entireWord; let checkbox = this.getElement("find-entire-word"); let statusLabel = this.getElement("entire-word-status"); checkbox.checked = entireWord; statusLabel.value = entireWord ? this._entireWordStr : ""; @@ -657,27 +655,28 @@ ]]></body> </method> <!-- - Sets the findbar entire-word mode - @param aEntireWord (boolean) - Whether or not entire-word mode should be turned on. --> - <method name="_setEntireWord"> + <method name="toggleEntireWord"> <parameter name="aEntireWord"/> + <parameter name="aFromPrefObserver"/> <body><![CDATA[ - let prefsvc = - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); + if (!aFromPrefObserver) { + // Just set the pref; our observer will change the find bar behavior. + this._prefsvc.setBoolPref("findbar.entireword", aEntireWord); + return; + } - // Just set the pref; our observer will change the find bar behavior. - prefsvc.setBoolPref("findbar.entireword", aEntireWord); - - this._maybeHighlightAll(); + this._findFailedString = null; + this._find(); ]]></body> </method> <field name="_strBundle">null</field> <property name="strBundle"> <getter><![CDATA[ if (!this._strBundle) { this._strBundle = @@ -966,17 +965,17 @@ if (node == wrapper || node == foundMatches) continue; node.hidden = showMinimalUI; } this.getElement("find-next").hidden = this.getElement("find-previous").hidden = showMinimalUI; foundMatches.hidden = showMinimalUI || !foundMatches.value; this._updateCaseSensitivity(); - this._updateEntireWord(); + this._setEntireWord(); this._setHighlightAll(); if (showMinimalUI) this._findField.classList.add("minimal"); else this._findField.classList.remove("minimal"); if (this._findMode == this.FIND_TYPEAHEAD) @@ -1014,17 +1013,17 @@ // initial prefilling is ignored if it hasn't happened yet. if (this._startFindDeferred) { this._startFindDeferred.resolve(); this._startFindDeferred = null; } this._enableFindButtons(val); this._updateCaseSensitivity(val); - this._updateEntireWord(); + this._setEntireWord(); this.browser.finder.fastFind(val, this._findMode == this.FIND_LINKS, this._findMode != this.FIND_NORMAL); } if (this._findMode != this.FIND_NORMAL) this._setFindCloseTimeout();
--- a/toolkit/modules/FinderHighlighter.jsm +++ b/toolkit/modules/FinderHighlighter.jsm @@ -401,18 +401,17 @@ FinderHighlighter.prototype = { if (this.iterator._areParamsEqual(params, dict.lastIteratorParams)) return; if (params) this.highlight(true, params.word, params.linksOnly); } return; } - // Place the match placeholder on top of the current found range. - if (data.result == Ci.nsITypeAheadFind.FIND_NOTFOUND || !foundRange) { + if (data.result == Ci.nsITypeAheadFind.FIND_NOTFOUND || !data.searchString || !foundRange) { this.hide(); return; } if (foundRange !== dict.currentFoundRange || data.findAgain) { dict.currentFoundRange = foundRange; let textContent = this._getRangeContentArray(foundRange); @@ -441,17 +440,17 @@ FinderHighlighter.prototype = { if (outlineNode) { if (dict.animation) dict.animation.finish(); dict.animation = outlineNode.setAnimationForElement(kModalOutlineId, Cu.cloneInto(kModalOutlineAnim.keyframes, window), kModalOutlineAnim.duration); dict.animation.onfinish = () => dict.animation = null; } - if (this._highlightAll && data.searchString) + if (this._highlightAll) this.highlight(true, data.searchString, data.linksOnly); }, /** * Invalidates the list by clearing the map of highlighted ranges that we * keep to build the mask for. */ clear(window = null) {
--- a/toolkit/modules/tests/browser/browser_FinderHighlighter.js +++ b/toolkit/modules/tests/browser/browser_FinderHighlighter.js @@ -385,8 +385,37 @@ add_task(function* testHideOnLocationCha insertCalls: [0, 0], removeCalls: [1, 2] }); yield BrowserTestUtils.loadURI(browser, url); yield promise; yield BrowserTestUtils.removeTab(tab); }); + +add_task(function* testHideOnClear() { + let url = kFixtureBaseURL + "file_FinderSample.html"; + yield BrowserTestUtils.withNewTab(url, function* (browser) { + let findbar = gBrowser.getFindBar(); + yield promiseOpenFindbar(findbar); + + let word = "Roland"; + let expectedResult = { + rectCount: 1, + insertCalls: [2, 4], + removeCalls: [1, 2] + }; + let promise = promiseTestHighlighterOutput(browser, word, expectedResult); + yield promiseEnterStringIntoFindField(findbar, word); + yield promise; + + yield new Promise(resolve => setTimeout(resolve, kIteratorTimeout)); + promise = promiseTestHighlighterOutput(browser, "", { + rectCount: 0, + insertCalls: [0, 0], + removeCalls: [1, 2] + }); + findbar.clear(); + yield promise; + + findbar.close(true); + }); +});