Browse Source

early stuff

Frank-Rainer Grahl 1 month ago
parent
commit
bd6c4bf3bb
41 changed files with 8571 additions and 49 deletions
  1. 74 0
      mozilla-release/patches/1309198-59a1.patch
  2. 3 3
      mozilla-release/patches/1373258-58a1.patch
  3. 126 0
      mozilla-release/patches/1381186-1-57a1.patch
  4. 152 0
      mozilla-release/patches/1381186-2-57a1.patch
  5. 115 0
      mozilla-release/patches/1382092-1-57a1.patch
  6. 314 0
      mozilla-release/patches/1382092-2-57a1.patch
  7. 449 0
      mozilla-release/patches/1382092-3-57a1.patch
  8. 23 0
      mozilla-release/patches/1382092-4-57a1.patch
  9. 635 0
      mozilla-release/patches/1382388-58a1.patch
  10. 43 0
      mozilla-release/patches/1390018-57a1.patch
  11. 139 0
      mozilla-release/patches/1399780-58a1.patch
  12. 33 0
      mozilla-release/patches/1403743-59a1.patch
  13. 30 0
      mozilla-release/patches/1415572-59a1.patch
  14. 22 0
      mozilla-release/patches/1417657-59a1.patch
  15. 41 0
      mozilla-release/patches/1419902-59a1.patch
  16. 316 0
      mozilla-release/patches/1420589-1-59a1.patch
  17. 163 0
      mozilla-release/patches/1420589-2-59a1.patch
  18. 203 0
      mozilla-release/patches/1420589-3-59a1.patch
  19. 173 0
      mozilla-release/patches/1420589-4-59a1.patch
  20. 154 0
      mozilla-release/patches/1420589-5-59a1.patch
  21. 452 0
      mozilla-release/patches/1420589-6-59a1.patch
  22. 717 0
      mozilla-release/patches/1420589-7-59a1.patch
  23. 513 0
      mozilla-release/patches/1420589-8-59a1.patch
  24. 365 0
      mozilla-release/patches/1420589-9-59a1.patch
  25. 122 0
      mozilla-release/patches/1422869-59a1.patch
  26. 35 0
      mozilla-release/patches/1425932-59a1.patch
  27. 39 0
      mozilla-release/patches/1426388-59a1.patch
  28. 118 0
      mozilla-release/patches/1426527-59a1.patch
  29. 113 0
      mozilla-release/patches/1426728-59a1.patch
  30. 31 0
      mozilla-release/patches/1426868-59a1.patch
  31. 11 10
      mozilla-release/patches/1428685-59a1.patch
  32. 33 0
      mozilla-release/patches/1432870-60a1.patch
  33. 1496 0
      mozilla-release/patches/1433671-2-1444375-61a1.patch
  34. 182 0
      mozilla-release/patches/1440257-60a1.patch
  35. 677 0
      mozilla-release/patches/1447993-1-61a1.patch
  36. 389 0
      mozilla-release/patches/1447993-2-61a1.patch
  37. 4 4
      mozilla-release/patches/1457338-62a1.patch
  38. 22 22
      mozilla-release/patches/1465585-3-std-62a1.patch
  39. 4 4
      mozilla-release/patches/1675319-84a1.patch
  40. 6 6
      mozilla-release/patches/TOP-NOBUG-enableCE-25318.patch
  41. 34 0
      mozilla-release/patches/series

+ 74 - 0
mozilla-release/patches/1309198-59a1.patch

@@ -0,0 +1,74 @@
+# HG changeset patch
+# User Hiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
+# Date 1513824023 -32400
+# Node ID 26b8e4fcf9992db4e092312e28c26d2dac8d68e3
+# Parent  51feeb32d06dae76d62bfce41b58475cfde4282b
+Bug 1309198 - Test case that document is discarded while Document.getAnimations(). r=smaug
+
+diff --git a/dom/animation/test/crashtests/1309198-1.html b/dom/animation/test/crashtests/1309198-1.html
+new file mode 100644
+--- /dev/null
++++ b/dom/animation/test/crashtests/1309198-1.html
+@@ -0,0 +1,40 @@
++<script>
++function start() {
++  o53=document.createElement('frameset');
++  o254=document.createElement('iframe');
++  o280=document.createElement('audio');
++  o317=document.documentElement;
++  o508=document.createElement('li');
++  o508.appendChild(o317);
++  o590=document.createElement('li');
++  o594=document.createElement('track');
++  o280.appendChild(o594);
++  o647=document.createElement('ol');
++  o654=document.createElement('li');
++  o647.appendChild(o654);
++  o654.insertAdjacentHTML('beforebegin','<iframe>');
++  document.write('<html><body></body></html>');
++  o955=document.documentElement;
++  document.documentElement.appendChild(o647);
++  o590.appendChild(o955);
++  document.close();
++  document.write('<html><body></body></html>');
++  document.documentElement.appendChild(o590);
++  document.documentElement.appendChild(o254);
++  o280.controls^=1;
++  SpecialPowers.forceGC();
++  o317.insertAdjacentHTML('afterend','<iframe>');
++  document.documentElement.appendChild(o280);
++  o2695=document.implementation.createHTMLDocument();
++  o2695.body.appendChild(o254);
++  o53.onerror=f0;
++  document.documentElement.appendChild(o508);
++  o2803=frames[1].document;
++  o2803.getAnimations();
++}
++function f0() {
++  o2803.write('<html><body></body></html>');
++  SpecialPowers.forceCC();
++}
++</script>
++<body onload="start()"></body>
+diff --git a/dom/animation/test/crashtests/crashtests.list b/dom/animation/test/crashtests/crashtests.list
+--- a/dom/animation/test/crashtests/crashtests.list
++++ b/dom/animation/test/crashtests/crashtests.list
+@@ -9,16 +9,17 @@ pref(dom.animations-api.core.enabled,tru
+ pref(dom.animations-api.core.enabled,true) load 1272475-1.html
+ pref(dom.animations-api.core.enabled,true) load 1272475-2.html
+ pref(dom.animations-api.core.enabled,true) load 1278485-1.html
+ pref(dom.animations-api.core.enabled,true) load 1277272-1.html
+ pref(dom.animations-api.core.enabled,true) load 1282691-1.html
+ pref(dom.animations-api.core.enabled,true) load 1291413-1.html
+ pref(dom.animations-api.core.enabled,true) load 1291413-2.html
+ pref(dom.animations-api.core.enabled,true) load 1304886-1.html
++pref(dom.animations-api.core.enabled,true) load 1309198-1.html
+ pref(dom.animations-api.core.enabled,true) load 1322382-1.html
+ pref(dom.animations-api.core.enabled,true) load 1322291-1.html
+ pref(dom.animations-api.core.enabled,true) load 1322291-2.html
+ pref(dom.animations-api.core.enabled,true) load 1323114-1.html
+ pref(dom.animations-api.core.enabled,true) load 1323114-2.html
+ pref(dom.animations-api.core.enabled,true) load 1324554-1.html
+ pref(dom.animations-api.core.enabled,true) load 1325193-1.html
+ pref(dom.animations-api.core.enabled,true) load 1330190-1.html
+

+ 3 - 3
mozilla-release/patches/1373258-58a1.patch

@@ -2,7 +2,7 @@
 # User Ursula Sarracini <usarracini@mozilla.com>
 # User Ursula Sarracini <usarracini@mozilla.com>
 # Date 1496671446 14400
 # Date 1496671446 14400
 # Node ID d05449eb1e035689552164417468a6e9f6b9e5cd
 # Node ID d05449eb1e035689552164417468a6e9f6b9e5cd
-# Parent  e9c308c73e08b4eecd448150db90e39875ea775a
+# Parent  2253055b85340ed35bff0507b12a93dc80123182
 Bug 1373258 - Convert PageThumbsProtocol.js to PageThumbsProtocol.cpp r=adw
 Bug 1373258 - Convert PageThumbsProtocol.js to PageThumbsProtocol.cpp r=adw
 
 
 MozReview-Commit-ID: tdNee2EPdV
 MozReview-Commit-ID: tdNee2EPdV
@@ -128,9 +128,9 @@ diff --git a/browser/installer/package-manifest.in b/browser/installer/package-m
  @RESPATH@/components/nsSearchService.js
  @RESPATH@/components/nsSearchService.js
  @RESPATH@/components/nsSearchSuggestions.js
  @RESPATH@/components/nsSearchSuggestions.js
  @RESPATH@/components/nsSidebar.js
  @RESPATH@/components/nsSidebar.js
- #if defined(NIGHTLY_BUILD) && defined(MOZ_BUILD_APP_IS_BROWSER)
+ #ifdef NIGHTLY_BUILD
  @RESPATH@/components/payments.manifest
  @RESPATH@/components/payments.manifest
-@@ -446,17 +447,17 @@
+@@ -447,17 +448,17 @@
  @RESPATH@/components/toolkitplaces.manifest
  @RESPATH@/components/toolkitplaces.manifest
  @RESPATH@/components/nsLivemarkService.js
  @RESPATH@/components/nsLivemarkService.js
  @RESPATH@/components/nsTaggingService.js
  @RESPATH@/components/nsTaggingService.js

+ 126 - 0
mozilla-release/patches/1381186-1-57a1.patch

@@ -0,0 +1,126 @@
+# HG changeset patch
+# User Jonathan Guillotte-Blouin <jguillotteblouin@mozilla.com>
+# Date 1500323361 25200
+# Node ID 1df19277c9c1865655fff33309d8da6f690839f4
+# Parent  c5ff7720370e9b58e41899bbaec98d484ce45834
+Bug 1381186 - open/close stub dialog on (show/abort)Payment. r=MattN
+
+MozReview-Commit-ID: K3YyFlIttjD
+
+diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
+--- a/modules/libpref/init/all.js
++++ b/modules/libpref/init/all.js
+@@ -5891,16 +5891,17 @@ pref("dom.IntersectionObserver.enabled",
+ pref("dom.moduleScripts.enabled", true);
+ 
+ // Maximum amount of time in milliseconds consecutive setTimeout()/setInterval()
+ // callback are allowed to run before yielding the event loop.
+ pref("dom.timeout.max_consecutive_callbacks_ms", 4);
+ 
+ // Use this preference to house "Payment Request API" during development
+ pref("dom.payments.request.enabled", false);
++pref("dom.payments.loglevel", "Warn");
+ 
+ #ifdef FUZZING
+ pref("fuzzing.enabled", false);
+ #endif
+ 
+ #if defined(XP_WIN)
+ pref("layers.mlgpu.enabled", true);
+ 
+diff --git a/toolkit/components/payments/content/paymentRequest.xhtml b/toolkit/components/payments/content/paymentRequest.xhtml
+--- a/toolkit/components/payments/content/paymentRequest.xhtml
++++ b/toolkit/components/payments/content/paymentRequest.xhtml
+@@ -6,12 +6,12 @@
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+   <title></title>
+   <link rel="stylesheet" href="chrome://payments/content/paymentRequest.css" />
+   <script src="chrome://payments/content/paymentRequest.js"></script>
+ </head>
+ <body>
+   <div id="controls-container">
+-    <button id="cancel"></button>
++    <button id="cancel" onclick="window.close()">Cancel payment</button>
+   </div>
+ </body>
+ </html>
+diff --git a/toolkit/components/payments/paymentUIService.js b/toolkit/components/payments/paymentUIService.js
+--- a/toolkit/components/payments/paymentUIService.js
++++ b/toolkit/components/payments/paymentUIService.js
+@@ -3,35 +3,67 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ // eslint-disable-next-line no-unused-vars
+ const DIALOG_URL = "chrome://payments/content/paymentRequest.xhtml";
+ 
+ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
++ChromeUtils.import("resource://gre/modules/Services.jsm");
+ 
+ XPCOMUtils.defineLazyServiceGetter(this,
+                                    "paymentSrv",
+                                    "@mozilla.org/dom/payments/payment-request-service;1",
+                                    "nsIPaymentRequestService");
+ 
+-function PaymentUIService() {}
++function defineLazyLogGetter(scope, logPrefix) {
++  XPCOMUtils.defineLazyGetter(scope, "log", () => {
++    let {ConsoleAPI} = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
++    return new ConsoleAPI({
++      maxLogLevelPref: "dom.payments.loglevel",
++      prefix: logPrefix,
++    });
++  });
++}
++
++function PaymentUIService() {
++  defineLazyLogGetter(this, "Payment UI Service");
++  paymentSrv.setTestingUIService(this);
++  this.log.debug("constructor");
++}
+ 
+ PaymentUIService.prototype = {
+   classID: Components.ID("{01f8bd55-9017-438b-85ec-7c15d2b35cdc}"),
+   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
++  REQUEST_ID_PREFIX: "paymentRequest-",
+ 
+   showPayment(requestId) {
++    this.log.debug("showPayment");
++    let chromeWindow = Services.wm.getMostRecentWindow("navigator:browser");
++    chromeWindow.openDialog(DIALOG_URL,
++                            `${this.REQUEST_ID_PREFIX}${requestId}`,
++                            "modal,dialog,centerscreen");
+   },
+ 
+   abortPayment(requestId) {
++    this.log.debug(`abortPayment: ${requestId}`);
+     let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"]
+                           .createInstance(Ci.nsIPaymentAbortActionResponse);
+     abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
++
++    let enu = Services.wm.getEnumerator(null);
++    let win;
++    while ((win = enu.getNext())) {
++      if (win.name == `${this.REQUEST_ID_PREFIX}${requestId}`) {
++        this.log.debug(`closing: ${win.name}`);
++        win.close();
++        break;
++      }
++    }
+     paymentSrv.respondPayment(abortResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+   },
+ 
+   completePayment(requestId) {
+     let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"]
+                              .createInstance(Ci.nsIPaymentCompleteActionResponse);
+     completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLTETE_SUCCEEDED);
+     paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+diff --git a/toolkit/components/payments/payments.manifest b/toolkit/components/payments/payments.manifest
+--- a/toolkit/components/payments/payments.manifest
++++ b/toolkit/components/payments/payments.manifest
+@@ -1,2 +1,3 @@
+ component {01f8bd55-9017-438b-85ec-7c15d2b35cdc} paymentUIService.js
+ contract @mozilla.org/dom/payments/payment-ui-service;1 {01f8bd55-9017-438b-85ec-7c15d2b35cdc}
++category profile-after-change PaymentUIService @mozilla.org/dom/payments/payment-ui-service;1

+ 152 - 0
mozilla-release/patches/1381186-2-57a1.patch

@@ -0,0 +1,152 @@
+# HG changeset patch
+# User Jonathan Guillotte-Blouin <jguillotteblouin@mozilla.com>
+# Date 1500426747 25200
+# Node ID 4bdfc48d9639679a4ccb2c5039343ac863d53c95
+# Parent  0f78614b136281bfed9e2d65c71805a527a1af32
+Bug 1381186 - add show/abort chrome test. r=MattN
+
+MozReview-Commit-ID: I6Umm24nNvA
+
+diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
+--- a/browser/installer/package-manifest.in
++++ b/browser/installer/package-manifest.in
+@@ -385,18 +385,20 @@
+ @RESPATH@/components/DownloadLegacy.js
+ @RESPATH@/components/BrowserPageThumbs.manifest
+ @RESPATH@/components/crashmonitor.manifest
+ @RESPATH@/components/nsCrashMonitor.js
+ @RESPATH@/components/toolkitsearch.manifest
+ @RESPATH@/components/nsSearchService.js
+ @RESPATH@/components/nsSearchSuggestions.js
+ @RESPATH@/components/nsSidebar.js
++#ifdef NIGHTLY_BUILD
+ @RESPATH@/components/payments.manifest
+ @RESPATH@/components/paymentUIService.js
++#endif
+ @RESPATH@/components/passwordmgr.manifest
+ @RESPATH@/components/nsLoginInfo.js
+ @RESPATH@/components/nsLoginManager.js
+ @RESPATH@/components/nsLoginManagerPrompter.js
+ @RESPATH@/components/storage-json.js
+ @RESPATH@/components/crypto-SDR.js
+ @RESPATH@/components/TooltipTextProvider.js
+ @RESPATH@/components/TooltipTextProvider.manifest
+diff --git a/toolkit/components/payments/test/browser/blank_page.html b/toolkit/components/payments/test/browser/blank_page.html
+new file mode 100644
+--- /dev/null
++++ b/toolkit/components/payments/test/browser/blank_page.html
+@@ -0,0 +1,10 @@
++<!DOCTYPE HTML>
++<html>
++  <head>
++    <meta charset="UTF-8">
++    <title>Blank page</title>
++  </head>
++  <body>
++    BLANK PAGE
++  </body>
++</html>
+diff --git a/toolkit/components/payments/test/browser/browser.ini b/toolkit/components/payments/test/browser/browser.ini
+--- a/toolkit/components/payments/test/browser/browser.ini
++++ b/toolkit/components/payments/test/browser/browser.ini
+@@ -1,4 +1,10 @@
+ [DEFAULT]
++head = head.js
++
+ support-files =
++  blank_page.html
+ 
+ [browser_request_summary.js]
++[browser_show_dialog.js]
++# Bug 1365964 - Payment Request isn't implemented for non-e10s
++skip-if = !e10s
+diff --git a/toolkit/components/payments/test/browser/browser_show_dialog.js b/toolkit/components/payments/test/browser/browser_show_dialog.js
+new file mode 100644
+--- /dev/null
++++ b/toolkit/components/payments/test/browser/browser_show_dialog.js
+@@ -0,0 +1,31 @@
++"use strict";
++
++const methodData = [{
++  supportedMethods: ["basic-card"],
++}];
++const details = {
++  total: {
++    label: "Total due",
++    amount: { currency: "USD", value: "60.00" },
++  },
++};
++
++add_task(async function test_show_abort_dialog() {
++  await BrowserTestUtils.withNewTab({
++    gBrowser,
++    url: BLANK_PAGE_URL,
++  }, async browser => {
++    // start by creating a PaymentRequest, and show it
++    await ContentTask.spawn(browser, {methodData, details}, ContentTasks.createAndShowRequest);
++
++    // get a reference to the UI dialog and the requestId
++    let win = await getDialogWindow();
++    let requestId = requestIdForWindow(win);
++    ok(requestId, "requestId should be defined");
++    ok(!win.closed, "dialog should not be closed");
++
++    // abort the payment request
++    await ContentTask.spawn(browser, null, async() => content.rq.abort());
++    ok(win.closed, "dialog should be closed");
++  });
++});
+diff --git a/toolkit/components/payments/test/browser/head.js b/toolkit/components/payments/test/browser/head.js
+new file mode 100644
+--- /dev/null
++++ b/toolkit/components/payments/test/browser/head.js
+@@ -0,0 +1,49 @@
++"use strict";
++
++/* eslint
++  "no-unused-vars": ["error", {
++    vars: "local",
++    args: "none",
++    varsIgnorePattern: "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$",
++  }],
++*/
++
++const REQUEST_ID_PREFIX = "paymentRequest-";
++const BLANK_PAGE_URL = "https://example.com/browser/toolkit/components/" +
++                       "payments/test/browser/blank_page.html";
++const PREF_PAYMENT_ENABLED = "dom.payments.request.enabled";
++
++
++async function getDialogWindow() {
++  let win;
++  await BrowserTestUtils.waitForCondition(() => {
++    win = Services.wm.getMostRecentWindow(null);
++    return win.name.startsWith(REQUEST_ID_PREFIX);
++  }, "payment dialog should be the most recent");
++
++  return win;
++}
++
++function requestIdForWindow(window) {
++  let windowName = window.name;
++
++  return windowName.startsWith(REQUEST_ID_PREFIX) ?
++    windowName.replace(REQUEST_ID_PREFIX, "") : // returns suffix, which is the requestId
++    null;
++}
++
++/**
++ * Common content tasks functions to be used with ContentTask.spawn.
++ */
++let ContentTasks = {
++  createAndShowRequest: async ({methodData, details, options}) => {
++    let rq = new content.PaymentRequest(methodData, details, options);
++    content.rq = rq; // assign it so we can retrieve it later
++    rq.show();
++  },
++};
++
++
++add_task(async function setup_head() {
++  await SpecialPowers.pushPrefEnv({set: [[PREF_PAYMENT_ENABLED, true]]});
++});

+ 115 - 0
mozilla-release/patches/1382092-1-57a1.patch

@@ -0,0 +1,115 @@
+# HG changeset patch
+# User Eden Chuang <echuang@mozilla.com>
+# Date 1502075584 -28800
+#      Mon Aug 07 11:13:04 2017 +0800
+# Node ID 84aac02c78a9a366560a8bc965adea3963c422c4
+# Parent  7e083b7b8fc53e3ab9510fc676ca3e124823fe23
+Bug 1382092 - Support default payment UI in DOM code part 1. r=MattN
+
+diff --git a/browser/base/content/test/static/browser_all_files_referenced.js b/browser/base/content/test/static/browser_all_files_referenced.js
+--- a/browser/base/content/test/static/browser_all_files_referenced.js
++++ b/browser/base/content/test/static/browser_all_files_referenced.js
+@@ -167,24 +167,16 @@ var whitelist = [
+   // Bug 1337345
+   {file: "resource://gre/modules/Manifest.jsm"},
+   // Bug 1351097
+   {file: "resource://gre/modules/accessibility/AccessFu.jsm"},
+   // Bug 1351637
+   {file: "resource://gre/modules/sdk/bootstrap.js"},
+ ];
+ 
+-// Temporary whitelisted while WebPayments in construction
+-// See Bug 1381141
+-if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_BUILD_APP == "browser") {
+-  whitelist.push(
+-    {file: "chrome://payments/content/paymentRequest.xhtml"}
+-  );
+-}
+-
+ if (!AppConstants.MOZ_PHOTON_THEME) {
+   whitelist.push(
+     // Bug 1343824
+     {file: "chrome://browser/skin/customizableui/customize-illustration-rtl@2x.png",
+      platforms: ["linux", "win"]},
+     {file: "chrome://browser/skin/customizableui/customize-illustration@2x.png",
+      platforms: ["linux", "win"]},
+     {file: "chrome://browser/skin/customizableui/info-icon-customizeTip@2x.png",
+diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
+--- a/browser/installer/package-manifest.in
++++ b/browser/installer/package-manifest.in
+@@ -385,19 +385,18 @@
+ @RESPATH@/components/DownloadLegacy.js
+ @RESPATH@/components/BrowserPageThumbs.manifest
+ @RESPATH@/components/crashmonitor.manifest
+ @RESPATH@/components/nsCrashMonitor.js
+ @RESPATH@/components/toolkitsearch.manifest
+ @RESPATH@/components/nsSearchService.js
+ @RESPATH@/components/nsSearchSuggestions.js
+ @RESPATH@/components/nsSidebar.js
+-#if defined(NIGHTLY_BUILD) && defined(MOZ_BUILD_APP_IS_BROWSER)
+ @RESPATH@/components/payments.manifest
+-#endif
++@RESPATH@/components/paymentUIService.js
+ @RESPATH@/components/passwordmgr.manifest
+ @RESPATH@/components/nsLoginInfo.js
+ @RESPATH@/components/nsLoginManager.js
+ @RESPATH@/components/nsLoginManagerPrompter.js
+ @RESPATH@/components/storage-json.js
+ @RESPATH@/components/crypto-SDR.js
+ @RESPATH@/components/TooltipTextProvider.js
+ @RESPATH@/components/TooltipTextProvider.manifest
+diff --git a/toolkit/components/payments/paymentUIService.js b/toolkit/components/payments/paymentUIService.js
+--- a/toolkit/components/payments/paymentUIService.js
++++ b/toolkit/components/payments/paymentUIService.js
+@@ -4,38 +4,41 @@
+ 
+ "use strict";
+ 
+ // eslint-disable-next-line no-unused-vars
+ const DIALOG_URL = "chrome://payments/content/paymentRequest.xhtml";
+ 
+ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ 
++XPCOMUtils.defineLazyServiceGetter(this,
++                                   "paymentSrv",
++                                   "@mozilla.org/dom/payments/payment-request-service;1",
++                                   "nsIPaymentRequestService");
++
+ function PaymentUIService() {}
+ 
+ PaymentUIService.prototype = {
+   classID: Components.ID("{01f8bd55-9017-438b-85ec-7c15d2b35cdc}"),
+   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+ 
+-  canMakePayment(requestId) {
+-    let canMakeResponse = Cc["@mozilla.org/dom/payments/payment-canmake-action-response;1"]
+-                            .createInstance(Ci.nsIPaymentCanMakeActionResponse);
+-    canMakeResponse.init(requestId, false);
+-    return canMakeResponse.QueryInterface(Ci.nsIPaymentActionResponse);
+-  },
+-
+   showPayment(requestId) {
+-    return null;
+   },
+ 
+   abortPayment(requestId) {
+     let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"]
+                           .createInstance(Ci.nsIPaymentAbortActionResponse);
+     abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
+-    return abortResponse.QueryInterface(Ci.nsIPaymentActionResponse);
++    paymentSrv.respondPayment(abortResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+   },
+ 
+   completePayment(requestId) {
+-    return null;
++    let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"]
++                             .createInstance(Ci.nsIPaymentCompleteActionResponse);
++    completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLTETE_SUCCEEDED);
++    paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
++  },
++
++  updatePayment(requestId) {
+   },
+ };
+ 
+ this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentUIService]);

+ 314 - 0
mozilla-release/patches/1382092-2-57a1.patch

@@ -0,0 +1,314 @@
+# HG changeset patch
+# User Eden Chuang <echuang@mozilla.com>
+# Date 1501838699 -28800
+#      Fri Aug 04 17:24:59 2017 +0800
+# Node ID 39c022e64d9859bd866064742539191758423dd0
+# Parent  dec69e15268f7fbe4218f10d915c5e5f85892f97
+Bug 1382092 - Support default payment UI service in DOM code part 2. r=baku
+
+diff --git a/dom/interfaces/payments/nsIPaymentUIService.idl b/dom/interfaces/payments/nsIPaymentUIService.idl
+--- a/dom/interfaces/payments/nsIPaymentUIService.idl
++++ b/dom/interfaces/payments/nsIPaymentUIService.idl
+@@ -1,17 +1,23 @@
+ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+ /* 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/. */
+ 
+ #include "nsISupports.idl"
+ #include "nsIPaymentActionResponse.idl"
+ 
+-[scriptable, uuid(ea008d0c-9e9f-411f-a6c5-a62106ba7ab9)]
++[scriptable, uuid(01f8bd55-9017-438b-85ec-7c15d2b35cdc)]
+ interface nsIPaymentUIService : nsISupports
+ {
+-  nsIPaymentActionResponse canMakePayment(in AString requestId);
+-  nsIPaymentActionResponse showPayment(in AString requestId);
+-  nsIPaymentActionResponse abortPayment(in AString requestId);
+-  nsIPaymentActionResponse completePayment(in AString requestId);
+-  nsIPaymentActionResponse updatePayment(in AString requestId);
++  void showPayment(in AString requestId);
++  void abortPayment(in AString requestId);
++  void completePayment(in AString requestId);
++  void updatePayment(in AString requestId);
+ };
++
++%{C++
++#define NS_PAYMENT_UI_SERVICE_CID \
++  { 0x01f8bd55, 0x9017, 0x438b, { 0x85, 0xec, 0x7c, 0x15, 0xd2, 0xb3, 0x5c, 0xdc } }
++#define NS_PAYMENT_UI_SERVICE_CONTRACT_ID \
++  "@mozilla.org/dom/payments/payment-ui-service;1"
++%}
+diff --git a/dom/payments/BasicCardPayment.cpp b/dom/payments/BasicCardPayment.cpp
+--- a/dom/payments/BasicCardPayment.cpp
++++ b/dom/payments/BasicCardPayment.cpp
+@@ -349,19 +349,17 @@ BasicCardService::DecodeBasicCardData(co
+   return NS_OK;
+ }
+ 
+ #ifdef PaymentBasicCardMacros
+ #undef PaymentBasicCardMacros
+ #undef EncodeBasicCardProperty
+ #undef EncodeAddressProperty
+ #undef DecodeBasicCardProperty
+-#undef DecodeBasicCardCardNumber
+ #undef DecodeAddressProperty
+-#undef DecodeAddressLine
+ #undef AMEX
+ #undef CARTEBANCAIRE
+ #undef DINERS
+ #undef DISCOVER
+ #undef JCB
+ #undef MASTERCARD
+ #undef MIR
+ #undef UNIONPAY
+diff --git a/dom/payments/PaymentRequestService.cpp b/dom/payments/PaymentRequestService.cpp
+--- a/dom/payments/PaymentRequestService.cpp
++++ b/dom/payments/PaymentRequestService.cpp
+@@ -143,94 +143,51 @@ NS_IMETHODIMP
+ PaymentRequestService::SetTestingUIService(nsIPaymentUIService* aUIService)
+ {
+   // aUIService can be nullptr
+   mTestingUIService = aUIService;
+   return NS_OK;
+ }
+ 
+ nsresult
+-PaymentRequestService::CallTestingUIAction(const nsAString& aRequestId, uint32_t aActionType)
++PaymentRequestService::LaunchUIAction(const nsAString& aRequestId, uint32_t aActionType)
+ {
+-  nsCOMPtr<nsIPaymentActionResponse> response;
++  nsCOMPtr<nsIPaymentUIService> uiService;
+   nsresult rv;
+   if (mTestingUIService) {
+-    switch (aActionType) {
+-      case nsIPaymentActionRequest::CANMAKE_ACTION: {
+-        rv = mTestingUIService->CanMakePayment(aRequestId, getter_AddRefs(response));
+-        break;
+-      }
+-      case nsIPaymentActionRequest::SHOW_ACTION: {
+-        rv = mTestingUIService->ShowPayment(aRequestId, getter_AddRefs(response));
+-        break;
+-      }
+-      case nsIPaymentActionRequest::ABORT_ACTION: {
+-        rv = mTestingUIService->AbortPayment(aRequestId, getter_AddRefs(response));
+-        break;
+-      }
+-      case nsIPaymentActionRequest::COMPLETE_ACTION: {
+-        rv = mTestingUIService->CompletePayment(aRequestId, getter_AddRefs(response));
+-        break;
+-      }
+-      case nsIPaymentActionRequest::UPDATE_ACTION: {
+-        rv = mTestingUIService->UpdatePayment(aRequestId, getter_AddRefs(response));
+-        break;
+-      }
+-      default : {
+-        return NS_ERROR_FAILURE;
+-      }
+-    }
++    uiService = mTestingUIService;
++  } else {
++    uiService = do_GetService(NS_PAYMENT_UI_SERVICE_CONTRACT_ID, &rv);
+     if (NS_WARN_IF(NS_FAILED(rv))) {
+       return rv;
+     }
+-  } else {
+-    // Since there is no UI implementation and no testing UI Service is registered,
+-    // set false response for canMakePayment(), ABORT_SUCCEEDED for abort() and
+-    // COMPLETE_SUCCEEDED for complete().
+-    switch (aActionType) {
+-      case nsIPaymentActionRequest::CANMAKE_ACTION: {
+-        nsCOMPtr<nsIPaymentCanMakeActionResponse> canMakeResponse =
+-          do_CreateInstance(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID);
+-        MOZ_ASSERT(canMakeResponse);
+-        rv = canMakeResponse->Init(aRequestId, false);
+-        NS_ENSURE_SUCCESS(rv, rv);
+-        response = do_QueryInterface(canMakeResponse);
+-        MOZ_ASSERT(response);
+-        break;
+-      }
+-      case nsIPaymentActionRequest::ABORT_ACTION: {
+-        nsCOMPtr<nsIPaymentAbortActionResponse> abortResponse =
+-          do_CreateInstance(NS_PAYMENT_ABORT_ACTION_RESPONSE_CONTRACT_ID);
+-        MOZ_ASSERT(abortResponse);
+-        rv = abortResponse->Init(aRequestId, nsIPaymentActionResponse::ABORT_SUCCEEDED);
+-        NS_ENSURE_SUCCESS(rv, rv);
+-        response = do_QueryInterface(abortResponse);
+-        MOZ_ASSERT(response);
+-        break;
+-      }
+-      case nsIPaymentActionRequest::COMPLETE_ACTION: {
+-        nsCOMPtr<nsIPaymentCompleteActionResponse> completeResponse =
+-          do_CreateInstance(NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CONTRACT_ID);
+-        MOZ_ASSERT(completeResponse);
+-        rv = completeResponse->Init(aRequestId, nsIPaymentActionResponse::COMPLETE_SUCCEEDED);
+-        NS_ENSURE_SUCCESS(rv, rv);
+-        response = do_QueryInterface(completeResponse);
+-        MOZ_ASSERT(response);
+-        break;
+-      }
+-      default : {
+-        break;
+-      }
++  }
++  switch (aActionType) {
++    case nsIPaymentActionRequest::SHOW_ACTION: {
++      rv = uiService->ShowPayment(aRequestId);
++      break;
++    }
++    case nsIPaymentActionRequest::ABORT_ACTION: {
++      rv = uiService->AbortPayment(aRequestId);
++      break;
++    }
++    case nsIPaymentActionRequest::COMPLETE_ACTION: {
++      rv = uiService->CompletePayment(aRequestId);
++      break;
++    }
++    case nsIPaymentActionRequest::UPDATE_ACTION: {
++      rv = uiService->UpdatePayment(aRequestId);
++      break;
++    }
++    default : {
++      return NS_ERROR_FAILURE;
+     }
+   }
+-  if (response) {
+-    rv = RespondPayment(response);
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return rv;
+-    }
++  if (NS_WARN_IF(NS_FAILED(rv))) {
++    return rv;
+   }
+   return NS_OK;
+ }
+ 
+ NS_IMETHODIMP
+ PaymentRequestService::RemoveActionCallback(nsIPaymentActionCallback* aCallback)
+ {
+   NS_ENSURE_ARG_POINTER(aCallback);
+@@ -305,41 +262,35 @@ PaymentRequestService::RequestPayment(ns
+       }
+       break;
+     }
+     /*
+      *  TODO: 1. Check third party payment app support by traversing all
+      *           registered third party payment apps.
+      */
+     case nsIPaymentActionRequest::CANMAKE_ACTION: {
++      nsCOMPtr<nsIPaymentCanMakeActionResponse> canMakeResponse =
++        do_CreateInstance(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID);
++      MOZ_ASSERT(canMakeResponse);
+       if (IsBasicCardPayment(requestId)) {
+-        nsCOMPtr<nsIPaymentCanMakeActionResponse> canMakeResponse =
+-          do_CreateInstance(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID);
+-        MOZ_ASSERT(canMakeResponse);
+         rv = canMakeResponse->Init(requestId, true);
+-        if (NS_WARN_IF(NS_FAILED(rv))) {
+-          return rv;
+-        }
+-        nsCOMPtr<nsIPaymentActionResponse> response = do_QueryInterface(canMakeResponse);
+-        MOZ_ASSERT(response);
+-        rv = RespondPayment(response);
+-        if (NS_WARN_IF(NS_FAILED(rv))) {
+-          return rv;
+-        }
+       } else {
+-        rv = CallTestingUIAction(requestId, type);
+-        if (NS_WARN_IF(NS_FAILED(rv))) {
+-          return NS_ERROR_FAILURE;
+-        }
++        rv = canMakeResponse->Init(requestId, false);
++      }
++      if (NS_WARN_IF(NS_FAILED(rv))) {
++        return rv;
++      }
++      nsCOMPtr<nsIPaymentActionResponse> response = do_QueryInterface(canMakeResponse);
++      MOZ_ASSERT(response);
++      rv = RespondPayment(response);
++      if (NS_WARN_IF(NS_FAILED(rv))) {
++        return rv;
+       }
+       break;
+     }
+-    /*
+-     *  TODO: Launch/inform payment UI here once the UI module is implemented.
+-     */
+     case nsIPaymentActionRequest::SHOW_ACTION: {
+       if (mShowingRequest) {
+         nsCOMPtr<nsIPaymentResponseData> responseData =
+           do_CreateInstance(NS_GENERAL_RESPONSE_DATA_CONTRACT_ID);
+         MOZ_ASSERT(responseData);
+         nsCOMPtr<nsIPaymentShowActionResponse> showResponse =
+           do_CreateInstance(NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID);
+         MOZ_ASSERT(showResponse);
+@@ -356,26 +307,26 @@ PaymentRequestService::RequestPayment(ns
+         if (NS_WARN_IF(NS_FAILED(rv))) {
+           return rv;
+         }
+       } else {
+         rv = GetPaymentRequestById(requestId, getter_AddRefs(mShowingRequest));
+         if (NS_WARN_IF(NS_FAILED(rv))) {
+           return NS_ERROR_FAILURE;
+         }
+-        rv = CallTestingUIAction(requestId, type);
++        rv = LaunchUIAction(requestId, type);
+         if (NS_WARN_IF(NS_FAILED(rv))) {
+           return NS_ERROR_FAILURE;
+         }
+       }
+       break;
+     }
+     case nsIPaymentActionRequest::ABORT_ACTION:
+     case nsIPaymentActionRequest::COMPLETE_ACTION: {
+-      rv = CallTestingUIAction(requestId, type);
++      rv = LaunchUIAction(requestId, type);
+       if (NS_WARN_IF(NS_FAILED(rv))) {
+         return NS_ERROR_FAILURE;
+       }
+       break;
+     }
+     case nsIPaymentActionRequest::UPDATE_ACTION: {
+       nsCOMPtr<nsIPaymentUpdateActionRequest> request = do_QueryInterface(aRequest);
+       MOZ_ASSERT(request);
+@@ -390,17 +341,17 @@ PaymentRequestService::RequestPayment(ns
+       rv = GetPaymentRequestById(requestId, getter_AddRefs(payment));
+       if (NS_WARN_IF(NS_FAILED(rv))) {
+         return rv;
+       }
+       rv = payment->UpdatePaymentDetails(details);
+       if (NS_WARN_IF(NS_FAILED(rv))) {
+         return rv;
+       }
+-      rv = CallTestingUIAction(requestId, type);
++      rv = LaunchUIAction(requestId, type);
+       if (NS_WARN_IF(NS_FAILED(rv))) {
+         return NS_ERROR_FAILURE;
+       }
+       break;
+     }
+     default: {
+       return NS_ERROR_FAILURE;
+     }
+diff --git a/dom/payments/PaymentRequestService.h b/dom/payments/PaymentRequestService.h
+--- a/dom/payments/PaymentRequestService.h
++++ b/dom/payments/PaymentRequestService.h
+@@ -39,17 +39,17 @@ private:
+   nsresult
+   SetActionCallback(const nsAString& aRequestId,
+                     nsIPaymentActionCallback* aCallback);
+   nsresult
+   RemoveActionCallback(const nsAString& aRequestId);
+ 
+   // this method is only used for testing
+   nsresult
+-  CallTestingUIAction(const nsAString& aRequestId, uint32_t aActionType);
++  LaunchUIAction(const nsAString& aRequestId, uint32_t aActionType);
+ 
+   bool
+   IsBasicCardPayment(const nsAString& aRequestId);
+ 
+   FallibleTArray<nsCOMPtr<nsIPaymentRequest>> mRequestQueue;
+ 
+   nsInterfaceHashtable<nsStringHashKey, nsIPaymentActionCallback> mCallbackHashtable;
+ 

+ 449 - 0
mozilla-release/patches/1382092-3-57a1.patch

@@ -0,0 +1,449 @@
+# HG changeset patch
+# User Eden Chuang <echuang@mozilla.com>
+# Date 1500887907 -28800
+#      Mon Jul 24 17:18:27 2017 +0800
+# Node ID 1eaa794d25333caf9dfcb2240c764a49f4afa9cd
+# Parent  450d98b9da597ef42bd9ef06dc0e12676adbd361
+Bug 1382092 - Mochitest for supporting default payment UI in DOM code. r=baku
+
+diff --git a/dom/payments/test/BasiccardChromeScript.js b/dom/payments/test/BasiccardChromeScript.js
+--- a/dom/payments/test/BasiccardChromeScript.js
++++ b/dom/payments/test/BasiccardChromeScript.js
+@@ -34,30 +34,27 @@ const basiccardResponseData = Cc["@mozil
+ 
+ const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
+                         createInstance(Ci.nsIPaymentShowActionResponse);
+ 
+ function abortPaymentResponse(requestId) {
+   let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"].
+                          createInstance(Ci.nsIPaymentAbortActionResponse);
+   abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
+-  return abortResponse.QueryInterface(Ci.nsIPaymentActionResponse);
++  paymentSrv.respondPayment(abortResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+ }
+ 
+ function completePaymentResponse(requestId) {
+   let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
+                             createInstance(Ci.nsIPaymentCompleteActionResponse);
+   completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
+-  return completeResponse;
++  paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+ }
+ 
+ const detailedResponseUI = {
+-  canMakePayment: function(requestId) {
+-    return null;
+-  },
+   showPayment: function(requestId) {
+     try {
+       basiccardResponseData.initData("Bill A. Pacheco",  // cardholderName
+                                      "4916855166538720", // cardNumber
+                                      "01",               // expiryMonth
+                                      "2024",             // expiryYear
+                                      "180",              // cardSecurityCode
+                                      billingAddress);   // billingAddress
+@@ -66,30 +63,26 @@ const detailedResponseUI = {
+     }
+     showResponse.init(requestId,
+                       Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                       "basic-card",         // payment method
+                       basiccardResponseData,// payment method data
+                       "Bill A. Pacheco",    // payer name
+                       "",                   // payer email
+                       "");                  // payer phone
+-    return showResponse;
++    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+   },
+   abortPayment: abortPaymentResponse,
+   completePayment: completePaymentResponse,
+   updatePayment: function(requestId) {
+-    return null;
+   },
+   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+ };
+ 
+ const simpleResponseUI = {
+-  canMakePayment: function(requestId) {
+-    return null;
+-  },
+   showPayment: function(requestId) {
+     try {
+       basiccardResponseData.initData("",                 // cardholderName
+                                      "4916855166538720", // cardNumber
+                                      "",                 // expiryMonth
+                                      "",                 // expiryYear
+                                      "",                 // cardSecurityCode
+                                      null);              // billingAddress
+@@ -98,22 +91,21 @@ const simpleResponseUI = {
+     }
+     showResponse.init(requestId,
+                       Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                       "basic-card",         // payment method
+                       basiccardResponseData,// payment method data
+                       "Bill A. Pacheco",    // payer name
+                       "",                   // payer email
+                       "");                  // payer phone
+-    return showResponse;
++    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+   },
+   abortPayment: abortPaymentResponse,
+   completePayment: completePaymentResponse,
+   updatePayment: function(requestId) {
+-    return null;
+   },
+   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+ };
+ 
+ addMessageListener("set-detailed-ui-service", function() {
+   paymentSrv.setTestingUIService(detailedResponseUI.QueryInterface(Ci.nsIPaymentUIService));
+ });
+ 
+diff --git a/dom/payments/test/CanMakePaymentChromeScript.js b/dom/payments/test/CanMakePaymentChromeScript.js
+deleted file mode 100644
+--- a/dom/payments/test/CanMakePaymentChromeScript.js
++++ /dev/null
+@@ -1,37 +0,0 @@
+-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+-/* Any copyright is dedicated to the Public Domain.
+-   http://creativecommons.org/publicdomain/zero/1.0/ */
+-"use strict";
+-
+-const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+-
+-const DummyUIService = {
+-  canMakePayment: function(requestId) {
+-    let canMakeResponse = Cc["@mozilla.org/dom/payments/payment-canmake-action-response;1"].
+-                          createInstance(Ci.nsIPaymentCanMakeActionResponse);
+-    canMakeResponse.init(requestId, true);
+-    return canMakeResponse.QueryInterface(Ci.nsIPaymentActionResponse);
+-  },
+-  showPayment: function(requestId) {
+-    return null;
+-  },
+-  abortPayment: function(requestId) {
+-    let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"].
+-                        createInstance(Ci.nsIPaymentAbortActionResponse);
+-    abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
+-    return abortResponse.QueryInterface(Ci.nsIPaymentActionResponse);
+-  },
+-  completePayment: function(requestId) {
+-    return null;
+-  },
+-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+-};
+-
+-const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"].getService(Ci.nsIPaymentRequestService);
+-paymentSrv.setTestingUIService(DummyUIService.QueryInterface(Ci.nsIPaymentUIService));
+-
+-addMessageListener('teardown', function() {
+-  paymentSrv.cleanup();
+-  paymentSrv.setTestingUIService(null);
+-  sendAsyncMessage('teardown-complete');
+-});
+diff --git a/dom/payments/test/GeneralChromeScript.js b/dom/payments/test/GeneralChromeScript.js
+new file mode 100644
+--- /dev/null
++++ b/dom/payments/test/GeneralChromeScript.js
+@@ -0,0 +1,14 @@
++/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
++/* Any copyright is dedicated to the Public Domain.
++   http://creativecommons.org/publicdomain/zero/1.0/ */
++"use strict";
++
++const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
++
++const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"].getService(Ci.nsIPaymentRequestService);
++
++addMessageListener("teardown", function() {
++  paymentSrv.cleanup();
++  paymentSrv.setTestingUIService(null);
++  sendAsyncMessage('teardown-complete');
++});
+diff --git a/dom/payments/test/ShowPaymentChromeScript.js b/dom/payments/test/ShowPaymentChromeScript.js
+--- a/dom/payments/test/ShowPaymentChromeScript.js
++++ b/dom/payments/test/ShowPaymentChromeScript.js
+@@ -26,31 +26,26 @@ shippingAddress.init("USA",             
+                      "123456",           // sorting code
+                      "en",               // language code
+                      "",                 // organization
+                      "Bill A. Pacheco",  // recipient
+                      "+1-434-441-3879"); // phone
+ 
+ const NormalUIService = {
+   shippingOptionChanged: false,
+-  canMakePayment: function(requestId) {
+-    return null;
+-  },
+   showPayment: function(requestId) {
+     paymentSrv.changeShippingAddress(requestId, shippingAddress);
+-    return null;
+   },
+   abortPayment: function(requestId) {
+-    return null;
+   },
+   completePayment: function(requestId) {
+     let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
+                            createInstance(Ci.nsIPaymentCompleteActionResponse);
+     completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
+-    return completeResponse;
++    paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+   },
+   updatePayment: function(requestId) {
+     let showResponse = null;
+     let payRequest = paymentSrv.getPaymentRequestById(requestId);
+     if (payRequest.paymentDetails.error != "") {
+       emitTestFail("updatedDetails should not have errors(" + payRequest.paymentDetails.error + ").");
+     }
+     if (!this.shippingOptionChanged) {
+@@ -80,26 +75,23 @@ const NormalUIService = {
+                      createInstance(Ci.nsIPaymentShowActionResponse);
+       showResponse.init(requestId,
+                         Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                         "testing-payment-method",   // payment method
+                         showResponseData,           // payment method data
+                         "Bill A. Pacheco",          // payer name
+                         "",                         // payer email
+                         "");                        // payer phone
++      paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+     }
+-    return showResponse;
+   },
+   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+ };
+ 
+ const RejectUIService = {
+-  canMakePayment: function(requestId) {
+-    return null;
+-  },
+   showPayment: function(requestId) {
+     const responseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                             createInstance(Ci.nsIGeneralResponseData);
+ 
+     try {
+       responseData.initData({});
+     } catch (e) {
+       emitTestFail("Fail to initialize response data with empty object.");
+@@ -108,44 +100,34 @@ const RejectUIService = {
+                             createInstance(Ci.nsIPaymentShowActionResponse);
+     showResponse.init(requestId,
+                       Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+                       "",                 // payment method
+                       responseData,       // payment method data
+                       "",                 // payer name
+                       "",                 // payer email
+                       "");                // payer phone
+-
+-    return showResponse;
++    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+   },
+   abortPayment: function(requestId) {
+-    return null;
+   },
+   completePayment: function(requestId) {
+-    return null;
+   },
+   updatePayment: function(requestId) {
+-    return null;
+   },
+   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+ };
+ 
+ const ErrorUIService = {
+-  canMakePayment: function(requestId) {
+-    return null;
+-  },
+   showPayment: function(requestId) {
+     paymentSrv.changeShippingOption(requestId, "");
+-    return null;
+   },
+   abortPayment: function(requestId) {
+-    return null;
+   },
+   completePayment: function(requestId) {
+-    return null;
+   },
+   updatePayment: function(requestId) {
+     let payRequest = paymentSrv.getPaymentRequestById(requestId);
+     if (!payRequest) {
+       emitTestFail("Fail to get existing payment request.");
+     }
+     if (payRequest.paymentDetails.error != "Update with Error") {
+       emitTestFail("details.error should be 'Update with Error', but got " + payRequest.paymentDetails.error + ".");
+@@ -161,18 +143,17 @@ const ErrorUIService = {
+                             createInstance(Ci.nsIPaymentShowActionResponse);
+     showResponse.init(requestId,
+                       Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+                       "",                 // payment method
+                       responseData,       // payment method data
+                       "",                 // payer name
+                       "",                 // payer email
+                       "");                // payer phone
+-
+-    return showResponse;
++    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+   },
+   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+ 
+ };
+ 
+ function testNullDetailsResponseHandler() {
+   const showResponseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                               createInstance(Ci.nsIGeneralResponseData);
+diff --git a/dom/payments/test/mochitest.ini b/dom/payments/test/mochitest.ini
+--- a/dom/payments/test/mochitest.ini
++++ b/dom/payments/test/mochitest.ini
+@@ -1,19 +1,19 @@
+ [DEFAULT]
+ # skip-if !e10s will be removed once non-e10s is supported
+ skip-if = !e10s
+ scheme = https
+ support-files =
+   simple_payment_request.html
+   echo_payment_request.html
+   BasiccardChromeScript.js
+-  CanMakePaymentChromeScript.js
+   ConstructorChromeScript.js
+   CurrencyAmountValidationChromeScript.js
++  GeneralChromeScript.js
+   PMIValidationChromeScript.js
+   ShowPaymentChromeScript.js
+ 
+ [test_abortPayment.html]
+ [test_basiccard.html]
+ [test_block_none10s.html]
+ skip-if = e10s # Bug 1408250: Don't expose PaymentRequest Constructor in non-e10s
+ [test_canMakePayment.html]
+diff --git a/dom/payments/test/test_abortPayment.html b/dom/payments/test/test_abortPayment.html
+--- a/dom/payments/test/test_abortPayment.html
++++ b/dom/payments/test/test_abortPayment.html
+@@ -8,16 +8,19 @@ https://bugzilla.mozilla.org/show_bug.cg
+   <title>Test for Bug 1345367</title>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <script type="application/javascript">
+ 
+   "use strict";
+   SimpleTest.waitForExplicitFinish();
+ 
++  var gUrl = SimpleTest.getTestFileURL('GeneralChromeScript.js');
++  var gScript = SpecialPowers.loadChromeScript(gUrl);
++
+   const defaultMethods = [{
+     supportedMethods: "basic-card",
+   }];
+   const defaultDetails = {
+     total: {
+       label: "Total",
+       amount: {
+         currency: "USD",
+@@ -49,20 +52,29 @@ https://bugzilla.mozilla.org/show_bug.cg
+         resolve();
+       }).catch( (err) => {
+         ok(false, "Expected no error, but got '" + err.name + "'.");
+         resolve();
+       });
+     });
+   }
+ 
++  function teardown() {
++    gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
++      gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
++      gScript.destroy();
++      SimpleTest.finish();
++    });
++    gScript.sendAsyncMessage("teardown");
++  }
++
+   function runTests() {
+     testBeforeShow()
+     .then(testAfterShow)
+-    .then(SimpleTest.finish)
++    .then(teardown)
+     .catch( e => {
+       ok(false, "Unexpected error: " + e.name);
+       SimpleTest.finish();
+     });
+   }
+ 
+   window.addEventListener('load', function() {
+     SpecialPowers.pushPrefEnv({
+diff --git a/dom/payments/test/test_canMakePayment.html b/dom/payments/test/test_canMakePayment.html
+--- a/dom/payments/test/test_canMakePayment.html
++++ b/dom/payments/test/test_canMakePayment.html
+@@ -8,37 +8,54 @@ https://bugzilla.mozilla.org/show_bug.cg
+   <title>Test for PaymentRequest API canMakePayment()</title>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <script type="application/javascript">
+ 
+   "use strict";
+   SimpleTest.waitForExplicitFinish();
+ 
+-  var gUrl = SimpleTest.getTestFileURL('CanMakePaymentChromeScript.js');
++  var gUrl = SimpleTest.getTestFileURL('GeneralChromeScript.js');
+   var gScript = SpecialPowers.loadChromeScript(gUrl);
+ 
+   const defaultMethods = [{
+     supportedMethods: "basic-card",
+   }];
+   const defaultDetails = {
+     total: {
+       label: "Total",
+       amount: {
+         currency: "USD",
+         value: "1.00"
+       }
+     },
+   };
+ 
++  const nonsupportedMethods = [{
++    supportedMethods: "testing-payment-method",
++  }];
++
++  function testDefaultAction() {
++    return new Promise((resolve, reject) => {
++      const payRequest = new PaymentRequest(nonsupportedMethods, defaultDetails);
++      payRequest.canMakePayment().then((result) => {
++        ok(!result, "Should be resolved with false, but got " + result + ".");
++        resolve();
++      }).catch((err) => {
++        ok(false, "Expected no error, but got '" + err.name +"'.");
++        resolve();
++      });
++    });
++  }
++
+   function testSimple() {
+     return new Promise((resolve, reject) => {
+       const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+       payRequest.canMakePayment().then((result) => {
+-        ok(result, "Should be resolved with true, but got false.");
++        ok(result, "Should be resolved with true, but got " + result + ".");
+         resolve();
+       }).catch((err) => {
+         ok(false, "Expected no error, but got '" + err.name +"'.");
+         resolve();
+       });
+     });
+   }
+ 
+@@ -113,17 +130,18 @@ https://bugzilla.mozilla.org/show_bug.cg
+       gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+       gScript.destroy();
+       SimpleTest.finish();
+     });
+     gScript.sendAsyncMessage("teardown");
+   }
+ 
+   function runTests() {
+-    testSimple()
++    testDefaultAction()
++    .then(testSimple)
+     .then(testAfterShow)
+     .then(testAfterAbort)
+     .then(testNotAllowed)
+     .then(teardown)
+     .catch(e => {
+       ok(false, "Unexpected error: " + e.name);
+       SimpleTest.finish();
+     });

+ 23 - 0
mozilla-release/patches/1382092-4-57a1.patch

@@ -0,0 +1,23 @@
+# HG changeset patch
+# User Eden Chuang <echuang@mozilla.com>
+# Date 1502558231 -28800
+#      Sun Aug 13 01:17:11 2017 +0800
+# Node ID 113580d267ea3cbd23001a8b19f03a6730684ff4
+# Parent  e8799d84a76ccc9dc1f17dcafe7527386a87db18
+Bug 1382092 - Support default payment UI service in DOM code part 3. r=MattN.
+
+diff --git a/toolkit/components/moz.build b/toolkit/components/moz.build
+--- a/toolkit/components/moz.build
++++ b/toolkit/components/moz.build
+@@ -109,10 +109,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind
+     DIRS += ['gfx']
+ 
+ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
+     EXTRA_COMPONENTS += [
+         'nsDefaultCLH.js',
+         'nsDefaultCLH.manifest',
+     ]
+ 
+-if CONFIG['NIGHTLY_BUILD'] and CONFIG['MOZ_BUILD_APP'] == 'browser':
++if CONFIG['MOZ_BUILD_APP'] == 'browser':
+     DIRS += ['payments']

+ 635 - 0
mozilla-release/patches/1382388-58a1.patch

@@ -0,0 +1,635 @@
+# HG changeset patch
+# User Jonathan Guillotte-Blouin <jguillotteblouin@mozilla.com>
+# Date 1506026981 25200
+# Node ID bd2f45d07990febaa1020e3f413c932943f24de9
+# Parent  c35ddd472c70d5058c67efb14c12d99cbc4f46f9
+Bug 1382388 - Make the Payments Dialog unprivileged & add "abort" support. r=MattN
+
+MozReview-Commit-ID: IXexL4ju2Fj
+
+diff --git a/toolkit/components/payments/content/paymentDialog.js b/toolkit/components/payments/content/paymentDialog.js
+new file mode 100644
+--- /dev/null
++++ b/toolkit/components/payments/content/paymentDialog.js
+@@ -0,0 +1,89 @@
++/* 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";
++
++const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
++const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"]
++                     .getService(Ci.nsIPaymentRequestService);
++
++let PaymentDialog = {
++  componentsLoaded: new Map(),
++  frame: null,
++  mm: null,
++
++  init(frame) {
++    this.frame = frame;
++    this.mm = frame.frameLoader.messageManager;
++    this.mm.addMessageListener("paymentContentToChrome", this);
++    this.mm.loadFrameScript("chrome://payments/content/paymentDialogFrameScript.js", true);
++  },
++
++  createShowResponse({requestId, acceptStatus, methodName = "", data = null,
++                      payerName = "", payerEmail = "", payerPhone = ""}) {
++    let showResponse = this.createComponentInstance(Ci.nsIPaymentShowActionResponse);
++    let methodData = this.createComponentInstance(Ci.nsIGeneralResponseData);
++
++    showResponse.init(requestId,
++                      acceptStatus,
++                      methodName,
++                      methodData,
++                      payerName,
++                      payerEmail,
++                      payerPhone);
++    return showResponse;
++  },
++
++  createComponentInstance(componentInterface) {
++    let componentName;
++    switch (componentInterface) {
++      case Ci.nsIPaymentShowActionResponse: {
++        componentName = "@mozilla.org/dom/payments/payment-show-action-response;1";
++        break;
++      }
++      case Ci.nsIGeneralResponseData: {
++        componentName = "@mozilla.org/dom/payments/general-response-data;1";
++        break;
++      }
++    }
++    let component = this.componentsLoaded.get(componentName);
++
++    if (!component) {
++      component = Cc[componentName];
++      this.componentsLoaded.set(componentName, component);
++    }
++
++    return component.createInstance(componentInterface);
++  },
++
++  onPaymentCancel(requestId) {
++    const showResponse = this.createShowResponse({
++      requestId,
++      acceptStatus: Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
++    });
++    paymentSrv.respondPayment(showResponse);
++    window.close();
++  },
++
++  receiveMessage({data}) {
++    let {messageType, requestId} = data;
++
++    switch (messageType) {
++      case "initializeRequest": {
++        this.mm.sendAsyncMessage("paymentChromeToContent", {
++          messageType: "showPaymentRequest",
++          data: window.arguments[0],
++        });
++        break;
++      }
++      case "paymentCancel": {
++        this.onPaymentCancel(requestId);
++        break;
++      }
++    }
++  },
++};
++
++let frame = document.getElementById("paymentRequestFrame");
++PaymentDialog.init(frame);
+diff --git a/toolkit/components/payments/content/paymentDialog.xhtml b/toolkit/components/payments/content/paymentDialog.xhtml
+new file mode 100644
+--- /dev/null
++++ b/toolkit/components/payments/content/paymentDialog.xhtml
+@@ -0,0 +1,19 @@
++<?xml version="1.0" encoding="UTF-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/. -->
++<!DOCTYPE html>
++<html xmlns="http://www.w3.org/1999/xhtml">
++<head>
++  <title></title>
++</head>
++<body>
++  <iframe type="content"
++          id="paymentRequestFrame"
++          mozbrowser="true"
++          remote="true"
++          name="paymentRequestFrame"
++          src="resource://payments/paymentRequest.xhtml"></iframe>
++  <script src="chrome://payments/content/paymentDialog.js"></script>
++</body>
++</html>
+diff --git a/toolkit/components/payments/content/paymentDialogFrameScript.js b/toolkit/components/payments/content/paymentDialogFrameScript.js
+new file mode 100644
+--- /dev/null
++++ b/toolkit/components/payments/content/paymentDialogFrameScript.js
+@@ -0,0 +1,69 @@
++/* 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";
++
++/* eslint-env mozilla/frame-script */
++
++const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
++Cu.import("resource://gre/modules/XPCOMUtils.jsm");
++
++let PaymentFrameScript = {
++  init() {
++    this.defineLazyLogGetter(this, "frameScript");
++    addEventListener("paymentContentToChrome", this, false, true);
++
++    addMessageListener("paymentChromeToContent", this);
++  },
++
++  handleEvent(event) {
++    switch (event.type) {
++      case "paymentContentToChrome": {
++        this.sendToChrome(event);
++        break;
++      }
++      default: {
++        throw new Error("Unexpected event type");
++      }
++    }
++  },
++
++  receiveMessage({data: {messageType, data}}) {
++    this.sendToContent(messageType, data);
++  },
++
++  sendToChrome({detail}) {
++    let {messageType, requestId} = detail;
++    this.log.debug(`received message from content: ${messageType} ... ${requestId}`);
++    this.sendMessageToChrome(messageType, {
++      requestId,
++    });
++  },
++
++  defineLazyLogGetter(scope, logPrefix) {
++    XPCOMUtils.defineLazyGetter(scope, "log", () => {
++      let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
++      return new ConsoleAPI({
++        maxLogLevelPref: "dom.payments.loglevel",
++        prefix: logPrefix,
++      });
++    });
++  },
++
++  sendToContent(messageType, detail = {}) {
++    this.log.debug(`sendToContent (${messageType})`);
++    let response = Object.assign({messageType}, detail);
++    let event = new content.document.defaultView.CustomEvent("paymentChromeToContent", {
++      bubbles: true,
++      detail: Cu.cloneInto(response, content.document.defaultView),
++    });
++    content.document.dispatchEvent(event);
++  },
++
++  sendMessageToChrome(messageType, detail = {}) {
++    sendAsyncMessage("paymentContentToChrome", Object.assign(detail, {messageType}));
++  },
++};
++
++PaymentFrameScript.init();
+diff --git a/toolkit/components/payments/jar.mn b/toolkit/components/payments/jar.mn
+--- a/toolkit/components/payments/jar.mn
++++ b/toolkit/components/payments/jar.mn
+@@ -1,9 +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/.
+ 
+ toolkit.jar:
+ %   content payments %content/payments/
+-    content/payments/paymentRequest.css                (content/paymentRequest.css)
+-    content/payments/paymentRequest.js                 (content/paymentRequest.js)
+-    content/payments/paymentRequest.xhtml              (content/paymentRequest.xhtml)
++    content/payments/paymentDialog.js                 (content/paymentDialog.js)
++    content/payments/paymentDialogFrameScript.js      (content/paymentDialogFrameScript.js)
++    content/payments/paymentDialog.xhtml              (content/paymentDialog.xhtml)
++%   resource payments %res/payments/
++    res/payments (res/paymentRequest.*)
+diff --git a/toolkit/components/payments/paymentUIService.js b/toolkit/components/payments/paymentUIService.js
+--- a/toolkit/components/payments/paymentUIService.js
++++ b/toolkit/components/payments/paymentUIService.js
+@@ -1,16 +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/. */
+ 
+ "use strict";
+ 
+ // eslint-disable-next-line no-unused-vars
+-const DIALOG_URL = "chrome://payments/content/paymentRequest.xhtml";
++const DIALOG_URL = "chrome://payments/content/paymentDialog.xhtml";
+ 
+ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ ChromeUtils.import("resource://gre/modules/Services.jsm");
+ 
+ XPCOMUtils.defineLazyServiceGetter(this,
+                                    "paymentSrv",
+                                    "@mozilla.org/dom/payments/payment-request-service;1",
+                                    "nsIPaymentRequestService");
+@@ -21,56 +21,74 @@ function defineLazyLogGetter(scope, logP
+     return new ConsoleAPI({
+       maxLogLevelPref: "dom.payments.loglevel",
+       prefix: logPrefix,
+     });
+   });
+ }
+ 
+ function PaymentUIService() {
++  this.wrappedJSObject = this;
+   defineLazyLogGetter(this, "Payment UI Service");
+-  paymentSrv.setTestingUIService(this);
+   this.log.debug("constructor");
+ }
+ 
+ PaymentUIService.prototype = {
+   classID: Components.ID("{01f8bd55-9017-438b-85ec-7c15d2b35cdc}"),
+   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+   REQUEST_ID_PREFIX: "paymentRequest-",
+ 
+   showPayment(requestId) {
+     this.log.debug("showPayment");
+     let chromeWindow = Services.wm.getMostRecentWindow("navigator:browser");
+     chromeWindow.openDialog(DIALOG_URL,
+                             `${this.REQUEST_ID_PREFIX}${requestId}`,
+-                            "modal,dialog,centerscreen");
++                            "modal,dialog,centerscreen",
++                            {requestId});
+   },
+ 
+   abortPayment(requestId) {
+     this.log.debug(`abortPayment: ${requestId}`);
+     let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"]
+                           .createInstance(Ci.nsIPaymentAbortActionResponse);
+-    abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
+ 
+     let enu = Services.wm.getEnumerator(null);
+     let win;
+     while ((win = enu.getNext())) {
+       if (win.name == `${this.REQUEST_ID_PREFIX}${requestId}`) {
+         this.log.debug(`closing: ${win.name}`);
+         win.close();
+         break;
+       }
+     }
+-    paymentSrv.respondPayment(abortResponse.QueryInterface(Ci.nsIPaymentActionResponse));
++
++    // if `win` is falsy, then we haven't found the dialog, so the abort fails
++    // otherwise, the abort is successful
++    let response = win ?
++      Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED :
++      Ci.nsIPaymentActionResponse.ABORT_FAILED;
++
++    abortResponse.init(requestId, response);
++    paymentSrv.respondPayment(abortResponse);
+   },
+ 
+   completePayment(requestId) {
+     let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"]
+                              .createInstance(Ci.nsIPaymentCompleteActionResponse);
+     completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLTETE_SUCCEEDED);
+     paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+   },
+ 
+   updatePayment(requestId) {
+   },
++
++  // other helper methods
++
++  requestIdForWindow(window) {
++    let windowName = window.name;
++
++    return windowName.startsWith(this.REQUEST_ID_PREFIX) ?
++      windowName.replace(this.REQUEST_ID_PREFIX, "") : // returns suffix, which is the requestId
++      null;
++  },
+ };
+ 
+ this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentUIService]);
+diff --git a/toolkit/components/payments/payments.manifest b/toolkit/components/payments/payments.manifest
+--- a/toolkit/components/payments/payments.manifest
++++ b/toolkit/components/payments/payments.manifest
+@@ -1,3 +1,2 @@
+ component {01f8bd55-9017-438b-85ec-7c15d2b35cdc} paymentUIService.js
+ contract @mozilla.org/dom/payments/payment-ui-service;1 {01f8bd55-9017-438b-85ec-7c15d2b35cdc}
+-category profile-after-change PaymentUIService @mozilla.org/dom/payments/payment-ui-service;1
+diff --git a/toolkit/components/payments/content/paymentRequest.css b/toolkit/components/payments/res/paymentRequest.css
+rename from toolkit/components/payments/content/paymentRequest.css
+rename to toolkit/components/payments/res/paymentRequest.css
+diff --git a/toolkit/components/payments/content/paymentRequest.js b/toolkit/components/payments/res/paymentRequest.js
+rename from toolkit/components/payments/content/paymentRequest.js
+rename to toolkit/components/payments/res/paymentRequest.js
+--- a/toolkit/components/payments/content/paymentRequest.js
++++ b/toolkit/components/payments/res/paymentRequest.js
+@@ -1,6 +1,90 @@
+ /* 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";
+ 
++let PaymentRequest = {
++  requestId: null,
++
++  init() {
++    // listen to content
++    window.addEventListener("paymentChromeToContent", this);
++
++    // listen to user events
++    window.addEventListener("DOMContentLoaded", this, {once: true});
++
++    // This scope is now ready to listen to the initialization data
++    this.sendMessageToChrome("initializeRequest");
++  },
++
++  handleEvent(event) {
++    switch (event.type) {
++      case "DOMContentLoaded": {
++        this.onPaymentRequestLoad();
++        break;
++      }
++      case "click": {
++        switch (event.target.id) {
++          case "cancel": {
++            this.onCancel();
++            break;
++          }
++        }
++        break;
++      }
++      case "unload": {
++        this.onPaymentRequestUnload();
++        break;
++      }
++      case "paymentChromeToContent": {
++        this.onChromeToContent(event);
++        break;
++      }
++      default: {
++        throw new Error("Unexpected event type");
++      }
++    }
++  },
++
++  sendMessageToChrome(messageType, detail = {}) {
++    let event = new CustomEvent("paymentContentToChrome", {
++      bubbles: true,
++      detail: Object.assign({
++        requestId: this.requestId,
++        messageType,
++      }, detail),
++    });
++    document.dispatchEvent(event);
++  },
++
++  onChromeToContent({detail}) {
++    let {messageType, requestId} = detail;
++
++    switch (messageType) {
++      case "showPaymentRequest": {
++        this.requestId = requestId;
++        break;
++      }
++    }
++  },
++
++  onPaymentRequestLoad(requestId) {
++    let cancelBtn = document.getElementById("cancel");
++    cancelBtn.addEventListener("click", this, {once: true});
++
++    window.addEventListener("unload", this, {once: true});
++    this.sendMessageToChrome("paymentDialogReady");
++  },
++
++  onCancel() {
++    this.sendMessageToChrome("paymentCancel");
++  },
++
++  onPaymentRequestUnload() {
++    // remove listeners that may be used multiple times here
++    window.removeEventListener("paymentChromeToContent", this);
++  },
++};
++
++PaymentRequest.init();
+diff --git a/toolkit/components/payments/content/paymentRequest.xhtml b/toolkit/components/payments/res/paymentRequest.xhtml
+rename from toolkit/components/payments/content/paymentRequest.xhtml
+rename to toolkit/components/payments/res/paymentRequest.xhtml
+--- a/toolkit/components/payments/content/paymentRequest.xhtml
++++ b/toolkit/components/payments/res/paymentRequest.xhtml
+@@ -1,17 +1,17 @@
+ <?xml version="1.0" encoding="UTF-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/. -->
+ <!DOCTYPE html>
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+   <title></title>
+-  <link rel="stylesheet" href="chrome://payments/content/paymentRequest.css" />
+-  <script src="chrome://payments/content/paymentRequest.js"></script>
++  <link rel="stylesheet" href="resource://payments/paymentRequest.css" />
++  <script src="resource://payments/paymentRequest.js"></script>
+ </head>
+ <body>
+   <div id="controls-container">
+-    <button id="cancel" onclick="window.close()">Cancel payment</button>
++    <button id="cancel">Cancel payment</button>
+   </div>
+ </body>
+ </html>
+diff --git a/toolkit/components/payments/test/browser/browser.ini b/toolkit/components/payments/test/browser/browser.ini
+--- a/toolkit/components/payments/test/browser/browser.ini
++++ b/toolkit/components/payments/test/browser/browser.ini
+@@ -1,10 +1,9 @@
+ [DEFAULT]
+ head = head.js
+-
+ support-files =
+   blank_page.html
+ 
+ [browser_request_summary.js]
+ [browser_show_dialog.js]
+ # Bug 1365964 - Payment Request isn't implemented for non-e10s
+ skip-if = !e10s
+diff --git a/toolkit/components/payments/test/browser/browser_request_summary.js b/toolkit/components/payments/test/browser/browser_request_summary.js
+--- a/toolkit/components/payments/test/browser/browser_request_summary.js
++++ b/toolkit/components/payments/test/browser/browser_request_summary.js
+@@ -1,11 +1,11 @@
+ "use strict";
+ 
+ add_task(async function test_summary() {
+   await BrowserTestUtils.withNewTab({
+     gBrowser,
+-    url: "chrome://payments/content/paymentRequest.xhtml",
++    url: "resource://payments/paymentRequest.xhtml",
+   }, async browser => {
+     // eslint-disable-next-line mozilla/no-cpows-in-tests
+     ok(browser.contentDocument.getElementById("cancel"), "Cancel button exists");
+   });
+ });
+diff --git a/toolkit/components/payments/test/browser/browser_show_dialog.js b/toolkit/components/payments/test/browser/browser_show_dialog.js
+--- a/toolkit/components/payments/test/browser/browser_show_dialog.js
++++ b/toolkit/components/payments/test/browser/browser_show_dialog.js
+@@ -14,18 +14,45 @@ add_task(async function test_show_abort_
+   await BrowserTestUtils.withNewTab({
+     gBrowser,
+     url: BLANK_PAGE_URL,
+   }, async browser => {
+     // start by creating a PaymentRequest, and show it
+     await ContentTask.spawn(browser, {methodData, details}, ContentTasks.createAndShowRequest);
+ 
+     // get a reference to the UI dialog and the requestId
+-    let win = await getDialogWindow();
+-    let requestId = requestIdForWindow(win);
++    let win = await getPaymentWidget();
++    let requestId = paymentUISrv.requestIdForWindow(win);
+     ok(requestId, "requestId should be defined");
+-    ok(!win.closed, "dialog should not be closed");
++    is(win.closed, false, "dialog should not be closed");
+ 
+     // abort the payment request
+-    await ContentTask.spawn(browser, null, async() => content.rq.abort());
+-    ok(win.closed, "dialog should be closed");
++    ContentTask.spawn(browser, null, async() => content.rq.abort());
++    await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
+   });
+ });
++
++add_task(async function test_show_manualAbort_dialog() {
++  await BrowserTestUtils.withNewTab({
++    gBrowser,
++    url: BLANK_PAGE_URL,
++  }, async browser => {
++    let dialogReadyPromise = waitForWidgetReady();
++    // start by creating a PaymentRequest, and show it
++    await ContentTask.spawn(browser, {methodData, details}, ContentTasks.createAndShowRequest);
++
++    // get a reference to the UI dialog and the requestId
++    let [win] = await Promise.all([getPaymentWidget(), dialogReadyPromise]);
++    ok(win, "Got payment widget");
++    let requestId = paymentUISrv.requestIdForWindow(win);
++    ok(requestId, "requestId should be defined");
++    is(win.closed, false, "dialog should not be closed");
++
++    // abort the payment request manually
++    let frame = await getPaymentFrame(win);
++    ok(frame, "Got payment frame");
++    await dialogReadyPromise;
++    info("dialog ready");
++    spawnPaymentDialogTask(frame, ContentTasks.manuallyClickCancel);
++
++    await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
++  });
++});
+diff --git a/toolkit/components/payments/test/browser/head.js b/toolkit/components/payments/test/browser/head.js
+--- a/toolkit/components/payments/test/browser/head.js
++++ b/toolkit/components/payments/test/browser/head.js
+@@ -3,47 +3,88 @@
+ /* eslint
+   "no-unused-vars": ["error", {
+     vars: "local",
+     args: "none",
+     varsIgnorePattern: "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$",
+   }],
+ */
+ 
+-const REQUEST_ID_PREFIX = "paymentRequest-";
+ const BLANK_PAGE_URL = "https://example.com/browser/toolkit/components/" +
+                        "payments/test/browser/blank_page.html";
+ const PREF_PAYMENT_ENABLED = "dom.payments.request.enabled";
++const paymentUISrv = Cc["@mozilla.org/dom/payments/payment-ui-service;1"]
++                     .getService().wrappedJSObject;
+ 
+ 
+-async function getDialogWindow() {
++/**
++ * Return the container (e.g. dialog or overlay) that the payment request contents are shown in.
++ * This abstracts away the details of the widget used so that this can more earily transition from a
++ * dialog to another kind of overlay.
++ * Consumers shouldn't rely on a dialog window being returned.
++ * @returns {Promise}
++ */
++async function getPaymentWidget() {
+   let win;
+   await BrowserTestUtils.waitForCondition(() => {
+     win = Services.wm.getMostRecentWindow(null);
+-    return win.name.startsWith(REQUEST_ID_PREFIX);
++    return win.name.startsWith(paymentUISrv.REQUEST_ID_PREFIX);
+   }, "payment dialog should be the most recent");
+ 
+   return win;
+ }
+ 
+-function requestIdForWindow(window) {
+-  let windowName = window.name;
++async function getPaymentFrame(widget) {
++  return widget.document.getElementById("paymentRequestFrame");
++}
+ 
+-  return windowName.startsWith(REQUEST_ID_PREFIX) ?
+-    windowName.replace(REQUEST_ID_PREFIX, "") : // returns suffix, which is the requestId
+-    null;
++async function waitForMessageFromWidget(messageType, widget = null) {
++  info("waitForMessageFromWidget: " + messageType);
++  return new Promise(resolve => {
++    Services.mm.addMessageListener("paymentContentToChrome", function onMessage({data, target}) {
++      if (data.messageType != messageType) {
++        return;
++      }
++      if (widget && widget != target) {
++        return;
++      }
++      resolve();
++      info(`Got ${messageType} from widget`);
++      Services.mm.removeMessageListener("paymentContentToChrome", onMessage);
++    });
++  });
++}
++
++async function waitForWidgetReady(widget = null) {
++  return waitForMessageFromWidget("paymentDialogReady", widget);
++}
++
++function spawnPaymentDialogTask(paymentDialogFrame, taskFn, args = null) {
++  return ContentTask.spawn(paymentDialogFrame.frameLoader, args, taskFn);
+ }
+ 
+ /**
+  * Common content tasks functions to be used with ContentTask.spawn.
+  */
+ let ContentTasks = {
+   createAndShowRequest: async ({methodData, details, options}) => {
+     let rq = new content.PaymentRequest(methodData, details, options);
+     content.rq = rq; // assign it so we can retrieve it later
+     rq.show();
+   },
++
++  /**
++   * Click the cancel button
++   *
++   * Don't await on this task since the cancel can close the dialog before
++   * ContentTask can resolve the promise.
++   *
++   * @returns {undefined}
++   */
++  manuallyClickCancel: () => {
++    content.document.getElementById("cancel").click();
++  },
+ };
+ 
+ 
+ add_task(async function setup_head() {
+   await SpecialPowers.pushPrefEnv({set: [[PREF_PAYMENT_ENABLED, true]]});
+ });

+ 43 - 0
mozilla-release/patches/1390018-57a1.patch

@@ -0,0 +1,43 @@
+# HG changeset patch
+# User Eden Chuang <echuang@mozilla.com>
+# Date 1502838985 -28800
+# Node ID 767a4bd1abbaf3de1cd2271bc92f0edc29581c9b
+# Parent  b87980bcbd5d42066cb184d72643c0f3636de6cf
+Bug 1390018 - Build and test Payments UI code on Nightly builds only. r=MattN
+
+diff --git a/dom/payments/test/mochitest.ini b/dom/payments/test/mochitest.ini
+--- a/dom/payments/test/mochitest.ini
++++ b/dom/payments/test/mochitest.ini
+@@ -8,16 +8,17 @@ support-files =
+   BasiccardChromeScript.js
+   ConstructorChromeScript.js
+   CurrencyAmountValidationChromeScript.js
+   GeneralChromeScript.js
+   PMIValidationChromeScript.js
+   ShowPaymentChromeScript.js
+ 
+ [test_abortPayment.html]
++run-if = nightly_build # Bug 1390018: Depends on the Nightly-only UI service
+ [test_basiccard.html]
+ [test_block_none10s.html]
+ skip-if = e10s # Bug 1408250: Don't expose PaymentRequest Constructor in non-e10s
+ [test_canMakePayment.html]
+ run-if = nightly_build # Bug 1390737: Depends on the Nightly-only UI service
+ [test_constructor.html]
+ [test_currency_amount_validation.html]
+ [test_payment-request-in-iframe.html]
+diff --git a/toolkit/components/moz.build b/toolkit/components/moz.build
+--- a/toolkit/components/moz.build
++++ b/toolkit/components/moz.build
+@@ -106,10 +106,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind
+     DIRS += ['gfx']
+ 
+ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
+     EXTRA_COMPONENTS += [
+         'nsDefaultCLH.js',
+         'nsDefaultCLH.manifest',
+     ]
+ 
+-if CONFIG['MOZ_BUILD_APP'] == 'browser':
++if CONFIG['NIGHTLY_BUILD'] and CONFIG['MOZ_BUILD_APP'] == 'browser':
+     DIRS += ['payments']

+ 139 - 0
mozilla-release/patches/1399780-58a1.patch

@@ -0,0 +1,139 @@
+# HG changeset patch
+# User Thomas Nguyen <tnguyen@mozilla.com>
+# Date 1508121693 -28800
+# Node ID 5fdebd6d25a567eec64b5c0e54e6b3eb667ca9b1
+# Parent  5283b44ff51e356a78cf4819d8f90c398c82c8dd
+Bug 1399780 - Add a test that referrerpolicy attributes are honoured correctly in speculative loading r=ckerschb
+
+MozReview-Commit-ID: 6wU7RMEghjx
+
+diff --git a/dom/base/test/file_bug704320_preload_attr.html b/dom/base/test/file_bug704320_preload_attr.html
+new file mode 100644
+--- /dev/null
++++ b/dom/base/test/file_bug704320_preload_attr.html
+@@ -0,0 +1,27 @@
++<!DOCTYPE HTML>
++<html>
++<!--
++Test whether the speculative parser should use the referrerpolicy attribute
++https://bugzilla.mozilla.org/show_bug.cgi?id=1399780
++-->
++<head>
++  <meta charset="utf-8">
++  <script type="text/javascript" src="file_bug704320_preload_common.js"></script>
++  <script language="javascript" type="text/javascript">
++    // interfere doc.write(meta referrer) to the down side preloads
++    document.write("<meta name='referrer' content='unsafe-url'>");
++  </script>
++
++  <link rel="stylesheet"
++        href="http://example.com/tests/dom/base/test/bug704320_counter.sjs?type=css"
++        onload="incrementLoad2('link', 2);"
++        referrerpolicy="origin"/>
++
++  <img src="http://example.com/tests/dom/base/test/bug704320_counter.sjs?type=img"
++       onload="incrementLoad2('img', 2);"
++       referrerpolicy="origin"/>
++
++</head>
++<body>
++</body>
++</html>
+diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini
+--- a/dom/base/test/mochitest.ini
++++ b/dom/base/test/mochitest.ini
+@@ -103,16 +103,17 @@ support-files =
+   file_bug687859-16.js^headers^
+   file_bug687859-bom.js
+   file_bug687859-bom.js^headers^
+   file_bug687859-charset.js
+   file_bug687859-http.js
+   file_bug687859-http.js^headers^
+   file_bug687859-inherit.js
+   file_bug692434.xml
++  file_bug704320_preload_attr.html
+   file_bug704320_preload_common.js
+   file_bug704320_preload_reuse.html
+   file_bug704320_preload_noreuse.html
+   file_bug704320_redirect.html
+   file_bug707142_baseline.json
+   file_bug707142_bom.json
+   file_bug707142_utf-16.json
+   file_bug708620-2.html
+diff --git a/dom/base/test/test_bug704320_preload.html b/dom/base/test/test_bug704320_preload.html
+--- a/dom/base/test/test_bug704320_preload.html
++++ b/dom/base/test/test_bug704320_preload.html
+@@ -42,16 +42,27 @@ var tests = (function*() {
+   // load the second test frame
+   // it will call back into this function via postMessage when it finishes loading.
+   // and continue beyond the yield.
+   yield iframe.src = 'file_bug704320_preload_reuse.html';
+ 
+   // check the second test
+   yield checkResults(finalizePreloadReuse);
+ 
++  // reset the counter
++  yield resetCounter();
++
++  // load the third test frame
++  // it will call back into this function via postMessage when it finishes loading.
++  // and continue beyond the yield.
++  yield iframe.src = 'file_bug704320_preload_attr.html';
++
++  // check the third test
++  yield checkResults(finalizePreloadReferrerPolicyAttr);
++
+   // complete.
+   SimpleTest.finish();
+ })();
+ 
+ // Helper functions below.
+ 
+ /**
+  * This checks the first test: a test where the preloads should not
+@@ -116,16 +127,44 @@ function finalizePreloadReuse(results) {
+        "No loads for " + x + " should have a missing referrer.");
+     is(results[x].referrers.indexOf('full'), -1,
+        "No loads for " + x + " should have an 'full' referrer.")
+   }
+ 
+   advance();
+ }
+ 
++/**
++ * This checks the third test: a test where preload requests of image, style
++ * should use referrerpolicy attribute and we expect the preloads should not
++ * be reused
++ */
++function finalizePreloadReferrerPolicyAttr(results) {
++  var expected = {'css': {'count': 1, 'referrers': ['origin']},
++                  'img': {'count': 1, 'referrers': ['origin']}};
++
++  for (var x in expected) {
++    ok(x in results, "some " + x + " loads required in results object.");
++
++    is(results[x].count, expected[x].count,
++       "Expected " + expected[x].count + " loads for " + x + " requests.");
++
++    // 'origin' is required
++    ok(results[x].referrers.indexOf('origin') >= 0,
++       "One load for " + x + " should have had 'origin' referrer.");
++
++    // no other values should be in the referrers.
++    is(results[x].referrers.indexOf('none'), -1,
++       "No loads for " + x + " should have a missing referrer.");
++    is(results[x].referrers.indexOf('full'), -1,
++       "No loads for " + x + " should have an 'full' referrer.")
++  }
++
++  advance();
++}
+ 
+ /**
+  * Grabs the results via XHR and passes to checker.
+  */
+ function checkResults(checker) {
+   doXHR('/tests/dom/base/test/bug704320_counter.sjs?results',
+         function(xhr) {
+           checker(JSON.parse(xhr.responseText));
+

+ 33 - 0
mozilla-release/patches/1403743-59a1.patch

@@ -0,0 +1,33 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1514003971 -28800
+# Node ID 5a29258e113d285dc2c9692b0f09c672554b5812
+# Parent  4d1355701c926172d8f9825813e9f7e66421536a
+Bug 1403743 - Keeping the mousemove coalescing enabled in the release build. r=smaug.
+
+diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
+--- a/modules/libpref/init/all.js
++++ b/modules/libpref/init/all.js
+@@ -1381,21 +1381,17 @@ pref("privacy.resistFingerprinting", fal
+ pref("privacy.trackingprotection.lower_network_priority", true);
+ #else
+ pref("privacy.trackingprotection.lower_network_priority", false);
+ #endif
+ 
+ pref("dom.event.contextmenu.enabled",       true);
+ pref("dom.event.clipboardevents.enabled",   true);
+ pref("dom.event.highrestimestamp.enabled",  true);
+-#ifdef NIGHTLY_BUILD
+ pref("dom.event.coalesce_mouse_move",       true);
+-#else
+-pref("dom.event.coalesce_mouse_move",       false);
+-#endif
+ 
+ pref("dom.webcomponents.enabled",           false);
+ pref("dom.webcomponents.customelements.enabled", false);
+ 
+ pref("javascript.enabled",                  true);
+ pref("javascript.options.strict",           false);
+ #ifdef DEBUG
+ pref("javascript.options.strict.debug",     false);
+

+ 30 - 0
mozilla-release/patches/1415572-59a1.patch

@@ -0,0 +1,30 @@
+# HG changeset patch
+# User Jean-Luc Bonnafoux <jeanluc.bonnafoux@wanadoo.fr>
+# Date 1510159355 -3600
+# Node ID bba904a2db1e30bc9c86b717557eceee6b367e18
+# Parent  afbde1e5a67d2fe8dd8763f69f1ed4ddf4422b68
+Bug 1415572 - accessible\base\logging.cpp printf option fix. r=surkov
+
+diff --git a/accessible/base/Logging.cpp b/accessible/base/Logging.cpp
+--- a/accessible/base/Logging.cpp
++++ b/accessible/base/Logging.cpp
+@@ -753,17 +753,17 @@ logging::MsgBegin(const char* aTitle, co
+   va_start(argptr, aMsgText);
+   vprintf(aMsgText, argptr);
+   va_end(argptr);
+ 
+   PRIntervalTime time = PR_IntervalNow();
+   uint32_t mins = (PR_IntervalToSeconds(time) / 60) % 60;
+   uint32_t secs = PR_IntervalToSeconds(time) % 60;
+   uint32_t msecs = PR_IntervalToMilliseconds(time) % 1000;
+-  printf("; %02d:%02d.%03d", mins, secs, msecs);
++  printf("; %02u:%02u.%03u", mins, secs, msecs);
+ 
+   printf("\n  {\n");
+ }
+ 
+ void
+ logging::MsgEnd()
+ {
+   printf("  }\n");
+

+ 22 - 0
mozilla-release/patches/1417657-59a1.patch

@@ -0,0 +1,22 @@
+# HG changeset patch
+# User Andrew McCreight <continuation@gmail.com>
+# Date 1510781396 28800
+# Node ID f515ace4d2da7cdd73c6d915189f890284da2778
+# Parent  b88633697b75b3fae629cb89c9666dd7233c1f25
+Bug 1417657 - Add basic test for prio(). r=bkelly
+
+MozReview-Commit-ID: AigSKc2EKGr
+
+diff --git a/ipc/ipdl/test/ipdl/ok/Prio.ipdl b/ipc/ipdl/test/ipdl/ok/Prio.ipdl
+new file mode 100644
+--- /dev/null
++++ b/ipc/ipdl/test/ipdl/ok/Prio.ipdl
+@@ -0,0 +1,7 @@
++async protocol Prio
++{
++child:
++  prio(normal) async NormalPrio();
++  prio(high) async HighPrio();
++  prio(input) async InputPrio();
++};
+

+ 41 - 0
mozilla-release/patches/1419902-59a1.patch

@@ -0,0 +1,41 @@
+# HG changeset patch
+# User Christoph Kerschbaumer <ckerschb@christophkerschbaumer.com>
+# Date 1513193233 -3600
+# Node ID 6fda621a4e3b037a3caf873905ec0b8dff5272e5
+# Parent  2e6b873b57f5f5e0cf7de9b62c71981eef4f59b5
+Bug 1419902 - Add crashtest for nullptr deref of doc within nsGlobalWindowOuter::CloseOuter. r=smaug,ryanvm
+
+diff --git a/dom/base/crashtests/1419902.html b/dom/base/crashtests/1419902.html
+new file mode 100644
+--- /dev/null
++++ b/dom/base/crashtests/1419902.html
+@@ -0,0 +1,15 @@
++<html>
++  <head>
++    <script>
++      for (let i = 0; i < 38; i++) {
++        customElements.define("custom-element_0", class extends HTMLElement {
++          constructor() {
++            try { o1 = document.createElement("custom-element_0") } catch (e) {}
++            try { o2 = window.open("data:,") } catch (e) {}
++          }
++        })
++        try { o3 = document.createElement("custom-element_0") } catch (e) {}
++      }
++    </script>
++  </head>
++</html>
+diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list
+--- a/dom/base/crashtests/crashtests.list
++++ b/dom/base/crashtests/crashtests.list
+@@ -233,8 +233,9 @@ load 1400701.html
+ load 1403377.html
+ load 1405771.html
+ load 1406109-1.html
+ pref(dom.webcomponents.enabled,true) load 1324463.html
+ pref(dom.webcomponents.customelements.enabled,true) load 1413815.html
+ load 1411473.html
+ pref(dom.webcomponents.enabled,false) load 1422931.html
+ pref(dom.webcomponents.enabled,true) load 1419799.html
++skip-if(!browserIsRemote) pref(dom.webcomponents.customelements.enabled,true) pref(dom.disable_open_during_load,false) load 1419902.html # skip on non e10s loads, Bug 1419902
+

+ 316 - 0
mozilla-release/patches/1420589-1-59a1.patch

@@ -0,0 +1,316 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1511321795 -28800
+#      Wed Nov 22 11:36:35 2017 +0800
+# Node ID 16a0ca0a2afaaa322fb3d885b2760ee0d6ff04e0
+# Parent  ec28a490365c49cfd4320648652441e1ad8e779f
+Bug 1420589 Part1: Separate the logic of finding the event target for touch events into a function. r=smaug.
+
+MozReview-Commit-ID: 5IWpVBdiQ3o
+
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -7082,128 +7082,42 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+         }
+       }
+       else {
+         ClearMouseCapture(nullptr);
+         capturingContent = nullptr;
+       }
+     }
+ 
+-    // all touch events except for touchstart use a captured target
+-    if (aEvent->mClass == eTouchEventClass && aEvent->mMessage != eTouchStart) {
+-      captureRetarget = true;
+-    }
+-
+     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+     bool isWindowLevelMouseExit = (aEvent->mMessage == eMouseExitFromWidget) &&
+       (mouseEvent && mouseEvent->mExitFrom == WidgetMouseEvent::eTopLevel);
+ 
+     // Get the frame at the event point. However, don't do this if we're
+     // capturing and retargeting the event because the captured frame will
+     // be used instead below. Also keep using the root frame if we're dealing
+     // with a window-level mouse exit event since we want to start sending
+     // mouse out events at the root EventStateManager.
+     if (!captureRetarget && !isWindowLevelMouseExit) {
+-      nsPoint eventPoint;
+-      uint32_t flags = 0;
+-      if (aEvent->mMessage == eTouchStart) {
+-        if (gfxPrefs::APZAllowZooming()) {
+-          // Setting this flag will skip the scrollbars on the root frame from
+-          // participating in hit-testing, and we only want that to happen on
+-          // zoomable platforms (for now).
++      if (aEvent->mClass == eTouchEventClass) {
++        frame = TouchManager::SetupTarget(aEvent->AsTouchEvent(), frame);
++      } else {
++        uint32_t flags = 0;
++        nsPoint eventPoint =
++          nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
++
++        if (mouseEvent && mouseEvent->mClass == eMouseEventClass &&
++            mouseEvent->mIgnoreRootScrollFrame) {
+           flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
+         }
+-        WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+-        // if this is a continuing session, ensure that all these events are
+-        // in the same document by taking the target of the events already in
+-        // the capture list
+-        nsCOMPtr<nsIContent> anyTarget;
+-        if (touchEvent->mTouches.Length() > 1) {
+-          anyTarget = TouchManager::GetAnyCapturedTouchTarget();
++        nsIFrame* target =
++          FindFrameTargetedByInputEvent(aEvent, frame, eventPoint, flags);
++        if (target) {
++          frame = target;
+         }
+-
+-        // When handling pointerstart with multiple touch points, frame is
+-        // overwritten. Keep the original root frame for hit test.
+-        nsIFrame* rootFrame = frame;
+-        for (int32_t i = touchEvent->mTouches.Length(); i; ) {
+-          --i;
+-          dom::Touch* touch = touchEvent->mTouches[i];
+-
+-          int32_t id = touch->Identifier();
+-          if (!TouchManager::HasCapturedTouch(id)) {
+-            // find the target for this touch
+-            eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
+-                                                              touch->mRefPoint,
+-                                                              rootFrame);
+-            nsIFrame* target = FindFrameTargetedByInputEvent(aEvent,
+-                                                             rootFrame,
+-                                                             eventPoint,
+-                                                             flags);
+-            if (target && !anyTarget) {
+-              target->GetContentForEvent(aEvent, getter_AddRefs(anyTarget));
+-              while (anyTarget && !anyTarget->IsElement()) {
+-                anyTarget = anyTarget->GetParent();
+-              }
+-              touch->SetTarget(anyTarget);
+-            } else {
+-              nsIFrame* newTargetFrame = nullptr;
+-              for (nsIFrame* f = target; f;
+-                   f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
+-                if (f->PresContext()->Document() == anyTarget->OwnerDoc()) {
+-                  newTargetFrame = f;
+-                  break;
+-                }
+-                // We must be in a subdocument so jump directly to the root frame.
+-                // GetParentOrPlaceholderForCrossDoc gets called immediately to
+-                // jump up to the containing document.
+-                f = f->PresContext()->GetPresShell()->GetRootFrame();
+-              }
+-
+-              // if we couldn't find a target frame in the same document as
+-              // anyTarget, remove the touch from the capture touch list, as
+-              // well as the event->mTouches array. touchmove events that aren't
+-              // in the captured touch list will be discarded
+-              if (!newTargetFrame) {
+-                touchEvent->mTouches.RemoveElementAt(i);
+-              } else {
+-                target = newTargetFrame;
+-                nsCOMPtr<nsIContent> targetContent;
+-                target->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
+-                while (targetContent && !targetContent->IsElement()) {
+-                  targetContent = targetContent->GetParent();
+-                }
+-                touch->SetTarget(targetContent);
+-              }
+-            }
+-            if (target) {
+-              frame = target;
+-            }
+-          } else {
+-            // This touch is an old touch, we need to ensure that is not
+-            // marked as changed and set its target correctly
+-            touch->mChanged = false;
+-            int32_t id = touch->Identifier();
+-
+-            RefPtr<dom::Touch> oldTouch = TouchManager::GetCapturedTouch(id);
+-            if (oldTouch) {
+-              touch->SetTarget(oldTouch->mTarget);
+-            }
+-          }
+-        }
+-      } else {
+-        eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
+-      }
+-      if (mouseEvent && mouseEvent->mClass == eMouseEventClass &&
+-          mouseEvent->mIgnoreRootScrollFrame) {
+-        flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
+-      }
+-      nsIFrame* target =
+-        FindFrameTargetedByInputEvent(aEvent, frame, eventPoint, flags);
+-      if (target) {
+-        frame = target;
+       }
+     }
+ 
+     // if a node is capturing the mouse, check if the event needs to be
+     // retargeted at the capturing content instead. This will be the case when
+     // capture retargeting is being used, no frame was found or the frame's
+     // content is not a descendant of the capturing content.
+     if (capturingContent &&
+diff --git a/layout/base/TouchManager.cpp b/layout/base/TouchManager.cpp
+--- a/layout/base/TouchManager.cpp
++++ b/layout/base/TouchManager.cpp
+@@ -2,20 +2,22 @@
+ /* vim: set ts=8 sts=2 et sw=2 tw=80: */
+ /* 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/.
+  */
+ 
+ #include "TouchManager.h"
+ 
++#include "gfxPrefs.h"
+ #include "mozilla/dom/EventTarget.h"
+ #include "mozilla/PresShell.h"
+ #include "nsIFrame.h"
+ #include "nsView.h"
++#include "PositionedEventTargeting.h"
+ 
+ using namespace mozilla::dom;
+ 
+ namespace mozilla {
+ 
+ nsDataHashtable<nsUint32HashKey, TouchManager::TouchInfo>* TouchManager::sCaptureTouchList;
+ 
+ /*static*/ void
+@@ -105,16 +107,110 @@ TouchManager::EvictTouches()
+ {
+   WidgetTouchEvent::AutoTouchArray touches;
+   AppendToTouchList(&touches);
+   for (uint32_t i = 0; i < touches.Length(); ++i) {
+     EvictTouchPoint(touches[i], mDocument);
+   }
+ }
+ 
++/* static */ nsIFrame*
++TouchManager::SetupTarget(WidgetTouchEvent* aEvent, nsIFrame* aFrame)
++{
++  MOZ_ASSERT(aEvent);
++
++  if (!aEvent || aEvent->mMessage != eTouchStart) {
++    // All touch events except for touchstart use a captured target.
++    return aFrame;
++  }
++
++  uint32_t flags = 0;
++  // Setting this flag will skip the scrollbars on the root frame from
++  // participating in hit-testing, and we only want that to happen on
++  // zoomable platforms (for now).
++  if (gfxPrefs::APZAllowZooming()) {
++    flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
++  }
++  // if this is a continuing session, ensure that all these events are
++  // in the same document by taking the target of the events already in
++  // the capture list
++  nsCOMPtr<nsIContent> anyTarget;
++  if (aEvent->mTouches.Length() > 1) {
++    anyTarget = TouchManager::GetAnyCapturedTouchTarget();
++  }
++
++  nsPoint eventPoint;
++  nsIFrame* frame = aFrame;
++  for (int32_t i = aEvent->mTouches.Length(); i; ) {
++    --i;
++    dom::Touch* touch = aEvent->mTouches[i];
++
++    int32_t id = touch->Identifier();
++    if (!TouchManager::HasCapturedTouch(id)) {
++      // find the target for this touch
++      eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
++                                                        touch->mRefPoint,
++                                                        aFrame);
++      nsIFrame* target = FindFrameTargetedByInputEvent(aEvent,
++                                                       aFrame,
++                                                       eventPoint,
++                                                       flags);
++      if (target && !anyTarget) {
++        target->GetContentForEvent(aEvent, getter_AddRefs(anyTarget));
++        while (anyTarget && !anyTarget->IsElement()) {
++          anyTarget = anyTarget->GetParent();
++        }
++        touch->SetTarget(anyTarget);
++      } else {
++        nsIFrame* newTargetFrame = nullptr;
++        for (nsIFrame* f = target; f;
++             f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
++          if (f->PresContext()->Document() == anyTarget->OwnerDoc()) {
++            newTargetFrame = f;
++            break;
++          }
++          // We must be in a subdocument so jump directly to the root frame.
++          // GetParentOrPlaceholderForCrossDoc gets called immediately to
++          // jump up to the containing document.
++          f = f->PresShell()->GetRootFrame();
++        }
++
++        // if we couldn't find a target frame in the same document as
++        // anyTarget, remove the touch from the capture touch list, as
++        // well as the event->mTouches array. touchmove events that aren't
++        // in the captured touch list will be discarded
++        if (!newTargetFrame) {
++          aEvent->mTouches.RemoveElementAt(i);
++        } else {
++          target = newTargetFrame;
++          nsCOMPtr<nsIContent> targetContent;
++          target->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
++          while (targetContent && !targetContent->IsElement()) {
++            targetContent = targetContent->GetParent();
++          }
++          touch->SetTarget(targetContent);
++        }
++      }
++      if (target) {
++        frame = target;
++      }
++    } else {
++      // This touch is an old touch, we need to ensure that is not
++      // marked as changed and set its target correctly
++      touch->mChanged = false;
++
++      RefPtr<dom::Touch> oldTouch = TouchManager::GetCapturedTouch(id);
++      if (oldTouch) {
++        touch->SetTarget(oldTouch->mTarget);
++      }
++    }
++  }
++  return FindFrameTargetedByInputEvent(aEvent, frame, eventPoint, flags);
++}
++
+ bool
+ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
+                              nsEventStatus* aStatus,
+                              bool& aTouchIsNew,
+                              bool& aIsHandlingUserInput,
+                              nsCOMPtr<nsIContent>& aCurrentEventContent)
+ {
+   switch (aEvent->mMessage) {
+diff --git a/layout/base/TouchManager.h b/layout/base/TouchManager.h
+--- a/layout/base/TouchManager.h
++++ b/layout/base/TouchManager.h
+@@ -26,16 +26,20 @@ class TouchManager {
+ public:
+   // Initialize and release static variables
+   static void InitializeStatics();
+   static void ReleaseStatics();
+ 
+   void Init(PresShell* aPresShell, nsIDocument* aDocument);
+   void Destroy();
+ 
++  // Perform hit test and setup the event targets for touchstart. Other touch
++  // events are dispatched to the same target as touchstart.
++  static nsIFrame* SetupTarget(WidgetTouchEvent* aEvent, nsIFrame* aFrame);
++
+   bool PreHandleEvent(mozilla::WidgetEvent* aEvent,
+                       nsEventStatus* aStatus,
+                       bool& aTouchIsNew,
+                       bool& aIsHandlingUserInput,
+                       nsCOMPtr<nsIContent>& aCurrentEventContent);
+ 
+   static already_AddRefed<nsIContent> GetAnyCapturedTouchTarget();
+   static bool HasCapturedTouch(int32_t aId);

+ 163 - 0
mozilla-release/patches/1420589-2-59a1.patch

@@ -0,0 +1,163 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1511703535 -28800
+#      Sun Nov 26 21:38:55 2017 +0800
+# Node ID ce9b2476de5da58a3b3e24efc23e7cf9532efe83
+# Parent  35ae33d44f954881d0e629e147ed0e027bc529f4
+Bug 1420589 Part2: Define a helper class to store the target of pointer events. r=smaug.
+
+MozReview-Commit-ID: IS5MM3gXpC4
+
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -536,16 +536,57 @@ public:
+     }
+     return NS_OK;
+   }
+ 
+ private:
+   nsCOMPtr<nsIDocument> mDocument;
+ };
+ 
++// This is a helper class to track whether the targeted frame is destroyed after
++// dispatching pointer events. In that case, we need the original targeted
++// content so that we can dispatch the mouse events to it.
++class MOZ_STACK_CLASS AutoPointerEventTargetUpdater final
++{
++public:
++  AutoPointerEventTargetUpdater(PresShell* aShell,
++                                WidgetGUIEvent* aEvent,
++                                nsIFrame* aFrame,
++                                nsIContent** aTargetContent)
++  {
++    MOZ_ASSERT(aShell);
++    MOZ_ASSERT(aEvent);
++    MOZ_ASSERT(aFrame);
++    if (!aTargetContent || aEvent->mClass != ePointerEventClass) {
++      return;
++    }
++    MOZ_ASSERT(!aFrame->GetContent() ||
++               aShell->GetDocument() == aFrame->GetContent()->OwnerDoc());
++
++    MOZ_ASSERT(PointerEventHandler::IsPointerEventEnabled());
++    mShell = aShell;
++    mWeakFrame = aFrame;
++    mTargetContent = aTargetContent;
++    aShell->mPointerEventTarget = aFrame->GetContent();
++  }
++
++  ~AutoPointerEventTargetUpdater()
++  {
++    if (!mTargetContent || !mShell || mWeakFrame.IsAlive()) {
++      return;
++    }
++    mShell->mPointerEventTarget.swap(*mTargetContent);
++  }
++
++private:
++  RefPtr<PresShell> mShell;
++  AutoWeakFrame mWeakFrame;
++  nsIContent** mTargetContent;
++};
++
+ bool PresShell::sDisableNonTestMouseEvents = false;
+ 
+ mozilla::LazyLogModule PresShell::gLog("PresShell");
+ 
+ #ifdef DEBUG
+ static void
+ VerifyStyleTree(nsPresContext* aPresContext, nsFrameManager* aFrameManager)
+ {
+@@ -7061,51 +7102,31 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+                 shell = static_cast<PresShell*>(activeShell);
+                 frame = shell->GetRootFrame();
+               }
+             }
+           }
+         }
+       }
+     }
+-
+-    // Before HandlePositionedEvent we should save mPointerEventTarget in some
+-    // cases
+-    AutoWeakFrame weakFrame;
+-    if (aTargetContent && ePointerEventClass == aEvent->mClass) {
+-      MOZ_ASSERT(PointerEventHandler::IsPointerEventEnabled());
+-      weakFrame = frame;
+-      shell->mPointerEventTarget = frame->GetContent();
+-      MOZ_ASSERT(!frame->GetContent() ||
+-                 shell->GetDocument() == frame->GetContent()->OwnerDoc());
+-    }
+-
+     // Prevent deletion until we're done with event handling (bug 336582) and
+     // swap mPointerEventTarget to *aTargetContent
+     nsCOMPtr<nsIPresShell> kungFuDeathGrip(shell);
+     nsresult rv;
++    AutoPointerEventTargetUpdater updater(shell, aEvent, frame, aTargetContent);
+     if (shell != this) {
+       // Handle the event in the correct shell.
+       // We pass the subshell's root frame as the frame to start from. This is
+       // the only correct alternative; if the event was captured then it
+       // must have been captured by us or some ancestor shell and we
+       // now ask the subshell to dispatch it normally.
+       rv = shell->HandlePositionedEvent(frame, aEvent, aEventStatus);
+     } else {
+       rv = HandlePositionedEvent(frame, aEvent, aEventStatus);
+     }
+-
+-    // After HandlePositionedEvent we should reestablish
+-    // content (which still live in tree) in some cases
+-    if (aTargetContent && ePointerEventClass == aEvent->mClass) {
+-      if (!weakFrame.IsAlive()) {
+-        shell->mPointerEventTarget.swap(*aTargetContent);
+-      }
+-    }
+-
+     return rv;
+   }
+ 
+   nsresult rv = NS_OK;
+ 
+   if (frame) {
+     PushCurrentEventInfo(nullptr, nullptr);
+ 
+diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h
+--- a/layout/base/PresShell.h
++++ b/layout/base/PresShell.h
+@@ -37,16 +37,17 @@ class nsRange;
+ struct RangePaintInfo;
+ struct nsCallbackEventRequest;
+ #ifdef MOZ_REFLOW_PERF
+ class ReflowCountMgr;
+ #endif
+ 
+ class nsPresShellEventCB;
+ class nsAutoCauseReflowNotifier;
++class AutoPointerEventTargetUpdater;
+ 
+ namespace mozilla {
+ 
+ namespace dom {
+ class Element;
+ class Selection;
+ }  // namespace dom
+ 
+@@ -413,16 +414,17 @@ protected:
+   void UnsuppressAndInvalidate();
+ 
+   void WillCauseReflow() {
+     nsContentUtils::AddScriptBlocker();
+     ++mChangeNestCount;
+   }
+   nsresult DidCauseReflow();
+   friend class ::nsAutoCauseReflowNotifier;
++  friend class ::AutoPointerEventTargetUpdater;
+ 
+   nsresult DispatchEventToDOM(mozilla::WidgetEvent* aEvent,
+                               nsEventStatus* aStatus,
+                               nsPresShellEventCB* aEventCB);
+   void DispatchTouchEventToDOM(mozilla::WidgetEvent* aEvent,
+                                nsEventStatus* aStatus,
+                                nsPresShellEventCB* aEventCB,
+                                bool aTouchIsNew);

+ 203 - 0
mozilla-release/patches/1420589-3-59a1.patch

@@ -0,0 +1,203 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1512224725 -28800
+#      Sat Dec 02 22:25:25 2017 +0800
+# Node ID f9f606cef4077c22e8d32e0e0362761e69b7b705
+# Parent  373be6bbd19534942d5273a7e7849265bfb6c19f
+Bug 1420589 Part3: Merge PresShell::HandlePositionedEvent to PresShell::HandleEvent. r=smaug.
+
+MozReview-Commit-ID: 9w1DSb5uXME
+
+diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
+--- a/dom/events/EventStateManager.cpp
++++ b/dom/events/EventStateManager.cpp
+@@ -4874,17 +4874,17 @@ EventStateManager::CheckForAndDispatchCl
+     bool fireAuxClick = notDispatchToContents;
+ 
+     nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
+     if (presShell) {
+       nsCOMPtr<nsIContent> mouseContent = GetEventTargetContent(aEvent);
+       // Click events apply to *elements* not nodes. At this point the target
+       // content may have been reset to some non-element content, and so we need
+       // to walk up the closest ancestor element, just like we do in
+-      // nsPresShell::HandlePositionedEvent.
++      // nsPresShell::HandleEvent.
+       while (mouseContent && !mouseContent->IsElement()) {
+         mouseContent = mouseContent->GetParent();
+       }
+ 
+       if (!mouseContent && !mCurrentTarget) {
+         return NS_OK;
+       }
+ 
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -7103,31 +7103,64 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+                 shell = static_cast<PresShell*>(activeShell);
+                 frame = shell->GetRootFrame();
+               }
+             }
+           }
+         }
+       }
+     }
++
++    if (!frame) {
++      NS_WARNING("Nothing to handle this event!");
++      return NS_OK;
++    }
++
++    nsCOMPtr<nsIContent> targetElement;
++    frame->GetContentForEvent(aEvent, getter_AddRefs(targetElement));
++
++    // If there is no content for this frame, target it anyway.  Some
++    // frames can be targeted but do not have content, particularly
++    // windows with scrolling off.
++    if (targetElement) {
++      // Bug 103055, bug 185889: mouse events apply to *elements*, not all
++      // nodes.  Thus we get the nearest element parent here.
++      // XXX we leave the frame the same even if we find an element
++      // parent, so that the text frame will receive the event (selection
++      // and friends are the ones who care about that anyway)
++      //
++      // We use weak pointers because during this tight loop, the node
++      // will *not* go away.  And this happens on every mousemove.
++      while (targetElement && !targetElement->IsElement()) {
++        targetElement = targetElement->GetFlattenedTreeParent();
++      }
++
++      // If we found an element, target it.  Otherwise, target *nothing*.
++      if (!targetElement) {
++        return NS_OK;
++      }
++    }
++
+     // Prevent deletion until we're done with event handling (bug 336582) and
+     // swap mPointerEventTarget to *aTargetContent
+     nsCOMPtr<nsIPresShell> kungFuDeathGrip(shell);
+     nsresult rv;
+     AutoPointerEventTargetUpdater updater(shell, aEvent, frame, aTargetContent);
+-    if (shell != this) {
+-      // Handle the event in the correct shell.
+-      // We pass the subshell's root frame as the frame to start from. This is
+-      // the only correct alternative; if the event was captured then it
+-      // must have been captured by us or some ancestor shell and we
+-      // now ask the subshell to dispatch it normally.
+-      rv = shell->HandlePositionedEvent(frame, aEvent, aEventStatus);
+-    } else {
+-      rv = HandlePositionedEvent(frame, aEvent, aEventStatus);
+-    }
++
++    // Handle the event in the correct shell.
++    // We pass the subshell's root frame as the frame to start from. This is
++    // the only correct alternative; if the event was captured then it
++    // must have been captured by us or some ancestor shell and we
++    // now ask the subshell to dispatch it normally.
++    shell->PushCurrentEventInfo(frame, targetElement);
++    rv = shell->HandleEventInternal(aEvent, aEventStatus, true);
++#ifdef DEBUG
++    shell->ShowEventTargetDebug();
++#endif
++    shell->PopCurrentEventInfo();
+     return rv;
+   }
+ 
+   nsresult rv = NS_OK;
+ 
+   if (frame) {
+     PushCurrentEventInfo(nullptr, nullptr);
+ 
+@@ -7267,69 +7300,16 @@ PresShell::ShowEventTargetDebug()
+ 
+     mDrawEventTargetFrame = mCurrentEventFrame;
+     mDrawEventTargetFrame->InvalidateFrame();
+   }
+ }
+ #endif
+ 
+ nsresult
+-PresShell::HandlePositionedEvent(nsIFrame* aTargetFrame,
+-                                 WidgetGUIEvent* aEvent,
+-                                 nsEventStatus* aEventStatus)
+-{
+-  nsresult rv = NS_OK;
+-
+-  PushCurrentEventInfo(nullptr, nullptr);
+-
+-  mCurrentEventFrame = aTargetFrame;
+-
+-  if (mCurrentEventFrame) {
+-    nsCOMPtr<nsIContent> targetElement;
+-    mCurrentEventFrame->GetContentForEvent(aEvent,
+-                                           getter_AddRefs(targetElement));
+-
+-    // If there is no content for this frame, target it anyway.  Some
+-    // frames can be targeted but do not have content, particularly
+-    // windows with scrolling off.
+-    if (targetElement) {
+-      // Bug 103055, bug 185889: mouse events apply to *elements*, not all
+-      // nodes.  Thus we get the nearest element parent here.
+-      // XXX we leave the frame the same even if we find an element
+-      // parent, so that the text frame will receive the event (selection
+-      // and friends are the ones who care about that anyway)
+-      //
+-      // We use weak pointers because during this tight loop, the node
+-      // will *not* go away.  And this happens on every mousemove.
+-      while (targetElement && !targetElement->IsElement()) {
+-        targetElement = targetElement->GetFlattenedTreeParent();
+-      }
+-
+-      // If we found an element, target it.  Otherwise, target *nothing*.
+-      if (!targetElement) {
+-        mCurrentEventContent = nullptr;
+-        mCurrentEventFrame = nullptr;
+-      } else if (targetElement != mCurrentEventContent) {
+-        mCurrentEventContent = targetElement;
+-      }
+-    }
+-  }
+-
+-  if (GetCurrentEventFrame()) {
+-    rv = HandleEventInternal(aEvent, aEventStatus, true);
+-  }
+-
+-#ifdef DEBUG
+-  ShowEventTargetDebug();
+-#endif
+-  PopCurrentEventInfo();
+-  return rv;
+-}
+-
+-nsresult
+ PresShell::HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame,
+                                  nsIContent* aContent, nsEventStatus* aStatus)
+ {
+ #if DEBUG
+   MOZ_ASSERT(!aFrame || aFrame->PresContext()->GetPresShell() == this,
+              "wrong shell");
+   if (aContent) {
+     nsIDocument* doc = aContent->GetComposedDoc();
+diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h
+--- a/layout/base/PresShell.h
++++ b/layout/base/PresShell.h
+@@ -671,19 +671,16 @@ protected:
+   /**
+    * @param aIsHandlingNativeEvent      true when the caller (perhaps) handles
+    *                                    an event which is caused by native
+    *                                    event.  Otherwise, false.
+    */
+   nsresult HandleEventInternal(mozilla::WidgetEvent* aEvent,
+                                nsEventStatus* aStatus,
+                                bool aIsHandlingNativeEvent);
+-  nsresult HandlePositionedEvent(nsIFrame* aTargetFrame,
+-                                 mozilla::WidgetGUIEvent* aEvent,
+-                                 nsEventStatus* aEventStatus);
+ 
+   /*
+    * This and the next two helper methods are used to target and position the
+    * context menu when the keyboard shortcut is used to open it.
+    *
+    * If another menu is open, the context menu is opened relative to the
+    * active menuitem within the menu, or the menu itself if no item is active.
+    * Otherwise, if the caret is visible, the menu is opened near the caret.

+ 173 - 0
mozilla-release/patches/1420589-4-59a1.patch

@@ -0,0 +1,173 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1512780971 -28800
+#      Sat Dec 09 08:56:11 2017 +0800
+# Node ID 48188682637759405eade9bcca074ac4616c3d63
+# Parent  f9f606cef4077c22e8d32e0e0362761e69b7b705
+Bug 1420589 Part4: Revise PointerEventHandler utilities. r=smaug.
+
+MozReview-Commit-ID: DpGrbbYJbzh
+
+diff --git a/dom/events/PointerEventHandler.cpp b/dom/events/PointerEventHandler.cpp
+--- a/dom/events/PointerEventHandler.cpp
++++ b/dom/events/PointerEventHandler.cpp
+@@ -202,34 +202,35 @@ PointerEventHandler::CheckPointerCapture
+   if (!aEvent) {
+     return;
+   }
+   MOZ_ASSERT(IsPointerEventEnabled());
+   MOZ_ASSERT(aEvent->mClass == ePointerEventClass);
+ 
+   PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId);
+ 
+-  if (captureInfo &&
+-      captureInfo->mPendingContent != captureInfo->mOverrideContent) {
+-    // cache captureInfo->mPendingContent since it may be changed in the pointer
+-    // event listener
+-    nsIContent* pendingContent = captureInfo->mPendingContent.get();
+-    if (captureInfo->mOverrideContent) {
+-      DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent,
+-                                           captureInfo->mOverrideContent);
+-    }
+-    if (pendingContent) {
+-      DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
+-                                           pendingContent);
+-    }
++  if (!captureInfo ||
++      captureInfo->mPendingContent == captureInfo->mOverrideContent) {
++    return;
++  }
++  // cache captureInfo->mPendingContent since it may be changed in the pointer
++  // event listener
++  nsIContent* pendingContent = captureInfo->mPendingContent.get();
++  if (captureInfo->mOverrideContent) {
++    DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent,
++                                         captureInfo->mOverrideContent);
++  }
++  if (pendingContent) {
++    DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
++                                         pendingContent);
++  }
+ 
+-    captureInfo->mOverrideContent = pendingContent;
+-    if (captureInfo->Empty()) {
+-      sPointerCaptureList->Remove(aEvent->pointerId);
+-    }
++  captureInfo->mOverrideContent = pendingContent;
++  if (captureInfo->Empty()) {
++    sPointerCaptureList->Remove(aEvent->pointerId);
+   }
+ }
+ 
+ /* static */ void
+ PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame,
+                                               WidgetEvent* aEvent)
+ {
+   MOZ_ASSERT(aEvent->mMessage == ePointerDown);
+@@ -273,42 +274,41 @@ PointerEventHandler::GetPointerCapturing
+   PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
+   if (pointerCaptureInfo) {
+     return pointerCaptureInfo->mOverrideContent;
+   }
+   return nullptr;
+ }
+ 
+ /* static */ nsIFrame*
+-PointerEventHandler::GetPointerCapturingFrame(nsIFrame* aFrameUnderCursor,
+-                                              WidgetGUIEvent* aEvent)
++PointerEventHandler::GetPointerCapturingFrame(WidgetGUIEvent* aEvent)
+ {
+   if (!IsPointerEventEnabled() || (aEvent->mClass != ePointerEventClass &&
+                                    aEvent->mClass != eMouseEventClass) ||
+       aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) {
+     // Pointer capture should only be applied to all pointer events and mouse
+     // events except ePointerDown and eMouseDown;
+-    return aFrameUnderCursor;
++    return nullptr;
+   }
+ 
+   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+   if (!mouseEvent) {
+-    return aFrameUnderCursor;
++    return nullptr;
+   }
+ 
+   // Find the content which captures the pointer.
+   nsIContent* capturingContent =
+     GetPointerCapturingContent(mouseEvent->pointerId);
+ 
+   if (!capturingContent) {
+-    return aFrameUnderCursor;
++    return nullptr;
+   }
+   // Return the content's primary frame as the target frame.
+   nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
+-  return capturingFrame ? capturingFrame : aFrameUnderCursor;
++  return capturingFrame ? capturingFrame : nullptr;
+ }
+ 
+ /* static */ void
+ PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent)
+ {
+   // We should check that aChild does not contain pointer capturing elements.
+   // If it does we should release the pointer capture for the elements.
+   for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
+diff --git a/dom/events/PointerEventHandler.h b/dom/events/PointerEventHandler.h
+--- a/dom/events/PointerEventHandler.h
++++ b/dom/events/PointerEventHandler.h
+@@ -82,26 +82,24 @@ public:
+   // Implicitly get and release capture of current pointer for touch.
+   static void ImplicitlyCapturePointer(nsIFrame* aFrame, WidgetEvent* aEvent);
+   static void ImplicitlyReleasePointerCapture(WidgetEvent* aEvent);
+ 
+   /**
+    * GetPointerCapturingFrame returns a target frame of aEvent. If the event is
+    * a mouse or pointer event (except mousedown and pointerdown), the pointer
+    * may be captured by a content. This method returns the capturing content's
+-   * primary frame. Otherwise, aFrameUnderCursor.
++   * primary frame. Otherwise, nullptr.
+    *
+-   * @param aFrameUnderCursor    A frame under cursor.
+    * @param aEvent               A mouse event or pointer event which may be
+    *                             captured.
+    *
+    * @return                     Target frame for aEvent.
+    */
+-  static nsIFrame* GetPointerCapturingFrame(nsIFrame* aFrameUnderCursor,
+-                                            WidgetGUIEvent* aEvent);
++  static nsIFrame* GetPointerCapturingFrame(WidgetGUIEvent* aEvent);
+ 
+   static nsIContent* GetPointerCapturingContent(uint32_t aPointerId);
+ 
+   // Release pointer capture if captured by the specified content or it's
+   // descendant. This is called to handle the case that the pointer capturing
+   // content or it's parent is removed from the document.
+   static void ReleaseIfCaptureByDescendant(nsIContent* aContent);
+ 
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -7181,17 +7181,22 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+       AutoWeakFrame frameKeeper(frame);
+       PointerEventHandler::CheckPointerCaptureState(aEvent->AsPointerEvent());
+       // Prevent application crashes, in case damaged frame.
+       if (!frameKeeper.IsAlive()) {
+         frame = nullptr;
+       }
+     }
+ 
+-    frame = PointerEventHandler::GetPointerCapturingFrame(frame, aEvent);
++    nsIFrame* pointerCapturingFrame =
++      PointerEventHandler::GetPointerCapturingFrame(aEvent);
++
++    if (pointerCapturingFrame) {
++      frame = pointerCapturingFrame;
++    }
+ 
+     // Suppress mouse event if it's being targeted at an element inside
+     // a document which needs events suppressed
+     if (aEvent->mClass == eMouseEventClass &&
+         frame->PresContext()->Document()->EventHandlingSuppressed()) {
+       if (aEvent->mMessage == eMouseDown) {
+         mNoDelayedMouseEvents = true;
+       } else if (!mNoDelayedMouseEvents && (aEvent->mMessage == eMouseUp ||

+ 154 - 0
mozilla-release/patches/1420589-5-59a1.patch

@@ -0,0 +1,154 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1512781870 -28800
+#      Sat Dec 09 09:11:10 2017 +0800
+# Node ID 7a568d63c332deb3988fe058ced0fd6f62b3ed81
+# Parent  48188682637759405eade9bcca074ac4616c3d63
+Bug 1420589 Part5: Separate the logic to get shell for touch events into a function. r=smaug.
+
+MozReview-Commit-ID: LbYKBZAqeJM
+
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -6825,16 +6825,60 @@ PresShell::CanDispatchEvent(const Widget
+   bool rv =
+     mPresContext && !mHaveShutDown && nsContentUtils::IsSafeToRunScript();
+   if (aEvent) {
+     rv &= (aEvent && aEvent->mWidget && !aEvent->mWidget->Destroyed());
+   }
+   return rv;
+ }
+ 
++/* static */ PresShell*
++PresShell::GetShellForTouchEvent(WidgetGUIEvent* aEvent)
++{
++  PresShell* shell = nullptr;
++  switch (aEvent->mMessage) {
++  case eTouchMove:
++  case eTouchCancel:
++  case eTouchEnd: {
++    // get the correct shell to dispatch to
++    WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
++    for (dom::Touch* touch : touchEvent->mTouches) {
++      if (!touch) {
++        break;
++      }
++
++      RefPtr<dom::Touch> oldTouch =
++        TouchManager::GetCapturedTouch(touch->Identifier());
++      if (!oldTouch) {
++        break;
++      }
++
++      nsCOMPtr<nsIContent> content = do_QueryInterface(oldTouch->GetTarget());
++      if (!content) {
++        break;
++      }
++
++      nsIFrame* contentFrame = content->GetPrimaryFrame();
++      if (!contentFrame) {
++        break;
++      }
++
++      shell = static_cast<PresShell*>(contentFrame->PresContext()->PresShell());
++      if (shell) {
++        break;
++      }
++    }
++    break;
++  }
++  default:
++    break;
++  }
++  return shell;
++}
++
+ nsresult
+ PresShell::HandleEvent(nsIFrame* aFrame,
+                        WidgetGUIEvent* aEvent,
+                        bool aDontRetargetEvents,
+                        nsEventStatus* aEventStatus,
+                        nsIContent** aTargetContent)
+ {
+ #ifdef MOZ_TASK_TRACER
+@@ -7212,53 +7256,20 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+     }
+ 
+     if (!frame) {
+       NS_WARNING("Nothing to handle this event!");
+       return NS_OK;
+     }
+ 
+     PresShell* shell = static_cast<PresShell*>(frame->PresShell());
+-    switch (aEvent->mMessage) {
+-      case eTouchMove:
+-      case eTouchCancel:
+-      case eTouchEnd: {
+-        // get the correct shell to dispatch to
+-        WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+-        for (dom::Touch* touch : touchEvent->mTouches) {
+-          if (!touch) {
+-            break;
+-          }
+-
+-          RefPtr<dom::Touch> oldTouch =
+-            TouchManager::GetCapturedTouch(touch->Identifier());
+-          if (!oldTouch) {
+-            break;
+-          }
+-
+-          nsCOMPtr<nsIContent> content =
+-            do_QueryInterface(oldTouch->GetTarget());
+-          if (!content) {
+-            break;
+-          }
+-
+-          nsIFrame* contentFrame = content->GetPrimaryFrame();
+-          if (!contentFrame) {
+-            break;
+-          }
+-
+-          shell = static_cast<PresShell*>(contentFrame->PresShell());
+-          if (shell) {
+-            break;
+-          }
+-        }
+-        break;
+-      }
+-    default:
+-      break;
++    if (aEvent->mClass == eTouchEventClass) {
++      if (PresShell* newShell = GetShellForTouchEvent(aEvent)) {
++        shell = newShell;
++      }
+     }
+ 
+     // Check if we have an active EventStateManager which isn't the
+     // EventStateManager of the current PresContext.
+     // If that is the case, and mouse is over some ancestor document,
+     // forward event handling to the active document.
+     // This way content can get mouse events even when
+     // mouse is over the chrome or outside the window.
+diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h
+--- a/layout/base/PresShell.h
++++ b/layout/base/PresShell.h
+@@ -408,16 +408,18 @@ public:
+                                            uint32_t aSheetType) override;
+ 
+   virtual bool HasHandledUserInput() const override {
+     return mHasHandledUserInput;
+   }
+ 
+   virtual void FireResizeEvent() override;
+ 
++  static PresShell* GetShellForTouchEvent(WidgetGUIEvent* aEvent);
++
+ protected:
+   virtual ~PresShell();
+ 
+   void HandlePostedReflowCallbacks(bool aInterruptible);
+   void CancelPostedReflowCallbacks();
+ 
+   void ScheduleBeforeFirstPaint();
+   void UnsuppressAndInvalidate();

+ 452 - 0
mozilla-release/patches/1420589-6-59a1.patch

@@ -0,0 +1,452 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1512785178 -28800
+#      Sat Dec 09 10:06:18 2017 +0800
+# Node ID 62f76b10e91f4fcda8dc8a5288160a372ddd6855
+# Parent  7a568d63c332deb3988fe058ced0fd6f62b3ed81
+Bug 1420589 Part6: Keep those touch points that are not in the same document so that we can use them to dispatch pointer events.
+
+MozReview-Commit-ID: JlXHDwpbcEO
+
+diff --git a/dom/events/Touch.cpp b/dom/events/Touch.cpp
+--- a/dom/events/Touch.cpp
++++ b/dom/events/Touch.cpp
+@@ -45,16 +45,17 @@ Touch::Touch(EventTarget* aTarget,
+              int32_t aScreenX,
+              int32_t aScreenY,
+              int32_t aClientX,
+              int32_t aClientY,
+              int32_t aRadiusX,
+              int32_t aRadiusY,
+              float aRotationAngle,
+              float aForce)
++  : mIsTouchEventSuppressed(false)
+ {
+   mTarget = aTarget;
+   mIdentifier = aIdentifier;
+   mPagePoint = CSSIntPoint(aPageX, aPageY);
+   mScreenPoint = CSSIntPoint(aScreenX, aScreenY);
+   mClientPoint = CSSIntPoint(aClientX, aClientY);
+   mRefPoint = LayoutDeviceIntPoint(0, 0);
+   mPointsInitialized = true;
+@@ -68,16 +69,17 @@ Touch::Touch(EventTarget* aTarget,
+   nsJSContext::LikelyShortLivingObjectCreated();
+ }
+ 
+ Touch::Touch(int32_t aIdentifier,
+              LayoutDeviceIntPoint aPoint,
+              LayoutDeviceIntPoint aRadius,
+              float aRotationAngle,
+              float aForce)
++  : mIsTouchEventSuppressed(false)
+ {
+   mIdentifier = aIdentifier;
+   mPagePoint = CSSIntPoint(0, 0);
+   mScreenPoint = CSSIntPoint(0, 0);
+   mClientPoint = CSSIntPoint(0, 0);
+   mRefPoint = aPoint;
+   mPointsInitialized = false;
+   mRadius = aRadius;
+@@ -88,16 +90,17 @@ Touch::Touch(int32_t aIdentifier,
+   mMessage = 0;
+   nsJSContext::LikelyShortLivingObjectCreated();
+ }
+ 
+ Touch::Touch(const Touch& aOther)
+   : mTarget(aOther.mTarget)
+   , mRefPoint(aOther.mRefPoint)
+   , mChanged(aOther.mChanged)
++  , mIsTouchEventSuppressed(aOther.mIsTouchEventSuppressed)
+   , mMessage(aOther.mMessage)
+   , mIdentifier(aOther.mIdentifier)
+   , mPagePoint(aOther.mPagePoint)
+   , mClientPoint(aOther.mClientPoint)
+   , mScreenPoint(aOther.mScreenPoint)
+   , mRadius(aOther.mRadius)
+   , mRotationAngle(aOther.mRotationAngle)
+   , mForce(aOther.mForce)
+diff --git a/dom/events/Touch.h b/dom/events/Touch.h
+--- a/dom/events/Touch.h
++++ b/dom/events/Touch.h
+@@ -77,16 +77,22 @@ public:
+   int32_t RadiusX(CallerType aCallerType) const;
+   int32_t RadiusY(CallerType aCallerType) const;
+   float RotationAngle(CallerType aCallerType) const;
+   float Force(CallerType aCallerType) const;
+ 
+   nsCOMPtr<EventTarget> mTarget;
+   LayoutDeviceIntPoint mRefPoint;
+   bool mChanged;
++
++  // Is this touch instance being suppressed to dispatch touch event to content.
++  // We can't remove touch instance from WidgetTouchEvent::mTouches because we
++  // still need it when dispatching pointer events.
++  bool mIsTouchEventSuppressed;
++
+   uint32_t mMessage;
+   int32_t mIdentifier;
+   CSSIntPoint mPagePoint;
+   CSSIntPoint mClientPoint;
+   CSSIntPoint mScreenPoint;
+   LayoutDeviceIntPoint mRadius;
+   float mRotationAngle;
+   float mForce;
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -7179,16 +7179,21 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+     // Get the frame at the event point. However, don't do this if we're
+     // capturing and retargeting the event because the captured frame will
+     // be used instead below. Also keep using the root frame if we're dealing
+     // with a window-level mouse exit event since we want to start sending
+     // mouse out events at the root EventStateManager.
+     if (!captureRetarget && !isWindowLevelMouseExit) {
+       if (aEvent->mClass == eTouchEventClass) {
+         frame = TouchManager::SetupTarget(aEvent->AsTouchEvent(), frame);
++        if (nsIFrame* newFrame =
++            TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
++              aEvent->AsTouchEvent())) {
++          frame = newFrame;
++        }
+       } else {
+         uint32_t flags = 0;
+         nsPoint eventPoint =
+           nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
+ 
+         if (mouseEvent && mouseEvent->mClass == eMouseEventClass &&
+             mouseEvent->mIgnoreRootScrollFrame) {
+           flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
+@@ -7939,16 +7944,20 @@ PresShell::DispatchTouchEventToDOM(Widge
+                     (aEvent->mMessage == eTouchMove && aTouchIsNew) ||
+                     (aEvent->mMessage == eTouchEnd);
+   bool preventDefault = false;
+   nsEventStatus tmpStatus = nsEventStatus_eIgnore;
+   WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+ 
+   // loop over all touches and dispatch events on any that have changed
+   for (dom::Touch* touch : touchEvent->mTouches) {
++    // We should remove all suppressed touch instances in
++    // TouchManager::PreHandleEvent.
++    MOZ_ASSERT(!touch->mIsTouchEventSuppressed);
++
+     if (!touch || !touch->mChanged) {
+       continue;
+     }
+ 
+     nsCOMPtr<EventTarget> targetPtr = touch->mTarget;
+     nsCOMPtr<nsIContent> content = do_QueryInterface(targetPtr);
+     if (!content) {
+       continue;
+diff --git a/layout/base/TouchManager.cpp b/layout/base/TouchManager.cpp
+--- a/layout/base/TouchManager.cpp
++++ b/layout/base/TouchManager.cpp
+@@ -124,91 +124,116 @@ TouchManager::SetupTarget(WidgetTouchEve
+ 
+   uint32_t flags = 0;
+   // Setting this flag will skip the scrollbars on the root frame from
+   // participating in hit-testing, and we only want that to happen on
+   // zoomable platforms (for now).
+   if (gfxPrefs::APZAllowZooming()) {
+     flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
+   }
++
++  nsIFrame* target = aFrame;
++  for (int32_t i = aEvent->mTouches.Length(); i; ) {
++    --i;
++    dom::Touch* touch = aEvent->mTouches[i];
++
++    int32_t id = touch->Identifier();
++    if (!TouchManager::HasCapturedTouch(id)) {
++      // find the target for this touch
++      nsPoint eventPoint =
++        nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, touch->mRefPoint,
++                                                     aFrame);
++      target = FindFrameTargetedByInputEvent(aEvent, aFrame, eventPoint, flags);
++      if (target) {
++        nsCOMPtr<nsIContent> targetContent;
++        target->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
++        while (targetContent && !targetContent->IsElement()) {
++          targetContent = targetContent->GetParent();
++        }
++        touch->SetTarget(targetContent);
++      } else {
++        aEvent->mTouches.RemoveElementAt(i);
++      }
++    } else {
++      // This touch is an old touch, we need to ensure that is not
++      // marked as changed and set its target correctly
++      touch->mChanged = false;
++      RefPtr<dom::Touch> oldTouch = TouchManager::GetCapturedTouch(id);
++      if (oldTouch) {
++        touch->SetTarget(oldTouch->mTarget);
++      }
++    }
++  }
++  return target;
++}
++
++/* static */ nsIFrame*
++TouchManager::SuppressInvalidPointsAndGetTargetedFrame(WidgetTouchEvent* aEvent)
++{
++  MOZ_ASSERT(aEvent);
++
++  if (!aEvent || aEvent->mMessage != eTouchStart) {
++    // All touch events except for touchstart use a captured target.
++    return nullptr;
++  }
++
+   // if this is a continuing session, ensure that all these events are
+   // in the same document by taking the target of the events already in
+   // the capture list
+   nsCOMPtr<nsIContent> anyTarget;
+   if (aEvent->mTouches.Length() > 1) {
+     anyTarget = TouchManager::GetAnyCapturedTouchTarget();
+   }
+ 
+-  nsPoint eventPoint;
+-  nsIFrame* frame = aFrame;
++  nsIFrame* frame = nullptr;
+   for (int32_t i = aEvent->mTouches.Length(); i; ) {
+     --i;
+     dom::Touch* touch = aEvent->mTouches[i];
++    if (TouchManager::HasCapturedTouch(touch->Identifier())) {
++      continue;
++    }
+ 
+-    int32_t id = touch->Identifier();
+-    if (!TouchManager::HasCapturedTouch(id)) {
+-      // find the target for this touch
+-      eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
+-                                                        touch->mRefPoint,
+-                                                        aFrame);
+-      nsIFrame* target = FindFrameTargetedByInputEvent(aEvent,
+-                                                       aFrame,
+-                                                       eventPoint,
+-                                                       flags);
+-      if (target && !anyTarget) {
+-        target->GetContentForEvent(aEvent, getter_AddRefs(anyTarget));
+-        while (anyTarget && !anyTarget->IsElement()) {
+-          anyTarget = anyTarget->GetParent();
+-        }
+-        touch->SetTarget(anyTarget);
+-      } else {
+-        nsIFrame* newTargetFrame = nullptr;
+-        for (nsIFrame* f = target; f;
+-             f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
+-          if (f->PresContext()->Document() == anyTarget->OwnerDoc()) {
+-            newTargetFrame = f;
+-            break;
+-          }
+-          // We must be in a subdocument so jump directly to the root frame.
+-          // GetParentOrPlaceholderForCrossDoc gets called immediately to
+-          // jump up to the containing document.
+-          f = f->PresShell()->GetRootFrame();
++    MOZ_ASSERT(touch->mTarget);
++    nsCOMPtr<nsIContent> targetContent = do_QueryInterface(touch->GetTarget());
++    nsIFrame* targetFrame = targetContent->GetPrimaryFrame();
++    if (targetFrame && !anyTarget) {
++      anyTarget = targetContent;
++    } else {
++      nsIFrame* newTargetFrame = nullptr;
++      for (nsIFrame* f = targetFrame; f;
++           f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
++        if (f->PresContext()->Document() == anyTarget->OwnerDoc()) {
++          newTargetFrame = f;
++          break;
+         }
+-
+-        // if we couldn't find a target frame in the same document as
+-        // anyTarget, remove the touch from the capture touch list, as
+-        // well as the event->mTouches array. touchmove events that aren't
+-        // in the captured touch list will be discarded
+-        if (!newTargetFrame) {
+-          aEvent->mTouches.RemoveElementAt(i);
+-        } else {
+-          target = newTargetFrame;
+-          nsCOMPtr<nsIContent> targetContent;
+-          target->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
+-          while (targetContent && !targetContent->IsElement()) {
+-            targetContent = targetContent->GetParent();
+-          }
+-          touch->SetTarget(targetContent);
+-        }
++        // We must be in a subdocument so jump directly to the root frame.
++        // GetParentOrPlaceholderForCrossDoc gets called immediately to
++        // jump up to the containing document.
++        f = f->PresShell()->GetRootFrame();
+       }
+-      if (target) {
+-        frame = target;
++      // if we couldn't find a target frame in the same document as
++      // anyTarget, remove the touch from the capture touch list, as
++      // well as the event->mTouches array. touchmove events that aren't
++      // in the captured touch list will be discarded
++      if (!newTargetFrame) {
++        touch->mIsTouchEventSuppressed = true;
++      } else {
++        targetFrame = newTargetFrame;
+       }
+-    } else {
+-      // This touch is an old touch, we need to ensure that is not
+-      // marked as changed and set its target correctly
+-      touch->mChanged = false;
+-
+-      RefPtr<dom::Touch> oldTouch = TouchManager::GetCapturedTouch(id);
+-      if (oldTouch) {
+-        touch->SetTarget(oldTouch->mTarget);
++      targetFrame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
++      while (targetContent && !targetContent->IsElement()) {
++        targetContent = targetContent->GetParent();
+       }
++      touch->SetTarget(targetContent);
++    }
++    if (targetFrame) {
++      frame = targetFrame;
+     }
+   }
+-  return FindFrameTargetedByInputEvent(aEvent, frame, eventPoint, flags);
++  return frame;
+ }
+ 
+ bool
+ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
+                              nsEventStatus* aStatus,
+                              bool& aTouchIsNew,
+                              bool& aIsHandlingUserInput,
+                              nsCOMPtr<nsIContent>& aCurrentEventContent)
+@@ -223,27 +248,35 @@ TouchManager::PreHandleEvent(WidgetEvent
+       if (touchEvent->mTouches.Length() == 1) {
+         WidgetTouchEvent::AutoTouchArray touches;
+         AppendToTouchList(&touches);
+         for (uint32_t i = 0; i < touches.Length(); ++i) {
+           EvictTouchPoint(touches[i]);
+         }
+       }
+       // Add any new touches to the queue
+-      for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
+-        Touch* touch = touchEvent->mTouches[i];
++      WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
++      for (int32_t i = touches.Length(); i; ) {
++        --i;
++        Touch* touch = touches[i];
+         int32_t id = touch->Identifier();
+         if (!sCaptureTouchList->Get(id, nullptr)) {
+           // If it is not already in the queue, it is a new touch
+           touch->mChanged = true;
+         }
+         touch->mMessage = aEvent->mMessage;
+         TouchInfo info = { touch, GetNonAnonymousAncestor(touch->mTarget),
+                            true };
+         sCaptureTouchList->Put(id, info);
++        if (touch->mIsTouchEventSuppressed) {
++          // We're going to dispatch touch event. Remove this touch instance if
++          // it is suppressed.
++          touches.RemoveElementAt(i);
++          continue;
++        }
+       }
+       break;
+     }
+     case eTouchMove: {
+       // Check for touches that changed. Mark them add to queue
+       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+       WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
+       bool haveChanged = false;
+@@ -281,16 +314,21 @@ TouchManager::PreHandleEvent(WidgetEvent
+         info.mTouch = touch;
+         // info.mNonAnonymousTarget is still valid from above
+         sCaptureTouchList->Put(id, info);
+         // if we're moving from touchstart to touchmove for this touch
+         // we allow preventDefault to prevent mouse events
+         if (oldTouch->mMessage != touch->mMessage) {
+           aTouchIsNew = true;
+         }
++        if (oldTouch->mIsTouchEventSuppressed) {
++          touch->mIsTouchEventSuppressed = true;
++          touches.RemoveElementAt(i);
++          continue;
++        }
+       }
+       // is nothing has changed, we should just return
+       if (!haveChanged) {
+         if (aTouchIsNew) {
+           // however, if this is the first touchmove after a touchstart,
+           // it is special in that preventDefault is allowed on it, so
+           // we must dispatch it to content even if nothing changed. we
+           // arbitrarily pick the first touch point to be the "changed"
+@@ -312,17 +350,18 @@ TouchManager::PreHandleEvent(WidgetEvent
+       aIsHandlingUserInput = true;
+       // Fall through to touchcancel code
+       MOZ_FALLTHROUGH;
+     case eTouchCancel: {
+       // Remove the changed touches
+       // need to make sure we only remove touches that are ending here
+       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+       WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
+-      for (uint32_t i = 0; i < touches.Length(); ++i) {
++      for (int32_t i = touches.Length(); i; ) {
++        --i;
+         Touch* touch = touches[i];
+         if (!touch) {
+           continue;
+         }
+         touch->mMessage = aEvent->mMessage;
+         touch->mChanged = true;
+ 
+         int32_t id = touch->Identifier();
+@@ -334,16 +373,20 @@ TouchManager::PreHandleEvent(WidgetEvent
+         nsCOMPtr<nsINode> targetNode(do_QueryInterface(targetPtr));
+         if (targetNode && !targetNode->IsInComposedDoc()) {
+           targetPtr = do_QueryInterface(info.mNonAnonymousTarget);
+         }
+ 
+         aCurrentEventContent = do_QueryInterface(targetPtr);
+         touch->SetTarget(targetPtr);
+         sCaptureTouchList->Remove(id);
++        if (info.mTouch->mIsTouchEventSuppressed) {
++          touches.RemoveElementAt(i);
++          continue;
++        }
+       }
+       // add any touches left in the touch list, but ensure changed=false
+       AppendToTouchList(&touches);
+       break;
+     }
+     case eTouchPointerCancel: {
+       // Don't generate pointer events by touch events after eTouchPointerCancel
+       // is received.
+diff --git a/layout/base/TouchManager.h b/layout/base/TouchManager.h
+--- a/layout/base/TouchManager.h
++++ b/layout/base/TouchManager.h
+@@ -30,16 +30,31 @@ public:
+ 
+   void Init(PresShell* aPresShell, nsIDocument* aDocument);
+   void Destroy();
+ 
+   // Perform hit test and setup the event targets for touchstart. Other touch
+   // events are dispatched to the same target as touchstart.
+   static nsIFrame* SetupTarget(WidgetTouchEvent* aEvent, nsIFrame* aFrame);
+ 
++  /**
++   * This function checks whether all touch points hit elements in the same
++   * document. If not, we try to find its cross document parent which is in the
++   * same document of the existing target as the event target. We mark the
++   * touch point as suppressed if can't find it. The suppressed touch points are
++   * removed in TouchManager::PreHandleEvent so that we don't dispatch them to
++   * content.
++   *
++   * @param aEvent    A touch event to be checked.
++   *
++   * @return          The targeted frame of aEvent.
++   */
++  static nsIFrame* SuppressInvalidPointsAndGetTargetedFrame(
++    WidgetTouchEvent* aEvent);
++
+   bool PreHandleEvent(mozilla::WidgetEvent* aEvent,
+                       nsEventStatus* aStatus,
+                       bool& aTouchIsNew,
+                       bool& aIsHandlingUserInput,
+                       nsCOMPtr<nsIContent>& aCurrentEventContent);
+ 
+   static already_AddRefed<nsIContent> GetAnyCapturedTouchTarget();
+   static bool HasCapturedTouch(int32_t aId);

+ 717 - 0
mozilla-release/patches/1420589-7-59a1.patch

@@ -0,0 +1,717 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1511704447 -28800
+#      Sun Nov 26 21:54:07 2017 +0800
+# Node ID b8b3173ded005ff5a8fe747b4f9883497c800cb6
+# Parent  62f76b10e91f4fcda8dc8a5288160a372ddd6855
+Bug 1420589 Part7: Using mouse or touch event to do hit test and then generate pointer events with the same target. r=smaug.
+
+MozReview-Commit-ID: 1uM3MxurI9I
+
+diff --git a/dom/events/PointerEventHandler.cpp b/dom/events/PointerEventHandler.cpp
+--- a/dom/events/PointerEventHandler.cpp
++++ b/dom/events/PointerEventHandler.cpp
+@@ -190,16 +190,69 @@ PointerEventHandler::GetPointerInfo(uint
+   if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
+     aActiveState = pointerInfo->mActiveState;
+     return true;
+   }
+   return false;
+ }
+ 
+ /* static */ void
++PointerEventHandler::MaybeProcessPointerCapture(WidgetGUIEvent* aEvent)
++{
++  switch (aEvent->mClass) {
++  case eMouseEventClass:
++    ProcessPointerCaptureForMouse(aEvent->AsMouseEvent());
++    break;
++  case eTouchEventClass:
++    ProcessPointerCaptureForTouch(aEvent->AsTouchEvent());
++    break;
++  default:
++    break;
++  }
++}
++
++/* static */ void
++PointerEventHandler::ProcessPointerCaptureForMouse(WidgetMouseEvent* aEvent)
++{
++  if (!ShouldGeneratePointerEventFromMouse(aEvent)) {
++    return;
++  }
++
++  PointerCaptureInfo* info = GetPointerCaptureInfo(aEvent->pointerId);
++  if (!info || info->mPendingContent == info->mOverrideContent) {
++    return;
++  }
++  WidgetPointerEvent localEvent(*aEvent);
++  InitPointerEventFromMouse(&localEvent, aEvent, eVoidEvent);
++  CheckPointerCaptureState(&localEvent);
++}
++
++/* static */ void
++PointerEventHandler::ProcessPointerCaptureForTouch(WidgetTouchEvent* aEvent)
++{
++  if (!ShouldGeneratePointerEventFromTouch(aEvent)) {
++    return;
++  }
++
++  for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
++    Touch* touch = aEvent->mTouches[i];
++    if (!TouchManager::ShouldConvertTouchToPointer(touch, aEvent)) {
++      continue;
++    }
++    PointerCaptureInfo* info = GetPointerCaptureInfo(touch->Identifier());
++    if (!info || info->mPendingContent == info->mOverrideContent) {
++      continue;
++    }
++    WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget);
++    InitPointerEventFromTouch(&event, aEvent, touch, i == 0);
++    CheckPointerCaptureState(&event);
++  }
++}
++
++/* static */ void
+ PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent)
+ {
+   // Handle pending pointer capture before any pointer events except
+   // gotpointercapture / lostpointercapture.
+   if (!aEvent) {
+     return;
+   }
+   MOZ_ASSERT(IsPointerEventEnabled());
+@@ -430,23 +483,24 @@ PointerEventHandler::InitPointerEventFro
+   aPointerEvent->buttons = buttons;
+   aPointerEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+ }
+ 
+ /* static */ void
+ PointerEventHandler::DispatchPointerFromMouseOrTouch(
+                        PresShell* aShell,
+                        nsIFrame* aFrame,
++                       nsIContent* aContent,
+                        WidgetGUIEvent* aEvent,
+                        bool aDontRetargetEvents,
+                        nsEventStatus* aStatus,
+                        nsIContent** aTargetContent)
+ {
+   MOZ_ASSERT(IsPointerEventEnabled());
+-  MOZ_ASSERT(aFrame);
++  MOZ_ASSERT(aFrame || aContent);
+   MOZ_ASSERT(aEvent);
+ 
+   EventMessage pointerMessage = eVoidEvent;
+   if (aEvent->mClass == eMouseEventClass) {
+     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+     // 1. If it is not mouse then it is likely will come as touch event
+     // 2. We don't synthesize pointer events for those events that are not
+     //    dispatched to DOM.
+@@ -470,20 +524,28 @@ PointerEventHandler::DispatchPointerFrom
+       break;
+     default:
+       return;
+     }
+ 
+     WidgetPointerEvent event(*mouseEvent);
+     InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
+     event.convertToPointer = mouseEvent->convertToPointer = false;
++    RefPtr<PresShell> shell(aShell);
++    if (!aFrame) {
++      shell = PresShell::GetShellForEventTarget(nullptr, aContent);
++      if (!shell) {
++        return;
++      }
++    }
+     PreHandlePointerEventsPreventDefault(&event, aEvent);
+-    RefPtr<PresShell> shell(aShell);
+-    shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
+-                       aTargetContent);
++    // Dispatch pointer event to the same target which is found by the
++    // corresponding mouse event.
++    shell->HandleEventWithTarget(&event, aFrame, aContent, aStatus, true,
++                                 aTargetContent);
+     PostHandlePointerEventsPreventDefault(&event, aEvent);
+   } else if (aEvent->mClass == eTouchEventClass) {
+     WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+     // loop over all touches and dispatch pointer events on each touch
+     // copy the event
+     switch (touchEvent->mMessage) {
+     case eTouchMove:
+       pointerMessage = ePointerMove;
+@@ -509,20 +571,44 @@ PointerEventHandler::DispatchPointerFrom
+         continue;
+       }
+ 
+       WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
+                                touchEvent->mWidget);
+ 
+       InitPointerEventFromTouch(&event, touchEvent, touch, i == 0);
+       event.convertToPointer = touch->convertToPointer = false;
+-      PreHandlePointerEventsPreventDefault(&event, aEvent);
+-      shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
+-                         aTargetContent);
+-      PostHandlePointerEventsPreventDefault(&event, aEvent);
++      if (aEvent->mMessage == eTouchStart) {
++        // We already did hit test for touchstart in PresShell. We should
++        // dispatch pointerdown to the same target as touchstart.
++        nsCOMPtr<nsIContent> content = do_QueryInterface(touch->mTarget);
++        if (!content) {
++          continue;
++        }
++
++        nsIFrame* frame = content->GetPrimaryFrame();
++        shell = PresShell::GetShellForEventTarget(frame, content);
++        if (!shell) {
++          continue;
++        }
++
++        PreHandlePointerEventsPreventDefault(&event, aEvent);
++        shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
++                                     aTargetContent);
++        PostHandlePointerEventsPreventDefault(&event, aEvent);
++      } else {
++        // We didn't hit test for other touch events. Spec doesn't mention that
++        // all pointer events should be dispatched to the same target as their
++        // corresponding touch events. Call PresShell::HandleEvent so that we do
++        // hit test for pointer events.
++        PreHandlePointerEventsPreventDefault(&event, aEvent);
++        shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
++                           aTargetContent);
++        PostHandlePointerEventsPreventDefault(&event, aEvent);
++      }
+     }
+   }
+ }
+ 
+ /* static */ uint16_t
+ PointerEventHandler::GetPointerType(uint32_t aPointerId)
+ {
+   PointerInfo* pointerInfo = nullptr;
+diff --git a/dom/events/PointerEventHandler.h b/dom/events/PointerEventHandler.h
+--- a/dom/events/PointerEventHandler.h
++++ b/dom/events/PointerEventHandler.h
+@@ -3,16 +3,18 @@
+ /* 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/. */
+ 
+ #ifndef mozilla_PointerEventHandler_h
+ #define mozilla_PointerEventHandler_h
+ 
+ #include "mozilla/EventForwards.h"
++#include "mozilla/MouseEvents.h"
++#include "mozilla/TouchEvents.h"
+ 
+ class nsIFrame;
+ class nsIContent;
+ 
+ namespace mozilla {
+ 
+ class PresShell;
+ 
+@@ -72,16 +74,19 @@ public:
+   // GetPointerInfo returns true if pointer with aPointerId is situated in
+   // device, false otherwise.
+   // aActiveState is additional information, which shows state of pointer like
+   // button state for mouse.
+   static bool GetPointerInfo(uint32_t aPointerId, bool& aActiveState);
+ 
+   // CheckPointerCaptureState checks cases, when got/lostpointercapture events
+   // should be fired.
++  static void MaybeProcessPointerCapture(WidgetGUIEvent* aEvent);
++  static void ProcessPointerCaptureForMouse(WidgetMouseEvent* aEvent);
++  static void ProcessPointerCaptureForTouch(WidgetTouchEvent* aEvent);
+   static void CheckPointerCaptureState(WidgetPointerEvent* aEvent);
+ 
+   // Implicitly get and release capture of current pointer for touch.
+   static void ImplicitlyCapturePointer(nsIFrame* aFrame, WidgetEvent* aEvent);
+   static void ImplicitlyReleasePointerCapture(WidgetEvent* aEvent);
+ 
+   /**
+    * GetPointerCapturingFrame returns a target frame of aEvent. If the event is
+@@ -131,30 +136,44 @@ public:
+    * content preventDefault on pointerdown
+    */
+   static void PostHandlePointerEventsPreventDefault(
+                 WidgetPointerEvent* aPointerEvent,
+                 WidgetGUIEvent* aMouseOrTouchEvent);
+ 
+   static void DispatchPointerFromMouseOrTouch(PresShell* aShell,
+                                               nsIFrame* aFrame,
++                                              nsIContent* aContent,
+                                               WidgetGUIEvent* aEvent,
+                                               bool aDontRetargetEvents,
+                                               nsEventStatus* aStatus,
+                                               nsIContent** aTargetContent);
+ 
+   static void InitPointerEventFromMouse(WidgetPointerEvent* aPointerEvent,
+                                         WidgetMouseEvent* aMouseEvent,
+                                         EventMessage aMessage);
+ 
+   static void InitPointerEventFromTouch(WidgetPointerEvent* aPointerEvent,
+                                         WidgetTouchEvent* aTouchEvent,
+                                         mozilla::dom::Touch* aTouch,
+                                         bool aIsPrimary);
+ 
++  static bool ShouldGeneratePointerEventFromMouse(WidgetGUIEvent* aEvent)
++  {
++    return aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp ||
++           aEvent->mMessage == eMouseMove;
++  }
++
++  static bool ShouldGeneratePointerEventFromTouch(WidgetGUIEvent* aEvent)
++  {
++    return aEvent->mMessage == eTouchStart || aEvent->mMessage == eTouchMove ||
++           aEvent->mMessage == eTouchEnd || aEvent->mMessage == eTouchCancel ||
++           aEvent->mMessage == eTouchPointerCancel;
++  }
++
+ private:
+   // GetPointerType returns pointer type like mouse, pen or touch for pointer
+   // event with pointerId. The return value must be one of
+   // nsIDOMMouseEvent::MOZ_SOURCE_*
+   static uint16_t GetPointerType(uint32_t aPointerId);
+ 
+   // GetPointerPrimaryState returns state of attribute isPrimary for pointer
+   // event with pointerId
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -547,26 +547,26 @@ private:
+ 
+ // This is a helper class to track whether the targeted frame is destroyed after
+ // dispatching pointer events. In that case, we need the original targeted
+ // content so that we can dispatch the mouse events to it.
+ class MOZ_STACK_CLASS AutoPointerEventTargetUpdater final
+ {
+ public:
+   AutoPointerEventTargetUpdater(PresShell* aShell,
+-                                WidgetGUIEvent* aEvent,
++                                WidgetEvent* aEvent,
+                                 nsIFrame* aFrame,
+                                 nsIContent** aTargetContent)
+   {
+-    MOZ_ASSERT(aShell);
+     MOZ_ASSERT(aEvent);
+-    MOZ_ASSERT(aFrame);
+     if (!aTargetContent || aEvent->mClass != ePointerEventClass) {
+       return;
+     }
++    MOZ_ASSERT(aShell);
++    MOZ_ASSERT(aFrame);
+     MOZ_ASSERT(!aFrame->GetContent() ||
+                aShell->GetDocument() == aFrame->GetContent()->OwnerDoc());
+ 
+     MOZ_ASSERT(PointerEventHandler::IsPointerEventEnabled());
+     mShell = aShell;
+     mWeakFrame = aFrame;
+     mTargetContent = aTargetContent;
+     aShell->mPointerEventTarget = aFrame->GetContent();
+@@ -6826,16 +6826,32 @@ PresShell::CanDispatchEvent(const Widget
+     mPresContext && !mHaveShutDown && nsContentUtils::IsSafeToRunScript();
+   if (aEvent) {
+     rv &= (aEvent && aEvent->mWidget && !aEvent->mWidget->Destroyed());
+   }
+   return rv;
+ }
+ 
+ /* static */ PresShell*
++PresShell::GetShellForEventTarget(nsIFrame* aFrame, nsIContent* aContent)
++{
++  if (aFrame) {
++    return static_cast<PresShell*>(aFrame->PresShell());
++  }
++  if (aContent) {
++    nsIDocument* doc = aContent->GetComposedDoc();
++    if (!doc) {
++      return nullptr;
++    }
++    return static_cast<PresShell*>(doc->GetShell());
++  }
++  return nullptr;
++}
++
++/* static */ PresShell*
+ PresShell::GetShellForTouchEvent(WidgetGUIEvent* aEvent)
+ {
+   PresShell* shell = nullptr;
+   switch (aEvent->mMessage) {
+   case eTouchMove:
+   case eTouchCancel:
+   case eTouchEnd: {
+     // get the correct shell to dispatch to
+@@ -6904,37 +6920,16 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+   // Update the latest focus sequence number with this new sequence number
+   if (mAPZFocusSequenceNumber < aEvent->mFocusSequenceNumber) {
+     mAPZFocusSequenceNumber = aEvent->mFocusSequenceNumber;
+ 
+     // Schedule an empty transaction to transmit this focus update
+     aFrame->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY, false);
+   }
+ 
+-  if (PointerEventHandler::IsPointerEventEnabled()) {
+-    AutoWeakFrame weakFrame(aFrame);
+-    nsCOMPtr<nsIContent> targetContent;
+-    PointerEventHandler::DispatchPointerFromMouseOrTouch(
+-                           this, aFrame, aEvent, aDontRetargetEvents,
+-                           aEventStatus, getter_AddRefs(targetContent));
+-    if (!weakFrame.IsAlive()) {
+-      if (targetContent) {
+-        aFrame = targetContent->GetPrimaryFrame();
+-        if (!aFrame) {
+-          PushCurrentEventInfo(aFrame, targetContent);
+-          nsresult rv = HandleEventInternal(aEvent, aEventStatus, true);
+-          PopCurrentEventInfo();
+-          return rv;
+-        }
+-      } else {
+-        return NS_OK;
+-      }
+-    }
+-  }
+-
+   if (mIsDestroying ||
+       (sDisableNonTestMouseEvents && !aEvent->mFlags.mIsSynthesizedForTests &&
+        aEvent->HasMouseEventMessage())) {
+     return NS_OK;
+   }
+ 
+   RecordMouseLocation(aEvent);
+ 
+@@ -7167,33 +7162,56 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+         }
+       }
+       else {
+         ClearMouseCapture(nullptr);
+         capturingContent = nullptr;
+       }
+     }
+ 
++    // The order to generate pointer event is
++    // 1. check pending pointer capture.
++    // 2. check if there is a capturing content.
++    // 3. hit test
++    // 4. dispatch pointer events
++    // 5. check whether the targets of all Touch instances are in the same
++    //    document and suppress invalid instances.
++    // 6. dispatch mouse or touch events.
++
++    // Try to keep frame for following check, because frame can be damaged
++    // during MaybeProcessPointerCapture.
++    {
++      AutoWeakFrame frameKeeper(frame);
++      PointerEventHandler::MaybeProcessPointerCapture(aEvent);
++      // Prevent application crashes, in case damaged frame.
++      if (!frameKeeper.IsAlive()) {
++        NS_WARNING("Nothing to handle this event!");
++        return NS_OK;
++      }
++    }
++
++    nsIFrame* pointerCapturingFrame =
++      PointerEventHandler::GetPointerCapturingFrame(aEvent);
++
++    if (pointerCapturingFrame) {
++      frame = pointerCapturingFrame;
++    }
++
+     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+     bool isWindowLevelMouseExit = (aEvent->mMessage == eMouseExitFromWidget) &&
+       (mouseEvent && mouseEvent->mExitFrom == WidgetMouseEvent::eTopLevel);
+ 
+     // Get the frame at the event point. However, don't do this if we're
+     // capturing and retargeting the event because the captured frame will
+     // be used instead below. Also keep using the root frame if we're dealing
+     // with a window-level mouse exit event since we want to start sending
+     // mouse out events at the root EventStateManager.
+-    if (!captureRetarget && !isWindowLevelMouseExit) {
++    if (!captureRetarget && !isWindowLevelMouseExit && !pointerCapturingFrame) {
+       if (aEvent->mClass == eTouchEventClass) {
+         frame = TouchManager::SetupTarget(aEvent->AsTouchEvent(), frame);
+-        if (nsIFrame* newFrame =
+-            TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
+-              aEvent->AsTouchEvent())) {
+-          frame = newFrame;
+-        }
+       } else {
+         uint32_t flags = 0;
+         nsPoint eventPoint =
+           nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
+ 
+         if (mouseEvent && mouseEvent->mClass == eMouseEventClass &&
+             mouseEvent->mIgnoreRootScrollFrame) {
+           flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
+@@ -7205,48 +7223,30 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+         }
+       }
+     }
+ 
+     // if a node is capturing the mouse, check if the event needs to be
+     // retargeted at the capturing content instead. This will be the case when
+     // capture retargeting is being used, no frame was found or the frame's
+     // content is not a descendant of the capturing content.
+-    if (capturingContent &&
++    if (capturingContent && !pointerCapturingFrame &&
+         (gCaptureInfo.mRetargetToElement || !frame->GetContent() ||
+          !nsContentUtils::ContentIsCrossDocDescendantOf(frame->GetContent(),
+                                                         capturingContent))) {
+       // A check was already done above to ensure that capturingContent is
+       // in this presshell.
+       NS_ASSERTION(capturingContent->GetComposedDoc() == GetDocument(),
+                    "Unexpected document");
+       nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
+       if (capturingFrame) {
+         frame = capturingFrame;
+       }
+     }
+ 
+-    // Try to keep frame for following check, because frame can be damaged
+-    // during CheckPointerCaptureState.
+-    {
+-      AutoWeakFrame frameKeeper(frame);
+-      PointerEventHandler::CheckPointerCaptureState(aEvent->AsPointerEvent());
+-      // Prevent application crashes, in case damaged frame.
+-      if (!frameKeeper.IsAlive()) {
+-        frame = nullptr;
+-      }
+-    }
+-
+-    nsIFrame* pointerCapturingFrame =
+-      PointerEventHandler::GetPointerCapturingFrame(aEvent);
+-
+-    if (pointerCapturingFrame) {
+-      frame = pointerCapturingFrame;
+-    }
+-
+     // Suppress mouse event if it's being targeted at an element inside
+     // a document which needs events suppressed
+     if (aEvent->mClass == eMouseEventClass &&
+         frame->PresContext()->Document()->EventHandlingSuppressed()) {
+       if (aEvent->mMessage == eMouseDown) {
+         mNoDelayedMouseEvents = true;
+       } else if (!mNoDelayedMouseEvents && (aEvent->mMessage == eMouseUp ||
+         // contextmenu is triggered after right mouseup on Windows and right
+@@ -7261,22 +7261,16 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+     }
+ 
+     if (!frame) {
+       NS_WARNING("Nothing to handle this event!");
+       return NS_OK;
+     }
+ 
+     PresShell* shell = static_cast<PresShell*>(frame->PresShell());
+-    if (aEvent->mClass == eTouchEventClass) {
+-      if (PresShell* newShell = GetShellForTouchEvent(aEvent)) {
+-        shell = newShell;
+-      }
+-    }
+-
+     // Check if we have an active EventStateManager which isn't the
+     // EventStateManager of the current PresContext.
+     // If that is the case, and mouse is over some ancestor document,
+     // forward event handling to the active document.
+     // This way content can get mouse events even when
+     // mouse is over the chrome or outside the window.
+     //
+     // Note, currently for backwards compatibility we don't forward mouse events
+@@ -7322,21 +7316,75 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+       }
+ 
+       // If we found an element, target it.  Otherwise, target *nothing*.
+       if (!targetElement) {
+         return NS_OK;
+       }
+     }
+ 
+-    // Prevent deletion until we're done with event handling (bug 336582) and
+-    // swap mPointerEventTarget to *aTargetContent
++    if (PointerEventHandler::IsPointerEventEnabled()) {
++      // Dispatch pointer events from the mouse or touch events. Regarding
++      // pointer events from mouse, we should dispatch those pointer events to
++      // the same target as the source mouse events. We pass the frame found
++      // in hit test to PointerEventHandler and dispatch pointer events to it.
++      //
++      // Regarding pointer events from touch, the behavior is different. Touch
++      // events are dispatched to the same target as the target of touchstart.
++      // Multiple touch points must be dispatched to the same document. Pointer
++      // events from touch can be dispatched to different documents. We Pass the
++      // original frame to PointerEventHandler, reentry PresShell::HandleEvent,
++      // and do hit test for each point.
++      nsIFrame* targetFrame =
++        aEvent->mClass == eTouchEventClass ? aFrame : frame;
++
++      AutoWeakFrame weakFrame(targetFrame);
++      nsCOMPtr<nsIContent> targetContent;
++      PointerEventHandler::DispatchPointerFromMouseOrTouch(
++                             shell, targetFrame, targetElement, aEvent,
++                             aDontRetargetEvents, aEventStatus,
++                             getter_AddRefs(targetContent));
++
++      if (!weakFrame.IsAlive() && aEvent->mClass == eMouseEventClass) {
++        // Spec only defines that mouse events must be dispatched to the same
++        // target as the pointer event. If the target is no longer participating
++        // in its ownerDocument's tree, fire the event at the original target's
++        // nearest ancestor node
++        if (!targetContent) {
++          return NS_OK;
++        }
++        frame = targetContent->GetPrimaryFrame();
++        shell = GetShellForEventTarget(frame, targetContent);
++        if (!shell) {
++          return NS_OK;
++        }
++      }
++    }
++
++    // frame could be null after dispatching pointer events.
++    if (aEvent->mClass == eTouchEventClass) {
++      if (aEvent->mMessage == eTouchStart) {
++        WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
++        if (nsIFrame* newFrame =
++              TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
++                touchEvent)) {
++          frame = newFrame;
++          frame->GetContentForEvent(aEvent, getter_AddRefs(targetElement));
++          shell = static_cast<PresShell*>(frame->PresContext()->PresShell());
++        }
++      } else if (PresShell* newShell = GetShellForTouchEvent(aEvent)) {
++        // Touch events (except touchstart) are dispatching to the captured
++        // element. Get correct shell from it.
++        shell = newShell;
++      }
++    }
++
++    // Prevent deletion until we're done with event handling (bug 336582)
+     nsCOMPtr<nsIPresShell> kungFuDeathGrip(shell);
+     nsresult rv;
+-    AutoPointerEventTargetUpdater updater(shell, aEvent, frame, aTargetContent);
+ 
+     // Handle the event in the correct shell.
+     // We pass the subshell's root frame as the frame to start from. This is
+     // the only correct alternative; if the event was captured then it
+     // must have been captured by us or some ancestor shell and we
+     // now ask the subshell to dispatch it normally.
+     shell->PushCurrentEventInfo(frame, targetElement);
+     rv = shell->HandleEventInternal(aEvent, aEventStatus, true);
+@@ -7489,30 +7537,32 @@ PresShell::ShowEventTargetDebug()
+     mDrawEventTargetFrame = mCurrentEventFrame;
+     mDrawEventTargetFrame->InvalidateFrame();
+   }
+ }
+ #endif
+ 
+ nsresult
+ PresShell::HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame,
+-                                 nsIContent* aContent, nsEventStatus* aStatus)
++                                 nsIContent* aContent, nsEventStatus* aStatus,
++                                 bool aIsHandlingNativeEvent,
++                                 nsIContent** aTargetContent)
+ {
+ #if DEBUG
+   MOZ_ASSERT(!aFrame || aFrame->PresContext()->GetPresShell() == this,
+              "wrong shell");
+   if (aContent) {
+     nsIDocument* doc = aContent->GetComposedDoc();
+     NS_ASSERTION(doc, "event for content that isn't in a document");
+     // NOTE: We don't require that the document still have a PresShell.
+     // See bug 1375940.
+   }
+ #endif
+   NS_ENSURE_STATE(!aContent || aContent->GetComposedDoc() == mDocument);
+-
++  AutoPointerEventTargetUpdater updater(this, aEvent, aFrame, aTargetContent);
+   PushCurrentEventInfo(aFrame, aContent);
+   nsresult rv = HandleEventInternal(aEvent, aStatus, false);
+   PopCurrentEventInfo();
+   return rv;
+ }
+ 
+ nsresult
+ PresShell::HandleEventInternal(WidgetEvent* aEvent,
+diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h
+--- a/layout/base/PresShell.h
++++ b/layout/base/PresShell.h
+@@ -172,17 +172,19 @@ public:
+ 
+   virtual nsresult AddOverrideStyleSheet(mozilla::StyleSheet* aSheet) override;
+   virtual nsresult RemoveOverrideStyleSheet(mozilla::StyleSheet* aSheet) override;
+ 
+   virtual nsresult HandleEventWithTarget(
+                                  mozilla::WidgetEvent* aEvent,
+                                  nsIFrame* aFrame,
+                                  nsIContent* aContent,
+-                                 nsEventStatus* aStatus) override;
++                                 nsEventStatus* aStatus,
++                                 bool aIsHandlingNativeEvent = false,
++                                 nsIContent** aTargetContent = nullptr) override;
+   virtual nsIFrame* GetEventTargetFrame() override;
+   virtual already_AddRefed<nsIContent> GetEventTargetContent(
+                                                      mozilla::WidgetEvent* aEvent) override;
+ 
+   virtual void NotifyCounterStylesAreDirty() override;
+ 
+   virtual void ReconstructFrames(void) override;
+   virtual void Freeze() override;
+@@ -231,17 +233,17 @@ public:
+   //nsIViewObserver interface
+ 
+   virtual void Paint(nsView* aViewToPaint, const nsRegion& aDirtyRegion,
+                      uint32_t aFlags) override;
+   virtual nsresult HandleEvent(nsIFrame* aFrame,
+                                mozilla::WidgetGUIEvent* aEvent,
+                                bool aDontRetargetEvents,
+                                nsEventStatus* aEventStatus,
+-                               nsIContent** aTargetContent) override;
++                               nsIContent** aTargetContent = nullptr) override;
+   virtual nsresult HandleDOMEventWithTarget(
+                                  nsIContent* aTargetContent,
+                                  mozilla::WidgetEvent* aEvent,
+                                  nsEventStatus* aStatus) override;
+   virtual nsresult HandleDOMEventWithTarget(nsIContent* aTargetContent,
+                                                         nsIDOMEvent* aEvent,
+                                                         nsEventStatus* aStatus) override;
+   virtual bool ShouldIgnoreInvalidation() override;
+@@ -408,16 +410,18 @@ public:
+                                            uint32_t aSheetType) override;
+ 
+   virtual bool HasHandledUserInput() const override {
+     return mHasHandledUserInput;
+   }
+ 
+   virtual void FireResizeEvent() override;
+ 
++  static PresShell* GetShellForEventTarget(nsIFrame* aFrame,
++                                           nsIContent* aContent);
+   static PresShell* GetShellForTouchEvent(WidgetGUIEvent* aEvent);
+ 
+ protected:
+   virtual ~PresShell();
+ 
+   void HandlePostedReflowCallbacks(bool aInterruptible);
+   void CancelPostedReflowCallbacks();
+ 
+diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h
+--- a/layout/base/nsIPresShell.h
++++ b/layout/base/nsIPresShell.h
+@@ -886,17 +886,19 @@ public:
+   /**
+     * Interface to dispatch events via the presshell
+     * @note The caller must have a strong reference to the PresShell.
+     */
+   virtual nsresult HandleEventWithTarget(
+                                  mozilla::WidgetEvent* aEvent,
+                                  nsIFrame* aFrame,
+                                  nsIContent* aContent,
+-                                 nsEventStatus* aStatus) = 0;
++                                 nsEventStatus* aStatus,
++                                 bool aIsHandlingNativeEvent = false,
++                                 nsIContent** aTargetContent = nullptr) = 0;
+ 
+   /**
+    * Dispatch event to content only (NOT full processing)
+    * @note The caller must have a strong reference to the PresShell.
+    */
+   virtual nsresult HandleDOMEventWithTarget(
+                                  nsIContent* aTargetContent,
+                                  mozilla::WidgetEvent* aEvent,

+ 513 - 0
mozilla-release/patches/1420589-8-59a1.patch

@@ -0,0 +1,513 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1512013178 -28800
+#      Thu Nov 30 11:39:38 2017 +0800
+# Node ID 49491891f6b1119387bad90bb9a10fd9468d382c
+# Parent  3af4ab0cea0c45967e3e57a71be3cd039688f840
+Bug 1420589 Part8: Test multiple touch points on different documents. r=smaug.
+
+MozReview-Commit-ID: 49lKMFmuVq7
+
+diff --git a/dom/events/test/pointerevents/bug_1420589_iframe1.html b/dom/events/test/pointerevents/bug_1420589_iframe1.html
+new file mode 100644
+--- /dev/null
++++ b/dom/events/test/pointerevents/bug_1420589_iframe1.html
+@@ -0,0 +1,17 @@
++<body>
++	<script>
++	    let touchEvents = ["touchstart", "touchmove", "touchend"];
++	    let pointerEvents = ["pointerdown", "pointermove", "pointerup"];
++
++	    touchEvents.forEach((event) => {
++	        document.addEventListener(event, (e) => {
++	            parent.postMessage("iframe1 " + e.type, "*");
++	        }, { once: true });
++	    });
++	    pointerEvents.forEach((event) => {
++	        document.addEventListener(event, (e) => {
++	            parent.postMessage("iframe1 " + e.type, "*");
++	        }, { once: true });
++	    });
++	</script>
++</body>
+diff --git a/dom/events/test/pointerevents/bug_1420589_iframe2.html b/dom/events/test/pointerevents/bug_1420589_iframe2.html
+new file mode 100644
+--- /dev/null
++++ b/dom/events/test/pointerevents/bug_1420589_iframe2.html
+@@ -0,0 +1,17 @@
++<body>
++	<script>
++	    let touchEvents = ["touchstart", "touchmove", "touchend"];
++	    let pointerEvents = ["pointerdown", "pointermove", "pointerup"];
++
++	    touchEvents.forEach((event) => {
++	        document.addEventListener(event, (e) => {
++	            parent.postMessage("iframe2 " + e.type, "*");
++	        }, { once: true });
++	    });
++	    pointerEvents.forEach((event) => {
++	        document.addEventListener(event, (e) => {
++	            parent.postMessage("iframe2 " + e.type, "*");
++	        }, { once: true });
++	    });
++	</script>
++</body>
+diff --git a/dom/events/test/pointerevents/mochitest.ini b/dom/events/test/pointerevents/mochitest.ini
+--- a/dom/events/test/pointerevents/mochitest.ini
++++ b/dom/events/test/pointerevents/mochitest.ini
+@@ -10,16 +10,26 @@ support-files =
+ [test_bug1293174_implicit_pointer_capture_for_touch_1.html]
+   support-files = bug1293174_implicit_pointer_capture_for_touch_1.html
+ [test_bug1293174_implicit_pointer_capture_for_touch_2.html]
+   support-files = bug1293174_implicit_pointer_capture_for_touch_2.html
+ [test_bug1303704.html]
+ [test_bug1315862.html]
+ [test_bug1323158.html]
+ [test_bug1403055.html]
++[test_bug1420589_1.html]
++  support-files =
++    bug_1420589_iframe1.html
++    bug_1420589_iframe2.html
++[test_bug1420589_2.html]
++  support-files =
++    bug_1420589_iframe1.html
++[test_bug1420589_3.html]
++  support-files =
++    bug_1420589_iframe1.html
+ [test_empty_file.html]
+   disabled = disabled # Bug 1150091 - Issue with support-files
+ [test_pointerevent_attributes_hoverable_pointers-manual.html]
+   support-files =
+     pointerevent_attributes_hoverable_pointers-manual.html
+     ./resources/pointerevent_attributes_hoverable_pointers-iframe.html
+ [test_pointerevent_attributes_nohover_pointers-manual.html]
+   support-files =
+diff --git a/dom/events/test/pointerevents/test_bug1420589_1.html b/dom/events/test/pointerevents/test_bug1420589_1.html
+new file mode 100644
+--- /dev/null
++++ b/dom/events/test/pointerevents/test_bug1420589_1.html
+@@ -0,0 +1,103 @@
++<!DOCTYPE HTML>
++<html>
++<!--
++https://bugzilla.mozilla.org/show_bug.cgi?id=1420589
++-->
++<head>
++  <meta charset="utf-8">
++  <title>Test for Bug 1420589</title>
++  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
++  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
++  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
++</head>
++<body>
++<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1420589">Mozilla Bug 1420589</a>
++<p id="display"></p>
++<iframe id="iframe1" src="./bug_1420589_iframe1.html">
++</iframe>
++<iframe id="iframe2" src="./bug_1420589_iframe2.html">
++</iframe>
++<script type="text/javascript">
++/*
++  Test for Bug 1420589. This test synthesizes touch events with two points. The
++  first one hits iframe1 and the other hits iframe2.
++
++  We dispatch all touch events to the same document. We stop dispatching touch
++  events to a target if we can't find any ancestor document that is the same as
++  the document of the existing target. We check the points of the touch event in
++  reverse order. That means we choose the document of iframe2 as our targeted
++  document. We won't dispatch touch events to the document of iframe1 nor the
++  parent document of iframe1 and iframe2.
++
++  We dispatch pointer events to the hit targets even when there aren't in the
++  same document. This test expects that pointer events are dispatched to the div
++  element and the iframe document.
++*/
++SimpleTest.waitForExplicitFinish();
++
++var rx = 1;
++var ry = 1;
++var angle = 0;
++var force = 1;
++var modifiers = 0;
++var test1PointerId = 1;
++var test2PointerId = 2;
++
++function withoutImplicitlyPointerCaptureForTouch() {
++  let expectedEvents = [
++    // messages from the document of iframe1
++    "iframe1 pointerdown",
++    "iframe1 pointermove",
++    "iframe1 pointerup",
++
++    // messages from the document of iframe2
++    "iframe2 pointerdown",
++    "iframe2 pointermove",
++    "iframe2 pointerup",
++    "iframe2 touchstart",
++    "iframe2 touchmove",
++    "iframe2 touchend",
++  ];
++
++  window.addEventListener('message',function(e) {
++    ok(expectedEvents.includes(e.data), " don't expect " + e.data);
++    expectedEvents = expectedEvents.filter(item => item !== e.data);
++    if (e.data == "iframe2 touchend") {
++      ok(expectedEvents.length == 0, " expect " + expectedEvents);
++      SimpleTest.finish();
++    }
++  }, false)
++
++  let iframe1 = document.getElementById('iframe1');
++  let iframe2 = document.getElementById('iframe2');
++
++  let rect1 = iframe1.getBoundingClientRect();
++  let rect2 = iframe2.getBoundingClientRect();
++
++  let left1 = rect1.left + 5;
++  let left2 = rect2.left + 5;
++
++  let top1 = rect1.top + 5;
++  let top2 = rect2.top + 5;
++
++  var utils = SpecialPowers.getDOMWindowUtils(window);
++  utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
++                       [left1, left2], [top1, top2], [rx, rx], [ry, ry],
++                       [angle, angle], [force, force], 2, modifiers);
++  utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
++                       [left1, left2], [top1, top2], [rx, rx], [ry, ry],
++                       [angle, angle], [force, force], 2, modifiers);
++  utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
++                       [left1, left2], [top1, top2], [rx, rx], [ry, ry],
++                       [angle, angle], [force, force], 2, modifiers);
++}
++
++SimpleTest.waitForFocus(() => {
++  SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true],
++                                     ["dom.w3c_pointer_events.implicit_capture", false]]},
++                            withoutImplicitlyPointerCaptureForTouch);
++});
++
++</script>
++</body>
++</html>
+diff --git a/dom/events/test/pointerevents/test_bug1420589_1.html.1339461-1.later b/dom/events/test/pointerevents/test_bug1420589_1.html.1339461-1.later
+deleted file mode 100644
+--- a/dom/events/test/pointerevents/test_bug1420589_1.html.1339461-1.later
++++ /dev/null
+@@ -1,21 +0,0 @@
+---- test_bug1420589_1.html
+-+++ test_bug1420589_1.html
+-@@ -55,17 +55,17 @@ function withoutImplicitlyPointerCapture
+-     "iframe2 pointermove",
+-     "iframe2 pointerup",
+-     "iframe2 touchstart",
+-     "iframe2 touchmove",
+-     "iframe2 touchend",
+-   ];
+- 
+-   window.addEventListener('message',function(e) {
+--    ok(expectedEvents.indexOf(e.data) >= 0, " don't expect " + e.data);
+-+    ok(expectedEvents.includes(e.data), " don't expect " + e.data);
+-     expectedEvents = expectedEvents.filter(item => item !== e.data);
+-     if (e.data == "iframe2 touchend") {
+-       ok(expectedEvents.length == 0, " expect " + expectedEvents);
+-       SimpleTest.finish();
+-     }
+-   }, false)
+- 
+-   let iframe1 = document.getElementById('iframe1');
+diff --git a/dom/events/test/pointerevents/test_bug1420589_2.html b/dom/events/test/pointerevents/test_bug1420589_2.html
+new file mode 100644
+--- /dev/null
++++ b/dom/events/test/pointerevents/test_bug1420589_2.html
+@@ -0,0 +1,120 @@
++<!DOCTYPE HTML>
++<html>
++<!--
++https://bugzilla.mozilla.org/show_bug.cgi?id=1420589
++-->
++<head>
++  <meta charset="utf-8">
++  <title>Test for Bug 1420589</title>
++  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
++  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
++  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
++</head>
++<body>
++<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1420589">Mozilla Bug 1420589</a>
++<p id="display"></p>
++<div id="div1" style="width: 50px; height: 50px; background: green"></div>
++<iframe id="iframe1" src="./bug_1420589_iframe1.html">
++</iframe>
++<script type="text/javascript">
++/*
++  Test for Bug 1420589. This test synthesizes touch events with two points. One
++  hits the div element on the document and the other hits the iframe element.
++
++  We dispatch all touch events to the same document. If we find any target that
++  is not in the same document of the existed target, we try to find the ancestor
++  document of the new target which is in the same as the existing target and
++  dispatch touch events to it. We check the points of the touch event in reverse
++  order. That means we only dispatch touch events to the document which contains
++  the div element in this test and expect the div element and iframe element
++  receive touch events.
++
++  We dispatch pointer events to the hit targets even when there aren't in the
++  same document. This test expects that pointer events are dispatched to the div
++  element and the iframe document.
++*/
++SimpleTest.waitForExplicitFinish();
++
++var rx = 1;
++var ry = 1;
++var angle = 0;
++var force = 1;
++var modifiers = 0;
++var test1PointerId = 1;
++var test2PointerId = 2;
++
++function withoutImplicitlyPointerCaptureForTouch() {
++  let expectedEvents = [
++    // messages from the document of iframe1
++    "iframe1 pointerdown",
++    "iframe1 pointermove",
++    "iframe1 pointerup",
++
++    // messages from the parent document
++    "iframe touchstart",
++    "iframe touchmove",
++    "iframe touchend",
++    "div1 pointerdown",
++    "div1 pointermove",
++    "div1 pointerup",
++    "div1 touchstart",
++    "div1 touchmove",
++    "div1 touchend",
++  ];
++
++  window.addEventListener('message',function(e) {
++    ok(expectedEvents.includes(e.data), " don't expect " + e.data);
++    expectedEvents = expectedEvents.filter(item => item !== e.data);
++    if (e.data == "div1 touchend") {
++      ok(expectedEvents.length == 0, " expect " + expectedEvents);
++      SimpleTest.finish();
++    }
++  }, false)
++
++  let iframe1 = document.getElementById('iframe1');
++  let div1 = document.getElementById('div1');
++
++  let events = ["touchstart", "touchmove", "touchend", "pointerdown", "pointermove", "pointerup"];
++  events.forEach((event) => {
++    div1.addEventListener(event, (e) => {
++      postMessage("div1 " + e.type, "*");
++    }, { once: true });
++    iframe1.addEventListener(event, (e) => {
++      postMessage("iframe " + e.type, "*");
++    }, { once: true });
++  });
++
++  let rect1 = iframe1.getBoundingClientRect();
++  let rect2 = div1.getBoundingClientRect();
++
++  let left1 = rect1.left + 5;
++  let left2 = rect2.left + 5;
++
++  let top1 = rect1.top + 5;
++  let top2 = rect2.top + 5;
++
++  var utils = SpecialPowers.getDOMWindowUtils(window);
++  utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
++                       [left1, left2], [top1, top2], [rx, rx], [ry, ry],
++                       [angle, angle], [force, force], 2, modifiers);
++
++  // Move the touch pointers so that we dispatch all of them to content.
++  left1++;
++  left2++;
++  utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
++                       [left1, left2], [top1, top2], [rx, rx], [ry, ry],
++                       [angle, angle], [force, force], 2, modifiers);
++  utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
++                       [left1, left2], [top1, top2], [rx, rx], [ry, ry],
++                       [angle, angle], [force, force], 2, modifiers);
++}
++
++SimpleTest.waitForFocus(() => {
++  SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true],
++                                     ["dom.w3c_pointer_events.implicit_capture", false]]},
++                            withoutImplicitlyPointerCaptureForTouch);
++});
++
++</script>
++</body>
++</html>
+diff --git a/dom/events/test/pointerevents/test_bug1420589_2.html.1339461-1.later b/dom/events/test/pointerevents/test_bug1420589_2.html.1339461-1.later
+deleted file mode 100644
+--- a/dom/events/test/pointerevents/test_bug1420589_2.html.1339461-1.later
++++ /dev/null
+@@ -1,21 +0,0 @@
+---- test_bug1420589_2.html
+-+++ test_bug1420589_2.html
+-@@ -58,17 +58,17 @@ function withoutImplicitlyPointerCapture
+-     "div1 pointermove",
+-     "div1 pointerup",
+-     "div1 touchstart",
+-     "div1 touchmove",
+-     "div1 touchend",
+-   ];
+- 
+-   window.addEventListener('message',function(e) {
+--    ok(expectedEvents.indexOf(e.data) >= 0, " don't expect " + e.data);
+-+    ok(expectedEvents.includes(e.data), " don't expect " + e.data);
+-     expectedEvents = expectedEvents.filter(item => item !== e.data);
+-     if (e.data == "div1 touchend") {
+-       ok(expectedEvents.length == 0, " expect " + expectedEvents);
+-       SimpleTest.finish();
+-     }
+-   }, false)
+- 
+-   let iframe1 = document.getElementById('iframe1');
+diff --git a/dom/events/test/pointerevents/test_bug1420589_3.html b/dom/events/test/pointerevents/test_bug1420589_3.html
+new file mode 100644
+--- /dev/null
++++ b/dom/events/test/pointerevents/test_bug1420589_3.html
+@@ -0,0 +1,113 @@
++<!DOCTYPE HTML>
++<html>
++<!--
++https://bugzilla.mozilla.org/show_bug.cgi?id=1420589
++-->
++<head>
++  <meta charset="utf-8">
++  <title>Test for Bug 1420589</title>
++  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
++  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
++  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
++</head>
++<body>
++<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1420589">Mozilla Bug 1420589</a>
++<p id="display"></p>
++<div id="div1" style="width: 50px; height: 50px; background: green"></div>
++<iframe id="iframe1" src="./bug_1420589_iframe1.html">
++</iframe>
++<script type="text/javascript">
++/*
++  Test for Bug 1420589. This test is similar to test_bug1420589_2.html but the
++  first touch point hit the div element and the second point hits the iframe.
++
++  We stop dispatching touch events to a target when we can't find any ancestor
++  document that is the same as the document of the existing target. This test
++  expects that we only dispatch touch events to the iframe document.
++
++  We dispatch pointer events to the hit targets even when there aren't in the
++  same document. This test expects that pointer events are dispatched to the div
++  element and the iframe document.
++*/
++SimpleTest.waitForExplicitFinish();
++
++var rx = 1;
++var ry = 1;
++var angle = 0;
++var force = 1;
++var modifiers = 0;
++var test1PointerId = 1;
++var test2PointerId = 2;
++
++function withoutImplicitlyPointerCaptureForTouch() {
++  let expectedEvents = [
++    // messages from the document of iframe1
++    "iframe1 pointerdown",
++    "iframe1 pointermove",
++    "iframe1 pointerup",
++    "iframe1 touchstart",
++    "iframe1 touchmove",
++    "iframe1 touchend",
++
++    // messages from the parent document
++    "div1 pointerdown",
++    "div1 pointermove",
++    "div1 pointerup",
++  ];
++
++  window.addEventListener('message',function(e) {
++    ok(expectedEvents.includes(e.data), " don't expect " + e.data);
++    expectedEvents = expectedEvents.filter(item => item !== e.data);
++    if (e.data == "iframe1 touchend") {
++      ok(expectedEvents.length == 0, " expect " + expectedEvents);
++      SimpleTest.finish();
++    }
++  }, false)
++
++  let iframe1 = document.getElementById('iframe1');
++  let div1 = document.getElementById('div1');
++
++  let events = ["touchstart", "touchmove", "touchend", "pointerdown", "pointermove", "pointerup"];
++  events.forEach((event) => {
++    div1.addEventListener(event, (e) => {
++      postMessage("div1 " + e.type, "*");
++    }, { once: true });
++    iframe1.addEventListener(event, (e) => {
++      postMessage("iframe " + e.type, "*");
++    }, { once: true });
++  });
++
++  let rect1 = div1.getBoundingClientRect();
++  let rect2 = iframe1.getBoundingClientRect();
++
++  let left1 = rect1.left + 5;
++  let left2 = rect2.left + 5;
++
++  let top1 = rect1.top + 5;
++  let top2 = rect2.top + 5;
++
++  var utils = SpecialPowers.getDOMWindowUtils(window);
++  utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
++                       [left1, left2], [top1, top2], [rx, rx], [ry, ry],
++                       [angle, angle], [force, force], 2, modifiers);
++
++  // Move the touch pointers so that we dispatch all of them to content.
++  left1++;
++  left2++;
++  utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
++                       [left1, left2], [top1, top2], [rx, rx], [ry, ry],
++                       [angle, angle], [force, force], 2, modifiers);
++  utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
++                       [left1, left2], [top1, top2], [rx, rx], [ry, ry],
++                       [angle, angle], [force, force], 2, modifiers);
++}
++
++SimpleTest.waitForFocus(() => {
++  SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true],
++                                     ["dom.w3c_pointer_events.implicit_capture", false]]},
++                            withoutImplicitlyPointerCaptureForTouch);
++});
++
++</script>
++</body>
++</html>
+diff --git a/dom/events/test/pointerevents/test_bug1420589_3.html.1339461-1.later b/dom/events/test/pointerevents/test_bug1420589_3.html.1339461-1.later
+deleted file mode 100644
+--- a/dom/events/test/pointerevents/test_bug1420589_3.html.1339461-1.later
++++ /dev/null
+@@ -1,21 +0,0 @@
+---- test_bug1420589_3.html
+-+++ test_bug1420589_3.html
+-@@ -51,17 +51,17 @@ function withoutImplicitlyPointerCapture
+- 
+-     // messages from the parent document
+-     "div1 pointerdown",
+-     "div1 pointermove",
+-     "div1 pointerup",
+-   ];
+- 
+-   window.addEventListener('message',function(e) {
+--    ok(expectedEvents.indexOf(e.data) >= 0, " don't expect " + e.data);
+-+    ok(expectedEvents.includes(e.data), " don't expect " + e.data);
+-     expectedEvents = expectedEvents.filter(item => item !== e.data);
+-     if (e.data == "iframe1 touchend") {
+-       ok(expectedEvents.length == 0, " expect " + expectedEvents);
+-       SimpleTest.finish();
+-     }
+-   }, false)
+- 
+-   let iframe1 = document.getElementById('iframe1');

+ 365 - 0
mozilla-release/patches/1420589-9-59a1.patch

@@ -0,0 +1,365 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1512029403 -28800
+#      Thu Nov 30 16:10:03 2017 +0800
+# Node ID 2155af8d88c3bd116305378d7e22b947db339c55
+# Parent  8931ab34571c6d625d97d186bbf9582654ac5f5a
+Bug 1420589 Part9: Dispatch pointer events to the capturing target even it's frame is destroyed. r=smaug.
+
+MozReview-Commit-ID: DxNx3ByTdCW
+
+diff --git a/dom/events/PointerEventHandler.cpp b/dom/events/PointerEventHandler.cpp
+--- a/dom/events/PointerEventHandler.cpp
++++ b/dom/events/PointerEventHandler.cpp
+@@ -326,42 +326,32 @@ PointerEventHandler::GetPointerCapturing
+ {
+   PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
+   if (pointerCaptureInfo) {
+     return pointerCaptureInfo->mOverrideContent;
+   }
+   return nullptr;
+ }
+ 
+-/* static */ nsIFrame*
+-PointerEventHandler::GetPointerCapturingFrame(WidgetGUIEvent* aEvent)
++/* static */ nsIContent*
++PointerEventHandler::GetPointerCapturingContent(WidgetGUIEvent* aEvent)
+ {
+   if (!IsPointerEventEnabled() || (aEvent->mClass != ePointerEventClass &&
+                                    aEvent->mClass != eMouseEventClass) ||
+       aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) {
+     // Pointer capture should only be applied to all pointer events and mouse
+     // events except ePointerDown and eMouseDown;
+     return nullptr;
+   }
+ 
+   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+   if (!mouseEvent) {
+     return nullptr;
+   }
+-
+-  // Find the content which captures the pointer.
+-  nsIContent* capturingContent =
+-    GetPointerCapturingContent(mouseEvent->pointerId);
+-
+-  if (!capturingContent) {
+-    return nullptr;
+-  }
+-  // Return the content's primary frame as the target frame.
+-  nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
+-  return capturingFrame ? capturingFrame : nullptr;
++  return GetPointerCapturingContent(mouseEvent->pointerId);
+ }
+ 
+ /* static */ void
+ PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent)
+ {
+   // We should check that aChild does not contain pointer capturing elements.
+   // If it does we should release the pointer capture for the elements.
+   for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
+diff --git a/dom/events/PointerEventHandler.h b/dom/events/PointerEventHandler.h
+--- a/dom/events/PointerEventHandler.h
++++ b/dom/events/PointerEventHandler.h
+@@ -84,27 +84,26 @@ public:
+   static void ProcessPointerCaptureForTouch(WidgetTouchEvent* aEvent);
+   static void CheckPointerCaptureState(WidgetPointerEvent* aEvent);
+ 
+   // Implicitly get and release capture of current pointer for touch.
+   static void ImplicitlyCapturePointer(nsIFrame* aFrame, WidgetEvent* aEvent);
+   static void ImplicitlyReleasePointerCapture(WidgetEvent* aEvent);
+ 
+   /**
+-   * GetPointerCapturingFrame returns a target frame of aEvent. If the event is
+-   * a mouse or pointer event (except mousedown and pointerdown), the pointer
+-   * may be captured by a content. This method returns the capturing content's
+-   * primary frame. Otherwise, nullptr.
++   * GetPointerCapturingContent returns a target content which captures the
++   * pointer. It's applied to mouse or pointer event (except mousedown and
++   * pointerdown). When capturing, return the content. Otherwise, nullptr.
+    *
+    * @param aEvent               A mouse event or pointer event which may be
+    *                             captured.
+    *
+-   * @return                     Target frame for aEvent.
++   * @return                     Target content for aEvent.
+    */
+-  static nsIFrame* GetPointerCapturingFrame(WidgetGUIEvent* aEvent);
++  static nsIContent* GetPointerCapturingContent(WidgetGUIEvent* aEvent);
+ 
+   static nsIContent* GetPointerCapturingContent(uint32_t aPointerId);
+ 
+   // Release pointer capture if captured by the specified content or it's
+   // descendant. This is called to handle the case that the pointer capturing
+   // content or it's parent is removed from the document.
+   static void ReleaseIfCaptureByDescendant(nsIContent* aContent);
+ 
+diff --git a/dom/events/test/pointerevents/mochitest.ini b/dom/events/test/pointerevents/mochitest.ini
+--- a/dom/events/test/pointerevents/mochitest.ini
++++ b/dom/events/test/pointerevents/mochitest.ini
+@@ -140,12 +140,13 @@ support-files =
+     pointerevent_touch-action-pan-down-css_touch-manual.html
+     pointerevent_touch-action-pan-left-css_touch-manual.html
+     pointerevent_touch-action-pan-right-css_touch-manual.html
+     pointerevent_touch-action-pan-up-css_touch-manual.html
+ [test_trigger_fullscreen_by_pointer_events.html]
+   support-files =
+     file_test_trigger_fullscreen.html
+ [test_trigger_popup_by_pointer_events.html]
++[test_remove_frame_when_got_pointer_capture.html]
+ [test_getCoalescedEvents.html]
+   skip-if = !e10s
+   support-files =
+     ../../../../gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
+diff --git a/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html b/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html
+new file mode 100644
+--- /dev/null
++++ b/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html
+@@ -0,0 +1,116 @@
++<!DOCTYPE html>
++<html>
++<head>
++  <meta charset="utf-8">
++  <title>Test for triggering popup by pointer events</title>
++  <script src="/tests/SimpleTest/SimpleTest.js"></script>
++  <script src="/tests/SimpleTest/EventUtils.js"></script>
++  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
++</head>
++<body>
++<div id="div1" style="width: 50px; height: 50px; background: green"></div>
++<div id="div2" style="width: 50px; height: 50px; background: green"></div>
++<script>
++
++SimpleTest.waitForExplicitFinish();
++
++function startTest() {
++  let div1 = document.getElementById("div1");
++  let divEvents = [
++    "pointerdown",
++    "gotpointercapture",
++    "pointermove",
++    "pointerup",
++    "lostpointercapture",
++    "mousedown",
++    "mousemove",
++    "mouseup",
++  ];
++
++  let documentEvents = [
++    "pointerdown",
++    "pointermove",
++    "pointerup",
++    "mousedown",
++    "mousemove",
++    "mouseup",
++  ];
++
++  divEvents.forEach((event) => {
++    div1.addEventListener(event, (e) => {
++      ok(divEvents.includes(e.type), " don't expect " + e.type);
++      divEvents = divEvents.filter(item => item !== e.type);
++    }, { once: true });
++  });
++
++  documentEvents.forEach((event) => {
++    document.addEventListener(event, (e) => {
++      is(e.target, div1, e.type + " should be dispatched to div1");
++    }, { once: true });
++  });
++
++  div1.addEventListener("pointerdown", (e) => {
++    div1.setPointerCapture(e.pointerId);
++  });
++
++  div1.addEventListener("gotpointercapture", (e) => {
++    div1.style.display = "none";
++  });
++
++  synthesizeMouseAtCenter(div1, {type: "mousedown"});
++  synthesizeMouseAtCenter(div2, {type: "mousemove"});
++  synthesizeMouseAtCenter(div2, {type: "mouseup"});
++
++  ok(divEvents.length == 0, " expect " + divEvents);
++
++  divEvents = [
++    "pointerdown",
++    "gotpointercapture",
++    "pointermove",
++    "pointerup",
++    "lostpointercapture",
++    "touchstart",
++    "touchmove",
++    "touchend",
++  ];
++
++  documentEvents = [
++    "pointerdown",
++    "pointermove",
++    "pointerup",
++    "touchstart",
++    "touchmove",
++    "touchend",
++  ];
++  divEvents.forEach((event) => {
++    div1.addEventListener(event, (e) => {
++      ok(divEvents.includes(e.type), " don't expect " + e.type);
++      divEvents = divEvents.filter(item => item !== e.type);
++    }, { once: true });
++  });
++
++  documentEvents.forEach((event) => {
++    document.addEventListener(event, (e) => {
++      is(e.target, div1, e.type + " should be dispatched to div1");
++    }, { once: true });
++  });
++
++  div1.style.display = "block";
++  synthesizeMouseAtCenter(div1, {type: "mousemove"});
++  synthesizeTouch(div1, 5, 5, { type: "touchstart" });
++  synthesizeTouch(div2, 5, 5, { type: "touchmove" });
++  synthesizeTouch(div2, 5, 5, { type: "touchend" });
++
++  ok(divEvents.length == 0, " expect " + divEvents);
++  SimpleTest.finish();
++}
++
++SimpleTest.waitForFocus(() => {
++  SpecialPowers.pushPrefEnv({
++    "set": [["dom.w3c_pointer_events.enabled", true]]
++  }, startTest);
++});
++
++</script>
++</body>
++</html>
+diff --git a/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html.1339461-1.later b/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html.1339461-1.later
+deleted file mode 100644
+--- a/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html.1339461-1.later
++++ /dev/null
+@@ -1,40 +0,0 @@
+---- test_remove_frame_when_got_pointer_capture.html
+-+++ test_remove_frame_when_got_pointer_capture.html
+-@@ -43,17 +43,17 @@ function startTest() {
+-     "pointerup",
+-     "mousedown",
+-     "mousemove",
+-     "mouseup",
+-   ];
+- 
+-   divEvents.forEach((event) => {
+-     div1.addEventListener(event, (e) => {
+--      ok(divEvents.indexOf(e.type) >= 0, " don't expect " + e.type);
+-+      ok(divEvents.includes(e.type), " don't expect " + e.type);
+-       divEvents = divEvents.filter(item => item !== e.type);
+-     }, { once: true });
+-   });
+- 
+-   documentEvents.forEach((event) => {
+-     iframe.contentDocument.addEventListener(event, (e) => {
+-       is(e.target, div1, e.type + " should be dispatched to div1");
+-     }, { once: true });
+-@@ -89,17 +89,17 @@ function startTest() {
+-     "pointermove",
+-     "pointerup",
+-     "touchstart",
+-     "touchmove",
+-     "touchend",
+-   ];
+-   divEvents.forEach((event) => {
+-     div1.addEventListener(event, (e) => {
+--      ok(divEvents.indexOf(e.type) >= 0, " don't expect " + e.type);
+-+      ok(divEvents.includes(e.type), " don't expect " + e.type);
+-       divEvents = divEvents.filter(item => item !== e.type);
+-     }, { once: true });
+-   });
+- 
+-   documentEvents.forEach((event) => {
+-     iframe.contentDocument.addEventListener(event, (e) => {
+-       is(e.target, div1, e.type + " should be dispatched to div1");
+-     }, { once: true });
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -7016,33 +7016,58 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+       PointerEventHandler::MaybeProcessPointerCapture(aEvent);
+       // Prevent application crashes, in case damaged frame.
+       if (!frameKeeper.IsAlive()) {
+         NS_WARNING("Nothing to handle this event!");
+         return NS_OK;
+       }
+     }
+ 
+-    nsIFrame* pointerCapturingFrame =
+-      PointerEventHandler::GetPointerCapturingFrame(aEvent);
+-
+-    if (pointerCapturingFrame) {
+-      frame = pointerCapturingFrame;
++    // Only capture mouse events and pointer events.
++    nsIContent* pointerCapturingContent =
++      PointerEventHandler::GetPointerCapturingContent(aEvent);
++
++    if (pointerCapturingContent) {
++      nsIFrame* pointerCapturingFrame =
++        pointerCapturingContent->GetPrimaryFrame();
++
++      if (!pointerCapturingFrame) {
++        // Dispatch events to the capturing content even it's frame is
++        // destroyed.
++        PointerEventHandler::DispatchPointerFromMouseOrTouch(
++          this, nullptr, pointerCapturingContent, aEvent, false, aEventStatus,
++          nullptr);
++
++        PresShell* shell = GetShellForEventTarget(nullptr,
++                                                  pointerCapturingContent);
++
++        if (!shell) {
++          // The capturing element could be changed when dispatch pointer
++          // events.
++          return NS_OK;
++        }
++        return shell->HandleEventWithTarget(aEvent, nullptr,
++                                            pointerCapturingContent,
++                                            aEventStatus, true);
++      } else {
++        frame = pointerCapturingFrame;
++      }
+     }
+ 
+     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+     bool isWindowLevelMouseExit = (aEvent->mMessage == eMouseExitFromWidget) &&
+       (mouseEvent && mouseEvent->mExitFrom == WidgetMouseEvent::eTopLevel);
+ 
+     // Get the frame at the event point. However, don't do this if we're
+     // capturing and retargeting the event because the captured frame will
+     // be used instead below. Also keep using the root frame if we're dealing
+     // with a window-level mouse exit event since we want to start sending
+     // mouse out events at the root EventStateManager.
+-    if (!captureRetarget && !isWindowLevelMouseExit && !pointerCapturingFrame) {
++    if (!captureRetarget && !isWindowLevelMouseExit &&
++        !pointerCapturingContent) {
+       if (aEvent->mClass == eTouchEventClass) {
+         frame = TouchManager::SetupTarget(aEvent->AsTouchEvent(), frame);
+       } else {
+         uint32_t flags = 0;
+         nsPoint eventPoint =
+           nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
+ 
+         if (mouseEvent && mouseEvent->mClass == eMouseEventClass &&
+@@ -7056,17 +7081,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+         }
+       }
+     }
+ 
+     // if a node is capturing the mouse, check if the event needs to be
+     // retargeted at the capturing content instead. This will be the case when
+     // capture retargeting is being used, no frame was found or the frame's
+     // content is not a descendant of the capturing content.
+-    if (capturingContent && !pointerCapturingFrame &&
++    if (capturingContent && !pointerCapturingContent &&
+         (gCaptureInfo.mRetargetToElement || !frame->GetContent() ||
+          !nsContentUtils::ContentIsCrossDocDescendantOf(frame->GetContent(),
+                                                         capturingContent))) {
+       // A check was already done above to ensure that capturingContent is
+       // in this presshell.
+       NS_ASSERTION(capturingContent->GetComposedDoc() == GetDocument(),
+                    "Unexpected document");
+       nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();

+ 122 - 0
mozilla-release/patches/1422869-59a1.patch

@@ -0,0 +1,122 @@
+# HG changeset patch
+# User David Major <dmajor@mozilla.com>
+# Date 1512408084 18000
+# Node ID 71e2ef59fec09521a4f51ee1d85de970ea59d1b0
+# Parent  6a6449817a033ed64652c7a50a600bc60a9e9189
+Bug 1422869 - Add "htp" and "htps" to the scheme typo fixup list. r=bz
+
+diff --git a/docshell/base/nsDefaultURIFixup.cpp b/docshell/base/nsDefaultURIFixup.cpp
+--- a/docshell/base/nsDefaultURIFixup.cpp
++++ b/docshell/base/nsDefaultURIFixup.cpp
+@@ -265,31 +265,41 @@ nsDefaultURIFixup::GetFixupURIInfo(const
+         scheme.LowerCaseEqualsLiteral("ftp") ||
+         scheme.LowerCaseEqualsLiteral("file")) {
+       // Do nothing.
+     } else if (scheme.LowerCaseEqualsLiteral("ttp")) {
+       // ttp -> http.
+       uriString.ReplaceLiteral(0, 3, "http");
+       scheme.AssignLiteral("http");
+       info->mFixupChangedProtocol = true;
++    } else if (scheme.LowerCaseEqualsLiteral("htp")) {
++      // htp -> http.
++      uriString.ReplaceLiteral(0, 3, "http");
++      scheme.AssignLiteral("http");
++      info->mFixupChangedProtocol = true;
+     } else if (scheme.LowerCaseEqualsLiteral("ttps")) {
+       // ttps -> https.
+       uriString.ReplaceLiteral(0, 4, "https");
+       scheme.AssignLiteral("https");
+       info->mFixupChangedProtocol = true;
+     } else if (scheme.LowerCaseEqualsLiteral("tps")) {
+       // tps -> https.
+       uriString.ReplaceLiteral(0, 3, "https");
+       scheme.AssignLiteral("https");
+       info->mFixupChangedProtocol = true;
+     } else if (scheme.LowerCaseEqualsLiteral("ps")) {
+       // ps -> https.
+       uriString.ReplaceLiteral(0, 2, "https");
+       scheme.AssignLiteral("https");
+       info->mFixupChangedProtocol = true;
++    } else if (scheme.LowerCaseEqualsLiteral("htps")) {
++      // htps -> https.
++      uriString.ReplaceLiteral(0, 4, "https");
++      scheme.AssignLiteral("https");
++      info->mFixupChangedProtocol = true;
+     } else if (scheme.LowerCaseEqualsLiteral("ile")) {
+       // ile -> file.
+       uriString.ReplaceLiteral(0, 3, "file");
+       scheme.AssignLiteral("file");
+       info->mFixupChangedProtocol = true;
+     } else if (scheme.LowerCaseEqualsLiteral("le")) {
+       // le -> file.
+       uriString.ReplaceLiteral(0, 2, "file");
+diff --git a/docshell/test/unit/test_nsDefaultURIFixup.js b/docshell/test/unit/test_nsDefaultURIFixup.js
+--- a/docshell/test/unit/test_nsDefaultURIFixup.js
++++ b/docshell/test/unit/test_nsDefaultURIFixup.js
+@@ -7,31 +7,41 @@ var pref = "browser.fixup.typo.scheme";
+ 
+ var data = [
+   {
+     // ttp -> http.
+     wrong: 'ttp://www.example.com/',
+     fixed: 'http://www.example.com/',
+   },
+   {
++    // htp -> http.
++    wrong: 'htp://www.example.com/',
++    fixed: 'http://www.example.com/',
++  },
++  {
+     // ttps -> https.
+     wrong: 'ttps://www.example.com/',
+     fixed: 'https://www.example.com/',
+   },
+   {
+     // tps -> https.
+     wrong: 'tps://www.example.com/',
+     fixed: 'https://www.example.com/',
+   },
+   {
+     // ps -> https.
+     wrong: 'ps://www.example.com/',
+     fixed: 'https://www.example.com/',
+   },
+   {
++    // htps -> https.
++    wrong: 'htps://www.example.com/',
++    fixed: 'https://www.example.com/',
++  },
++  {
+     // ile -> file.
+     wrong: 'ile:///this/is/a/test.html',
+     fixed: 'file:///this/is/a/test.html',
+   },
+   {
+     // le -> file.
+     wrong: 'le:///this/is/a/test.html',
+     fixed: 'file:///this/is/a/test.html',
+diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
+--- a/modules/libpref/init/all.js
++++ b/modules/libpref/init/all.js
+@@ -1632,19 +1632,21 @@ pref("network.protocol-handler.external.
+ #endif
+ pref("network.protocol-handler.external.disk", false);
+ pref("network.protocol-handler.external.disks", false);
+ pref("network.protocol-handler.external.afp", false);
+ pref("network.protocol-handler.external.moz-icon", false);
+ 
+ // Don't allow  external protocol handlers for common typos
+ pref("network.protocol-handler.external.ttp", false);  // http
++pref("network.protocol-handler.external.htp", false);  // http
+ pref("network.protocol-handler.external.ttps", false); // https
+ pref("network.protocol-handler.external.tps", false);  // https
+ pref("network.protocol-handler.external.ps", false);   // https
++pref("network.protocol-handler.external.htps", false); // https
+ pref("network.protocol-handler.external.ile", false);  // file
+ pref("network.protocol-handler.external.le", false);   // file
+ 
+ // An exposed protocol handler is one that can be used in all contexts.  A
+ // non-exposed protocol handler is one that can only be used internally by the
+ // application.  For example, a non-exposed protocol would not be loaded by the
+ // application in response to a link click or a X-remote openURL command.
+ // Instead, it would be deferred to the system's external protocol handler.

+ 35 - 0
mozilla-release/patches/1425932-59a1.patch

@@ -0,0 +1,35 @@
+# HG changeset patch
+# User Samuel Thibault <samuel.thibault@ens-lyon.org>
+# Date 1513861140 18000
+# Node ID 09e57c36e8daa5a52290fcf1023c1b4e670f0a33
+# Parent  3e349655b92dc0f88a9b313d16c21b3033f080b8
+Bug 1425932 - Explicitly process chrome events before processing document events. r=surkov
+
+diff --git a/accessible/base/NotificationController.cpp b/accessible/base/NotificationController.cpp
+--- a/accessible/base/NotificationController.cpp
++++ b/accessible/base/NotificationController.cpp
+@@ -626,20 +626,20 @@ NotificationController::WillRefresh(mozi
+   // Wait until an update, we have started, or an interruptible reflow is
+   // finished.
+   if (mObservingState == eRefreshProcessing ||
+       mObservingState == eRefreshProcessingForUpdate ||
+       mPresShell->IsReflowInterrupted()) {
+     return;
+   }
+ 
+-  // Wait for parent's notifications, to get proper ordering between e.g. tab
+-  // event and content event.
++  // Process parent's notifications before ours, to get proper ordering between
++  // e.g. tab event and content event.
+   if (WaitingForParent()) {
+-    return;
++    mDocument->ParentDocument()->mNotificationController->WillRefresh(aTime);
+   }
+ 
+   // Any generic notifications should be queued if we're processing content
+   // insertions or generic notifications.
+   mObservingState = eRefreshProcessingForUpdate;
+ 
+   // Initial accessible tree construction.
+   if (!mDocument->HasLoadState(DocAccessible::eTreeConstructed)) {
+

+ 39 - 0
mozilla-release/patches/1426388-59a1.patch

@@ -0,0 +1,39 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1513783972 -28800
+# Node ID 4c1302ff2703c8ac8c50b1b763b9e2a67a2dc7ae
+# Parent  39d5991f02ba0e9950c380f91817fb9bac430d87
+Bug 1426388 - Crash in mozilla::TouchManager::SuppressInvalidPointsAndGetTargetedFrame. r=smaug.
+
+diff --git a/layout/base/TouchManager.cpp b/layout/base/TouchManager.cpp
+--- a/layout/base/TouchManager.cpp
++++ b/layout/base/TouchManager.cpp
+@@ -212,22 +212,22 @@ TouchManager::SuppressInvalidPointsAndGe
+       // if we couldn't find a target frame in the same document as
+       // anyTarget, remove the touch from the capture touch list, as
+       // well as the event->mTouches array. touchmove events that aren't
+       // in the captured touch list will be discarded
+       if (!newTargetFrame) {
+         touch->mIsTouchEventSuppressed = true;
+       } else {
+         targetFrame = newTargetFrame;
++        targetFrame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
++        while (targetContent && !targetContent->IsElement()) {
++          targetContent = targetContent->GetParent();
++        }
++        touch->SetTarget(targetContent);
+       }
+-      targetFrame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
+-      while (targetContent && !targetContent->IsElement()) {
+-        targetContent = targetContent->GetParent();
+-      }
+-      touch->SetTarget(targetContent);
+     }
+     if (targetFrame) {
+       frame = targetFrame;
+     }
+   }
+   return frame;
+ }
+ 
+

+ 118 - 0
mozilla-release/patches/1426527-59a1.patch

@@ -0,0 +1,118 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1513989095 -28800
+# Node ID 44f6455da33e3c8bd32df9611de89d20c771b0d7
+# Parent  06d4983f64feb093c676943db31584b3febb4fc6
+Bug 1426527 - Revise test_remove_frame_when_got_pointer_capture.html to correctly run the test in release build. f=RyanVM. r=smaug.
+
+diff --git a/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html b/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html
+--- a/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html
++++ b/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html
+@@ -3,24 +3,34 @@
+ <head>
+   <meta charset="utf-8">
+   <title>Test for triggering popup by pointer events</title>
+   <script src="/tests/SimpleTest/SimpleTest.js"></script>
+   <script src="/tests/SimpleTest/EventUtils.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+-<div id="div1" style="width: 50px; height: 50px; background: green"></div>
+-<div id="div2" style="width: 50px; height: 50px; background: green"></div>
++<p id="content">
++</p>
+ <script>
+ 
+ SimpleTest.waitForExplicitFinish();
+ 
+ function startTest() {
+-  let div1 = document.getElementById("div1");
++  let content = document.getElementById('content');
++  let iframe = document.createElement('iframe');
++  iframe.width = 200;
++  iframe.height = 200;
++  content.appendChild(iframe);
++  iframe.contentDocument.body.innerHTML =
++    "<div id='div1' style='width: 50px; height: 50px; background: green'></div>" +
++    "<div id='div2' style='width: 50px; height: 50px; background: red'></div>";
++
++  let div1 = iframe.contentDocument.getElementById("div1");
++  let div2 = iframe.contentDocument.getElementById("div2");
+   let divEvents = [
+     "pointerdown",
+     "gotpointercapture",
+     "pointermove",
+     "pointerup",
+     "lostpointercapture",
+     "mousedown",
+     "mousemove",
+@@ -39,32 +49,32 @@ function startTest() {
+   divEvents.forEach((event) => {
+     div1.addEventListener(event, (e) => {
+       ok(divEvents.includes(e.type), " don't expect " + e.type);
+       divEvents = divEvents.filter(item => item !== e.type);
+     }, { once: true });
+   });
+ 
+   documentEvents.forEach((event) => {
+-    document.addEventListener(event, (e) => {
++    iframe.contentDocument.addEventListener(event, (e) => {
+       is(e.target, div1, e.type + " should be dispatched to div1");
+     }, { once: true });
+   });
+ 
+   div1.addEventListener("pointerdown", (e) => {
+     div1.setPointerCapture(e.pointerId);
+   });
+ 
+   div1.addEventListener("gotpointercapture", (e) => {
+     div1.style.display = "none";
+   });
+ 
+-  synthesizeMouseAtCenter(div1, {type: "mousedown"});
+-  synthesizeMouseAtCenter(div2, {type: "mousemove"});
+-  synthesizeMouseAtCenter(div2, {type: "mouseup"});
++  synthesizeMouseAtCenter(div1, {type: "mousedown"}, iframe.contentWindow);
++  synthesizeMouseAtCenter(div2, {type: "mousemove"}, iframe.contentWindow);
++  synthesizeMouseAtCenter(div2, {type: "mouseup"}, iframe.contentWindow);
+ 
+   ok(divEvents.length == 0, " expect " + divEvents);
+ 
+   divEvents = [
+     "pointerdown",
+     "gotpointercapture",
+     "pointermove",
+     "pointerup",
+@@ -85,26 +95,26 @@ function startTest() {
+   divEvents.forEach((event) => {
+     div1.addEventListener(event, (e) => {
+       ok(divEvents.includes(e.type), " don't expect " + e.type);
+       divEvents = divEvents.filter(item => item !== e.type);
+     }, { once: true });
+   });
+ 
+   documentEvents.forEach((event) => {
+-    document.addEventListener(event, (e) => {
++    iframe.contentDocument.addEventListener(event, (e) => {
+       is(e.target, div1, e.type + " should be dispatched to div1");
+     }, { once: true });
+   });
+ 
+   div1.style.display = "block";
+-  synthesizeMouseAtCenter(div1, {type: "mousemove"});
+-  synthesizeTouch(div1, 5, 5, { type: "touchstart" });
+-  synthesizeTouch(div2, 5, 5, { type: "touchmove" });
+-  synthesizeTouch(div2, 5, 5, { type: "touchend" });
++  synthesizeMouseAtCenter(div1, {type: "mousemove"}, iframe.contentWindow);
++  synthesizeTouch(div1, 5, 5, { type: "touchstart" }, iframe.contentWindow);
++  synthesizeTouch(div2, 5, 5, { type: "touchmove" }, iframe.contentWindow);
++  synthesizeTouch(div2, 5, 5, { type: "touchend" }, iframe.contentWindow);
+ 
+   ok(divEvents.length == 0, " expect " + divEvents);
+   SimpleTest.finish();
+ }
+ 
+ SimpleTest.waitForFocus(() => {
+   SpecialPowers.pushPrefEnv({
+     "set": [["dom.w3c_pointer_events.enabled", true]]
+

+ 113 - 0
mozilla-release/patches/1426728-59a1.patch

@@ -0,0 +1,113 @@
+# HG changeset patch
+# User Stone Shih <sshih@mozilla.com>
+# Date 1513916825 -28800
+# Node ID 786c0c3f6975d73397a7a596939ddc92046a057f
+# Parent  0d6b0f5ffc8f3da1aa6ddd98bbd0102aeb744091
+Bug 1426728 - Don't cache the event target of pointer events when they are generated from touch. r=smaug.
+
+The event targets of touch events are not necessarily to be the same as their corresponding pointer events. So we don't have to cache the event target of pointer events when they are generated from touch.
+
+MozReview-Commit-ID: 9Gd6ion7NXf
+
+diff --git a/dom/events/PointerEventHandler.cpp b/dom/events/PointerEventHandler.cpp
+--- a/dom/events/PointerEventHandler.cpp
++++ b/dom/events/PointerEventHandler.cpp
+@@ -577,26 +577,25 @@ PointerEventHandler::DispatchPointerFrom
+         nsIFrame* frame = content->GetPrimaryFrame();
+         shell = PresShell::GetShellForEventTarget(frame, content);
+         if (!shell) {
+           continue;
+         }
+ 
+         PreHandlePointerEventsPreventDefault(&event, aEvent);
+         shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
+-                                     aTargetContent);
++                                     nullptr);
+         PostHandlePointerEventsPreventDefault(&event, aEvent);
+       } else {
+         // We didn't hit test for other touch events. Spec doesn't mention that
+         // all pointer events should be dispatched to the same target as their
+         // corresponding touch events. Call PresShell::HandleEvent so that we do
+         // hit test for pointer events.
+         PreHandlePointerEventsPreventDefault(&event, aEvent);
+-        shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
+-                           aTargetContent);
++        shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus);
+         PostHandlePointerEventsPreventDefault(&event, aEvent);
+       }
+     }
+   }
+ }
+ 
+ /* static */ uint16_t
+ PointerEventHandler::GetPointerType(uint32_t aPointerId)
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -6888,18 +6888,17 @@ PresShell::GetShellForTouchEvent(WidgetG
+   }
+   return shell;
+ }
+ 
+ nsresult
+ PresShell::HandleEvent(nsIFrame* aFrame,
+                        WidgetGUIEvent* aEvent,
+                        bool aDontRetargetEvents,
+-                       nsEventStatus* aEventStatus,
+-                       nsIContent** aTargetContent)
++                       nsEventStatus* aEventStatus)
+ {
+ #ifdef MOZ_TASK_TRACER
+   Maybe<AutoSourceEvent> taskTracerEvent;
+   if (MOZ_UNLIKELY(IsStartLogging())) {
+     // Make touch events, mouse events and hardware key events to be
+     // the source events of TaskTracer, and originate the rest
+     // correlation tasks from here.
+     SourceEventType type = SourceEventType::Unknown;
+diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h
+--- a/layout/base/PresShell.h
++++ b/layout/base/PresShell.h
+@@ -229,18 +229,17 @@ public:
+ 
+   //nsIViewObserver interface
+ 
+   virtual void Paint(nsView* aViewToPaint, const nsRegion& aDirtyRegion,
+                      uint32_t aFlags) override;
+   virtual nsresult HandleEvent(nsIFrame* aFrame,
+                                mozilla::WidgetGUIEvent* aEvent,
+                                bool aDontRetargetEvents,
+-                               nsEventStatus* aEventStatus,
+-                               nsIContent** aTargetContent = nullptr) override;
++                               nsEventStatus* aEventStatus) override;
+   virtual nsresult HandleDOMEventWithTarget(
+                                  nsIContent* aTargetContent,
+                                  mozilla::WidgetEvent* aEvent,
+                                  nsEventStatus* aStatus) override;
+   virtual nsresult HandleDOMEventWithTarget(nsIContent* aTargetContent,
+                                                         nsIDOMEvent* aEvent,
+                                                         nsEventStatus* aStatus) override;
+   virtual bool ShouldIgnoreInvalidation() override;
+diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h
+--- a/layout/base/nsIPresShell.h
++++ b/layout/base/nsIPresShell.h
+@@ -1441,18 +1441,17 @@ public:
+     /* Sync-decode images. */
+     PAINT_SYNC_DECODE_IMAGES = 0x04
+   };
+   virtual void Paint(nsView* aViewToPaint, const nsRegion& aDirtyRegion,
+                      uint32_t aFlags) = 0;
+   virtual nsresult HandleEvent(nsIFrame* aFrame,
+                                mozilla::WidgetGUIEvent* aEvent,
+                                bool aDontRetargetEvents,
+-                               nsEventStatus* aEventStatus,
+-                               nsIContent** aTargetContent = nullptr) = 0;
++                               nsEventStatus* aEventStatus) = 0;
+   virtual bool ShouldIgnoreInvalidation() = 0;
+   /**
+    * Notify that we're going to call Paint with PAINT_LAYERS
+    * on the pres shell for a widget (which might not be this one, since
+    * WillPaint is called on all presshells in the same toplevel window as the
+    * painted widget). This is issued at a time when it's safe to modify
+    * widget geometry.
+    */
+

+ 31 - 0
mozilla-release/patches/1426868-59a1.patch

@@ -0,0 +1,31 @@
+# HG changeset patch
+# User Samuel Thibault <samuel.thibault>
+# Date 1514009700 -7200
+# Node ID 4cdac2f62179db0ca9b89cf1b45c6201c3dc526a
+# Parent  6ad8b5129333eba823787614af6015a2fefc18a6
+Bug 1426868 - Check that child document still exists after parent processing r=surkov
+
+diff --git a/accessible/base/NotificationController.cpp b/accessible/base/NotificationController.cpp
+--- a/accessible/base/NotificationController.cpp
++++ b/accessible/base/NotificationController.cpp
+@@ -630,16 +630,19 @@ NotificationController::WillRefresh(mozi
+       mPresShell->IsReflowInterrupted()) {
+     return;
+   }
+ 
+   // Process parent's notifications before ours, to get proper ordering between
+   // e.g. tab event and content event.
+   if (WaitingForParent()) {
+     mDocument->ParentDocument()->mNotificationController->WillRefresh(aTime);
++    if (!mDocument) {
++      return;
++    }
+   }
+ 
+   // Any generic notifications should be queued if we're processing content
+   // insertions or generic notifications.
+   mObservingState = eRefreshProcessingForUpdate;
+ 
+   // Initial accessible tree construction.
+   if (!mDocument->HasLoadState(DocAccessible::eTreeConstructed)) {
+

+ 11 - 10
mozilla-release/patches/1428685-59a1.patch

@@ -2,7 +2,7 @@
 # User Jessica Jong <jjong@mozilla.com>
 # User Jessica Jong <jjong@mozilla.com>
 # Date 1516095720 -28800
 # Date 1516095720 -28800
 # Node ID bdd0bea249e3ded6e7e9a263ee002205ec30abf9
 # Node ID bdd0bea249e3ded6e7e9a263ee002205ec30abf9
-# Parent  7efe35de5bc5f9062761f6de886954a2f7a435fd
+# Parent  4f951bff7920aa1a3e189cd1e94aec3a6473ebf3
 Bug 1428685 - Use dom.webcomponents.shadowdom.enabled pref for Shadow DOM. r=smaug
 Bug 1428685 - Use dom.webcomponents.shadowdom.enabled pref for Shadow DOM. r=smaug
 
 
 Most of the Shadow DOM related code are behind "dom.webcomponents.enabled" and
 Most of the Shadow DOM related code are behind "dom.webcomponents.enabled" and
@@ -272,7 +272,7 @@ diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtest
  pref(dom.IntersectionObserver.enabled,true) load 1332939.html
  pref(dom.IntersectionObserver.enabled,true) load 1332939.html
  pref(dom.webcomponents.customelements.enabled,true) load 1341693.html
  pref(dom.webcomponents.customelements.enabled,true) load 1341693.html
  load 1352453.html
  load 1352453.html
-@@ -228,15 +228,15 @@ load 1383478.html
+@@ -228,16 +228,16 @@ load 1383478.html
  load 1383780.html
  load 1383780.html
  pref(clipboard.autocopy,true) load 1385272-1.html
  pref(clipboard.autocopy,true) load 1385272-1.html
  load 1393806.html
  load 1393806.html
@@ -287,9 +287,10 @@ diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtest
  load 1411473.html
  load 1411473.html
 -pref(dom.webcomponents.enabled,false) load 1422931.html
 -pref(dom.webcomponents.enabled,false) load 1422931.html
 -pref(dom.webcomponents.enabled,true) load 1419799.html
 -pref(dom.webcomponents.enabled,true) load 1419799.html
--pref(dom.webcomponents.enabled,true) load 1428053.html
 +pref(dom.webcomponents.shadowdom.enabled,false) load 1422931.html
 +pref(dom.webcomponents.shadowdom.enabled,false) load 1422931.html
 +pref(dom.webcomponents.shadowdom.enabled,true) load 1419799.html
 +pref(dom.webcomponents.shadowdom.enabled,true) load 1419799.html
+ skip-if(!browserIsRemote) pref(dom.webcomponents.customelements.enabled,true) pref(dom.disable_open_during_load,false) load 1419902.html # skip on non e10s loads, Bug 1419902
+-pref(dom.webcomponents.enabled,true) load 1428053.html
 +pref(dom.webcomponents.shadowdom.enabled,true) load 1428053.html
 +pref(dom.webcomponents.shadowdom.enabled,true) load 1428053.html
  pref(layout.css.resizeobserver.enabled,true) load 1555786.html
  pref(layout.css.resizeobserver.enabled,true) load 1555786.html
 diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
 diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
@@ -338,7 +339,7 @@ diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
 diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
 diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
 --- a/dom/base/nsContentUtils.h
 --- a/dom/base/nsContentUtils.h
 +++ b/dom/base/nsContentUtils.h
 +++ b/dom/base/nsContentUtils.h
-@@ -3141,17 +3141,17 @@ public:
+@@ -3139,17 +3139,17 @@ public:
                                          uint64_t* aRequestContextID);
                                          uint64_t* aRequestContextID);
  
  
    static nsresult
    static nsresult
@@ -444,7 +445,7 @@ diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
 diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h
 diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h
 --- a/dom/base/nsDocument.h
 --- a/dom/base/nsDocument.h
 +++ b/dom/base/nsDocument.h
 +++ b/dom/base/nsDocument.h
-@@ -624,21 +624,20 @@ public:
+@@ -625,21 +625,20 @@ public:
    virtual void UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
    virtual void UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
    virtual void ResolveScheduledSVGPresAttrs() override;
    virtual void ResolveScheduledSVGPresAttrs() override;
    bool IsSynthesized();
    bool IsSynthesized();
@@ -1388,14 +1389,14 @@ diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/c
 diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
 diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
 --- a/modules/libpref/init/all.js
 --- a/modules/libpref/init/all.js
 +++ b/modules/libpref/init/all.js
 +++ b/modules/libpref/init/all.js
-@@ -1434,17 +1434,17 @@ pref("dom.event.contextmenu.enabled",   
+@@ -1432,17 +1432,17 @@ pref("privacy.trackingprotection.lower_n
+ pref("privacy.trackingprotection.lower_network_priority", false);
+ #endif
+ 
+ pref("dom.event.contextmenu.enabled",       true);
  pref("dom.event.clipboardevents.enabled",   true);
  pref("dom.event.clipboardevents.enabled",   true);
  pref("dom.event.highrestimestamp.enabled",  true);
  pref("dom.event.highrestimestamp.enabled",  true);
- #ifdef NIGHTLY_BUILD
  pref("dom.event.coalesce_mouse_move",       true);
  pref("dom.event.coalesce_mouse_move",       true);
- #else
- pref("dom.event.coalesce_mouse_move",       false);
- #endif
  
  
 -pref("dom.webcomponents.enabled",           false);
 -pref("dom.webcomponents.enabled",           false);
 +pref("dom.webcomponents.shadowdom.enabled", false);
 +pref("dom.webcomponents.shadowdom.enabled", false);

+ 33 - 0
mozilla-release/patches/1432870-60a1.patch

@@ -0,0 +1,33 @@
+# HG changeset patch
+# User Gijs Kruitbosch <gijskruitbosch@gmail.com>
+# Date 1517314028 0
+# Node ID 54491805113acdce065ed6164b524acc411986ef
+# Parent  ccef77b1cf96ca3e0a6a234879b4aa679130f6ab
+Bug 1432870, r=bz
+
+diff --git a/chrome/nsChromeRegistry.cpp b/chrome/nsChromeRegistry.cpp
+--- a/chrome/nsChromeRegistry.cpp
++++ b/chrome/nsChromeRegistry.cpp
+@@ -230,16 +230,22 @@ nsChromeRegistry::Canonify(nsIURL* aChro
+     }
+     aChromeURL->SetPath(path);
+   }
+   else {
+     // prevent directory traversals ("..")
+     // path is already unescaped once, but uris can get unescaped twice
+     const char* pos = path.BeginReading();
+     const char* end = path.EndReading();
++    // Must start with [a-zA-Z0-9].
++    if (!('a' <= *pos && *pos <= 'z') &&
++        !('A' <= *pos && *pos <= 'Z') &&
++        !('0' <= *pos && *pos <= '9')) {
++      return NS_ERROR_DOM_BAD_URI;
++    }
+     while (pos < end) {
+       switch (*pos) {
+         case ':':
+           return NS_ERROR_DOM_BAD_URI;
+         case '.':
+           if (pos[1] == '.')
+             return NS_ERROR_DOM_BAD_URI;
+           break;

+ 1496 - 0
mozilla-release/patches/1433671-2-1444375-61a1.patch

@@ -0,0 +1,1496 @@
+# HG changeset patch
+# User Emilio Cobos Alvarez <emilio@crisal.io>
+# Date 1517254517 -3600
+# Node ID 69a882fbd02a50b9ac6ed917a96c42cf22f410e7
+# Parent  4869df66bb1641aa97173293074abc11d7dc8f30
+Bug 1433671: Add MOZ_CAN_RUN_SCRIPT annotations to AccessibleCaret and other stuff. r=bz
+
+MozReview-Commit-ID: Js0CF7WQM73
+
+diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
+--- a/dom/base/nsContentUtils.h
++++ b/dom/base/nsContentUtils.h
+@@ -2898,16 +2898,17 @@ public:
+                                int32_t aModifiers,
+                                uint32_t aAdditionalFlags,
+                                bool* aDefaultActionTaken);
+ 
+   /**
+    * Synthesize a mouse event to the given widget
+    * (see nsIDOMWindowUtils.sendMouseEvent).
+    */
++  MOZ_CAN_RUN_SCRIPT
+   static nsresult SendMouseEvent(const nsCOMPtr<nsIPresShell>& aPresShell,
+                                  const nsAString& aType,
+                                  float aX,
+                                  float aY,
+                                  int32_t aButton,
+                                  int32_t aButtons,
+                                  int32_t aClickCount,
+                                  int32_t aModifiers,
+diff --git a/dom/base/nsDOMWindowUtils.h b/dom/base/nsDOMWindowUtils.h
+--- a/dom/base/nsDOMWindowUtils.h
++++ b/dom/base/nsDOMWindowUtils.h
+@@ -79,16 +79,18 @@ protected:
+   nsIWidget* GetWidgetForElement(nsIDOMElement* aElement);
+ 
+   nsIPresShell* GetPresShell();
+   nsPresContext* GetPresContext();
+   nsIDocument* GetDocument();
+   mozilla::layers::LayerTransactionChild* GetLayerTransaction();
+   mozilla::layers::WebRenderBridgeChild* GetWebRenderBridge();
+ 
++  // Until callers are annotated.
++  MOZ_CAN_RUN_SCRIPT_BOUNDARY
+   NS_IMETHOD SendMouseEventCommon(const nsAString& aType,
+                                   float aX,
+                                   float aY,
+                                   int32_t aButton,
+                                   int32_t aClickCount,
+                                   int32_t aModifiers,
+                                   bool aIgnoreRootScrollFrame,
+                                   float aPressure,
+diff --git a/dom/base/nsFocusManager.h b/dom/base/nsFocusManager.h
+--- a/dom/base/nsFocusManager.h
++++ b/dom/base/nsFocusManager.h
+@@ -269,16 +269,18 @@ protected:
+    * switching focus to a sibling window.
+    *
+    * aIsLeavingDocument should be set to true if the document/window is being
+    * blurred as well. Document/window blur events will be fired. It should be
+    * false if an element is the same document is about to be focused.
+    *
+    * If aAdjustWidget is false, don't change the widget focus state.
+    */
++  // MOZ_CAN_RUN_SCRIPT_BOUNDARY for now, until we annotate callers.
++  MOZ_CAN_RUN_SCRIPT_BOUNDARY
+   bool Blur(nsPIDOMWindowOuter* aWindowToClear,
+             nsPIDOMWindowOuter* aAncestorWindowToFocus,
+             bool aIsLeavingDocument,
+             bool aAdjustWidget,
+             nsIContent* aContentToFocus = nullptr);
+ 
+   /**
+    * Focus an element in the active window and child frame.
+diff --git a/dom/events/PointerEventHandler.h b/dom/events/PointerEventHandler.h
+--- a/dom/events/PointerEventHandler.h
++++ b/dom/events/PointerEventHandler.h
+@@ -133,16 +133,17 @@ public:
+    * We add mPreventMouseEventByContent flag in PointerInfo to represent the
+    * active pointer won't firing compatible mouse events. It's set to true when
+    * content preventDefault on pointerdown
+    */
+   static void PostHandlePointerEventsPreventDefault(
+                 WidgetPointerEvent* aPointerEvent,
+                 WidgetGUIEvent* aMouseOrTouchEvent);
+ 
++  MOZ_CAN_RUN_SCRIPT
+   static void DispatchPointerFromMouseOrTouch(PresShell* aShell,
+                                               nsIFrame* aFrame,
+                                               nsIContent* aContent,
+                                               WidgetGUIEvent* aEvent,
+                                               bool aDontRetargetEvents,
+                                               nsEventStatus* aStatus,
+                                               nsIContent** aTargetContent);
+ 
+diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h
+--- a/dom/ipc/TabChild.h
++++ b/dom/ipc/TabChild.h
+@@ -351,16 +351,17 @@ public:
+   RecvUpdateDimensions(const mozilla::dom::DimensionInfo& aDimensionInfo) override;
+   virtual mozilla::ipc::IPCResult
+   RecvSizeModeChanged(const nsSizeMode& aSizeMode) override;
+ 
+   mozilla::ipc::IPCResult RecvActivate();
+ 
+   mozilla::ipc::IPCResult RecvDeactivate();
+ 
++  MOZ_CAN_RUN_SCRIPT
+   virtual mozilla::ipc::IPCResult RecvMouseEvent(const nsString& aType,
+                                                  const float& aX,
+                                                  const float& aY,
+                                                  const int32_t& aButton,
+                                                  const int32_t& aClickCount,
+                                                  const int32_t& aModifiers,
+                                                  const bool& aIgnoreRootScrollFrame) override;
+ 
+@@ -634,22 +635,24 @@ public:
+                   PRenderFrameChild* aRenderFrame,
+                   const ShowInfo& aShowInfo);
+ 
+   void ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
+                                  uint64_t aInputBlockId,
+                                  bool aPreventDefault) const;
+   void SetTargetAPZC(uint64_t aInputBlockId,
+                     const nsTArray<ScrollableLayerGuid>& aTargets) const;
++  MOZ_CAN_RUN_SCRIPT
+   mozilla::ipc::IPCResult RecvHandleTap(const layers::GeckoContentController::TapType& aType,
+                                         const LayoutDevicePoint& aPoint,
+                                         const Modifiers& aModifiers,
+                                         const ScrollableLayerGuid& aGuid,
+                                         const uint64_t& aInputBlockId) override;
+ 
++  MOZ_CAN_RUN_SCRIPT
+   mozilla::ipc::IPCResult
+   RecvNormalPriorityHandleTap(const layers::GeckoContentController::TapType& aType,
+                               const LayoutDevicePoint& aPoint,
+                               const Modifiers& aModifiers,
+                               const ScrollableLayerGuid& aGuid,
+                               const uint64_t& aInputBlockId) override;
+ 
+   void SetAllowedTouchBehavior(uint64_t aInputBlockId,
+diff --git a/gfx/layers/apz/util/APZCCallbackHelper.h b/gfx/layers/apz/util/APZCCallbackHelper.h
+--- a/gfx/layers/apz/util/APZCCallbackHelper.h
++++ b/gfx/layers/apz/util/APZCCallbackHelper.h
+@@ -109,16 +109,17 @@ public:
+                                                        uint64_t aTime,
+                                                        const LayoutDevicePoint& aRefPoint,
+                                                        Modifiers aModifiers,
+                                                        int32_t aClickCount,
+                                                        nsIWidget* aWidget);
+ 
+     /* Dispatch a mouse event with the given parameters.
+      * Return whether or not any listeners have called preventDefault on the event. */
++    MOZ_CAN_RUN_SCRIPT
+     static bool DispatchMouseEvent(const nsCOMPtr<nsIPresShell>& aPresShell,
+                                    const nsString& aType,
+                                    const CSSPoint& aPoint,
+                                    int32_t aButton,
+                                    int32_t aClickCount,
+                                    int32_t aModifiers,
+                                    bool aIgnoreRootScrollFrame,
+                                    unsigned short aInputSourceArg,
+diff --git a/gfx/layers/apz/util/APZEventState.h b/gfx/layers/apz/util/APZEventState.h
+--- a/gfx/layers/apz/util/APZEventState.h
++++ b/gfx/layers/apz/util/APZEventState.h
+@@ -49,22 +49,24 @@ public:
+ 
+   NS_INLINE_DECL_REFCOUNTING(APZEventState);
+ 
+   void ProcessSingleTap(const CSSPoint& aPoint,
+                         const CSSToLayoutDeviceScale& aScale,
+                         Modifiers aModifiers,
+                         const ScrollableLayerGuid& aGuid,
+                         int32_t aClickCount);
++  MOZ_CAN_RUN_SCRIPT
+   void ProcessLongTap(const nsCOMPtr<nsIPresShell>& aUtils,
+                       const CSSPoint& aPoint,
+                       const CSSToLayoutDeviceScale& aScale,
+                       Modifiers aModifiers,
+                       const ScrollableLayerGuid& aGuid,
+                       uint64_t aInputBlockId);
++  MOZ_CAN_RUN_SCRIPT
+   void ProcessLongTapUp(const nsCOMPtr<nsIPresShell>& aPresShell,
+                         const CSSPoint& aPoint,
+                         const CSSToLayoutDeviceScale& aScale,
+                         Modifiers aModifiers);
+   void ProcessTouchEvent(const WidgetTouchEvent& aEvent,
+                          const ScrollableLayerGuid& aGuid,
+                          uint64_t aInputBlockId,
+                          nsEventStatus aApzResponse,
+@@ -77,16 +79,17 @@ public:
+                          uint64_t aInputBlockId);
+   void ProcessAPZStateChange(ViewID aViewId,
+                              APZStateChange aChange,
+                              int aArg);
+   void ProcessClusterHit();
+ private:
+   ~APZEventState();
+   bool SendPendingTouchPreventedResponse(bool aPreventDefault);
++  MOZ_CAN_RUN_SCRIPT
+   bool FireContextmenuEvents(const nsCOMPtr<nsIPresShell>& aPresShell,
+                              const CSSPoint& aPoint,
+                              const CSSToLayoutDeviceScale& aScale,
+                              Modifiers aModifiers,
+                              const nsCOMPtr<nsIWidget>& aWidget);
+   already_AddRefed<nsIWidget> GetWidget() const;
+   already_AddRefed<nsIContent> GetTouchRollup() const;
+ private:
+diff --git a/gfx/layers/apz/util/ChromeProcessController.h b/gfx/layers/apz/util/ChromeProcessController.h
+--- a/gfx/layers/apz/util/ChromeProcessController.h
++++ b/gfx/layers/apz/util/ChromeProcessController.h
+@@ -44,16 +44,17 @@ public:
+   ~ChromeProcessController();
+   virtual void Destroy() override;
+ 
+   // GeckoContentController interface
+   virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override;
+   virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
+   virtual bool IsRepaintThread() override;
+   virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
++  MOZ_CAN_RUN_SCRIPT
+   virtual void HandleTap(TapType aType,
+                          const mozilla::LayoutDevicePoint& aPoint,
+                          Modifiers aModifiers,
+                          const ScrollableLayerGuid& aGuid,
+                          uint64_t aInputBlockId) override;
+   virtual void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+                                   const ScrollableLayerGuid& aGuid,
+                                   LayoutDeviceCoord aSpanChange,
+diff --git a/layout/base/AccessibleCaretEventHub.cpp b/layout/base/AccessibleCaretEventHub.cpp
+--- a/layout/base/AccessibleCaretEventHub.cpp
++++ b/layout/base/AccessibleCaretEventHub.cpp
+@@ -39,18 +39,20 @@ NS_IMPL_ISUPPORTS(AccessibleCaretEventHu
+ // NoActionState
+ //
+ class AccessibleCaretEventHub::NoActionState
+   : public AccessibleCaretEventHub::State
+ {
+ public:
+   const char* Name() const override { return "NoActionState"; }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   nsEventStatus OnPress(AccessibleCaretEventHub* aContext,
+-                        const nsPoint& aPoint, int32_t aTouchId,
++                        const nsPoint& aPoint,
++                        int32_t aTouchId,
+                         EventClassID aEventClass) override
+   {
+     nsEventStatus rv = nsEventStatus_eIgnore;
+ 
+     if (NS_SUCCEEDED(aContext->mManager->PressCaret(aPoint, aEventClass))) {
+       aContext->SetState(aContext->PressCaretState());
+       rv = nsEventStatus_eConsumeNoDefault;
+     } else {
+@@ -58,40 +60,46 @@ public:
+     }
+ 
+     aContext->mPressPoint = aPoint;
+     aContext->mActiveTouchId = aTouchId;
+ 
+     return rv;
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnScrollStart(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->mManager->OnScrollStart();
+     aContext->SetState(aContext->ScrollState());
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnScrollPositionChanged(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->mManager->OnScrollPositionChanged();
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnSelectionChanged(AccessibleCaretEventHub* aContext,
+-                          nsIDOMDocument* aDoc, nsISelection* aSel,
++                          nsIDOMDocument* aDoc,
++                          nsISelection* aSel,
+                           int16_t aReason) override
+   {
+     aContext->mManager->OnSelectionChanged(aDoc, aSel, aReason);
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnBlur(AccessibleCaretEventHub* aContext,
+               bool aIsLeavingDocument) override
+   {
+     aContext->mManager->OnBlur();
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnReflow(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->mManager->OnReflow();
+   }
+ 
+   void Enter(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->mPressPoint = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+@@ -103,28 +111,30 @@ public:
+ // PressCaretState: Always consume the event since we've pressed on the caret.
+ //
+ class AccessibleCaretEventHub::PressCaretState
+   : public AccessibleCaretEventHub::State
+ {
+ public:
+   const char* Name() const override { return "PressCaretState"; }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   nsEventStatus OnMove(AccessibleCaretEventHub* aContext,
+                        const nsPoint& aPoint) override
+   {
+     if (aContext->MoveDistanceIsLarge(aPoint)) {
+       if (NS_SUCCEEDED(aContext->mManager->DragCaret(aPoint))) {
+         aContext->SetState(aContext->DragCaretState());
+       }
+     }
+ 
+     return nsEventStatus_eConsumeNoDefault;
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   nsEventStatus OnRelease(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->mManager->ReleaseCaret();
+     aContext->mManager->TapCaret(aContext->mPressPoint);
+     aContext->SetState(aContext->NoActionState());
+ 
+     return nsEventStatus_eConsumeNoDefault;
+   }
+@@ -140,24 +150,26 @@ public:
+ // DragCaretState: Always consume the event since we've pressed on the caret.
+ //
+ class AccessibleCaretEventHub::DragCaretState
+   : public AccessibleCaretEventHub::State
+ {
+ public:
+   const char* Name() const override { return "DragCaretState"; }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   nsEventStatus OnMove(AccessibleCaretEventHub* aContext,
+                        const nsPoint& aPoint) override
+   {
+     aContext->mManager->DragCaret(aPoint);
+ 
+     return nsEventStatus_eConsumeNoDefault;
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   nsEventStatus OnRelease(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->mManager->ReleaseCaret();
+     aContext->SetState(aContext->NoActionState());
+ 
+     return nsEventStatus_eConsumeNoDefault;
+   }
+ };
+@@ -183,46 +195,52 @@ public:
+ 
+   nsEventStatus OnRelease(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->SetState(aContext->NoActionState());
+ 
+     return nsEventStatus_eIgnore;
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   nsEventStatus OnLongTap(AccessibleCaretEventHub* aContext,
+                           const nsPoint& aPoint) override
+   {
+     aContext->SetState(aContext->LongTapState());
+ 
+     return aContext->GetState()->OnLongTap(aContext, aPoint);
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnScrollStart(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->mManager->OnScrollStart();
+     aContext->SetState(aContext->ScrollState());
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnBlur(AccessibleCaretEventHub* aContext,
+               bool aIsLeavingDocument) override
+   {
+     aContext->mManager->OnBlur();
+     if (aIsLeavingDocument) {
+       aContext->SetState(aContext->NoActionState());
+     }
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnSelectionChanged(AccessibleCaretEventHub* aContext,
+-                          nsIDOMDocument* aDoc, nsISelection* aSel,
++                          nsIDOMDocument* aDoc,
++                          nsISelection* aSel,
+                           int16_t aReason) override
+   {
+     aContext->mManager->OnSelectionChanged(aDoc, aSel, aReason);
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnReflow(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->mManager->OnReflow();
+   }
+ 
+   void Enter(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->LaunchLongTapInjector();
+@@ -238,26 +256,29 @@ public:
+ // ScrollState
+ //
+ class AccessibleCaretEventHub::ScrollState
+   : public AccessibleCaretEventHub::State
+ {
+ public:
+   const char* Name() const override { return "ScrollState"; }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnScrollEnd(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->SetState(aContext->PostScrollState());
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnScrollPositionChanged(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->mManager->OnScrollPositionChanged();
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnBlur(AccessibleCaretEventHub* aContext,
+               bool aIsLeavingDocument) override
+   {
+     aContext->mManager->OnBlur();
+     if (aIsLeavingDocument) {
+       aContext->SetState(aContext->NoActionState());
+     }
+   }
+@@ -320,16 +341,17 @@ public:
+ // LongTapState
+ //
+ class AccessibleCaretEventHub::LongTapState
+   : public AccessibleCaretEventHub::State
+ {
+ public:
+   const char* Name() const override { return "LongTapState"; }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   nsEventStatus OnLongTap(AccessibleCaretEventHub* aContext,
+                           const nsPoint& aPoint) override
+   {
+     // In general text selection is lower-priority than the context menu. If
+     // we consume this long-press event, then it prevents the context menu from
+     // showing up on desktop Firefox (because that happens on long-tap-up, if
+     // the long-tap was not cancelled). So we return eIgnore instead.
+     aContext->mManager->SelectWordOrShortcut(aPoint);
+@@ -340,22 +362,24 @@ public:
+   {
+     aContext->SetState(aContext->NoActionState());
+ 
+     // Do not consume the release since the press is not consumed in
+     // PressNoCaretState either.
+     return nsEventStatus_eIgnore;
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnScrollStart(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->mManager->OnScrollStart();
+     aContext->SetState(aContext->ScrollState());
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void OnReflow(AccessibleCaretEventHub* aContext) override
+   {
+     aContext->mManager->OnReflow();
+   }
+ };
+ 
+ // -----------------------------------------------------------------------------
+ // Implementation of AccessibleCaretEventHub methods
+diff --git a/layout/base/AccessibleCaretEventHub.h b/layout/base/AccessibleCaretEventHub.h
+--- a/layout/base/AccessibleCaretEventHub.h
++++ b/layout/base/AccessibleCaretEventHub.h
+@@ -55,38 +55,56 @@ class WidgetTouchEvent;
+ // State transition diagram:
+ // https://hg.mozilla.org/mozilla-central/raw-file/default/layout/base/doc/AccessibleCaretEventHubStates.png
+ // Source code of the diagram:
+ // https://hg.mozilla.org/mozilla-central/file/default/layout/base/doc/AccessibleCaretEventHubStates.dot
+ //
+ // Please see the wiki page for more information.
+ // https://wiki.mozilla.org/AccessibleCaret
+ //
+-class AccessibleCaretEventHub : public nsIReflowObserver,
+-                                public nsIScrollObserver,
+-                                public nsISelectionListener,
+-                                public nsSupportsWeakReference
++class AccessibleCaretEventHub
++  : public nsIReflowObserver
++  , public nsIScrollObserver
++  , public nsISelectionListener
++  , public nsSupportsWeakReference
+ {
+ public:
+   explicit AccessibleCaretEventHub(nsIPresShell* aPresShell);
+   void Init();
+   void Terminate();
+ 
++  MOZ_CAN_RUN_SCRIPT
+   nsEventStatus HandleEvent(WidgetEvent* aEvent);
+ 
+   // Call this function to notify the blur event happened.
++  MOZ_CAN_RUN_SCRIPT
+   void NotifyBlur(bool aIsLeavingDocument);
+ 
+   NS_DECL_ISUPPORTS
+-  NS_DECL_NSIREFLOWOBSERVER
+-  NS_DECL_NSISELECTIONLISTENER
++
++  // nsIReflowObserver
++  MOZ_CAN_RUN_SCRIPT
++  NS_IMETHOD Reflow(DOMHighResTimeStamp start,
++                    DOMHighResTimeStamp end) final;
++  MOZ_CAN_RUN_SCRIPT
++  NS_IMETHOD ReflowInterruptible(DOMHighResTimeStamp start,
++                                 DOMHighResTimeStamp end) final;
++
++  // nsISelectionListener
++  MOZ_CAN_RUN_SCRIPT
++  NS_IMETHOD NotifySelectionChanged(nsIDOMDocument* doc,
++                                    nsISelection* sel,
++                                    int16_t reason) final;
+ 
+   // Override nsIScrollObserver methods.
++  MOZ_CAN_RUN_SCRIPT
+   virtual void ScrollPositionChanged() override;
++  MOZ_CAN_RUN_SCRIPT
+   virtual void AsyncPanZoomStarted() override;
++  MOZ_CAN_RUN_SCRIPT
+   virtual void AsyncPanZoomStopped() override;
+ 
+   // Base state
+   class State;
+   State* GetState() const;
+ 
+ protected:
+   virtual ~AccessibleCaretEventHub() = default;
+@@ -108,32 +126,39 @@ protected:
+   MOZ_DECL_STATE_CLASS_GETTER(DragCaretState)
+   MOZ_DECL_STATE_CLASS_GETTER(PressNoCaretState)
+   MOZ_DECL_STATE_CLASS_GETTER(ScrollState)
+   MOZ_DECL_STATE_CLASS_GETTER(PostScrollState)
+   MOZ_DECL_STATE_CLASS_GETTER(LongTapState)
+ 
+   void SetState(State* aState);
+ 
++  MOZ_CAN_RUN_SCRIPT
+   nsEventStatus HandleMouseEvent(WidgetMouseEvent* aEvent);
++  MOZ_CAN_RUN_SCRIPT
+   nsEventStatus HandleTouchEvent(WidgetTouchEvent* aEvent);
++  MOZ_CAN_RUN_SCRIPT
+   nsEventStatus HandleKeyboardEvent(WidgetKeyboardEvent* aEvent);
+ 
+   virtual nsPoint GetTouchEventPosition(WidgetTouchEvent* aEvent,
+                                         int32_t aIdentifier) const;
+   virtual nsPoint GetMouseEventPosition(WidgetMouseEvent* aEvent) const;
+ 
+   bool MoveDistanceIsLarge(const nsPoint& aPoint) const;
+ 
+   void LaunchLongTapInjector();
+   void CancelLongTapInjector();
++
++  MOZ_CAN_RUN_SCRIPT
+   static void FireLongTap(nsITimer* aTimer, void* aAccessibleCaretEventHub);
+ 
+   void LaunchScrollEndInjector();
+   void CancelScrollEndInjector();
++
++  MOZ_CAN_RUN_SCRIPT
+   static void FireScrollEnd(nsITimer* aTimer, void* aAccessibleCaretEventHub);
+ 
+   // Member variables
+   State* mState = NoActionState();
+ 
+   // Will be set to nullptr in Terminate().
+   nsIPresShell* MOZ_NON_OWNING_REF mPresShell = nullptr;
+ 
+diff --git a/layout/base/AccessibleCaretManager.cpp b/layout/base/AccessibleCaretManager.cpp
+--- a/layout/base/AccessibleCaretManager.cpp
++++ b/layout/base/AccessibleCaretManager.cpp
+@@ -542,28 +542,24 @@ AccessibleCaretManager::TapCaret(const n
+   }
+ 
+   return rv;
+ }
+ 
+ nsresult
+ AccessibleCaretManager::SelectWordOrShortcut(const nsPoint& aPoint)
+ {
+-  auto UpdateCaretsWithHapticFeedback = [this] {
+-    UpdateCarets();
+-    ProvideHapticFeedback();
+-  };
+-
+   // If the long-tap is landing on a pre-existing selection, don't replace
+   // it with a new one. Instead just return and let the context menu pop up
+   // on the pre-existing selection.
+   if (GetCaretMode() == CaretMode::Selection &&
+       GetSelection()->ContainsPoint(aPoint)) {
+     AC_LOG("%s: UpdateCarets() for current selection", __FUNCTION__);
+-    UpdateCaretsWithHapticFeedback();
++    UpdateCarets();
++    ProvideHapticFeedback();
+     return NS_OK;
+   }
+ 
+   if (!mPresShell) {
+     return NS_ERROR_UNEXPECTED;
+   }
+ 
+   nsIFrame* rootFrame = mPresShell->GetRootFrame();
+@@ -601,17 +597,18 @@ AccessibleCaretManager::SelectWordOrShor
+       !HasNonEmptyTextContent(newFocusEditingHost)) {
+     ChangeFocusToOrClearOldFocus(focusableFrame);
+ 
+     if (sCaretShownWhenLongTappingOnEmptyContent) {
+       mFirstCaret->SetAppearance(Appearance::Normal);
+     }
+     // We need to update carets to get correct information before dispatching
+     // CaretStateChangedEvent.
+-    UpdateCaretsWithHapticFeedback();
++    UpdateCarets();
++    ProvideHapticFeedback();
+     DispatchCaretStateChangedEvent(CaretChangedReason::Longpressonemptycontent);
+     return NS_OK;
+   }
+ 
+   bool selectable = ptFrame->IsSelectable(nullptr);
+ 
+ #ifdef DEBUG_FRAME_DUMP
+   AC_LOG("%s: %s %s selectable.", __FUNCTION__, ptFrame->ListTag().get(),
+@@ -635,17 +632,18 @@ AccessibleCaretManager::SelectWordOrShor
+   ChangeFocusToOrClearOldFocus(focusableFrame);
+   if (!ptFrame.IsAlive()) {
+     // Cannot continue because ptFrame died.
+     return NS_ERROR_FAILURE;
+   }
+ 
+   // Then try select a word under point.
+   nsresult rv = SelectWord(ptFrame, ptInFrame);
+-  UpdateCaretsWithHapticFeedback();
++  UpdateCarets();
++  ProvideHapticFeedback();
+ 
+   return rv;
+ }
+ 
+ void
+ AccessibleCaretManager::OnScrollStart()
+ {
+   AC_LOG("%s", __FUNCTION__);
+diff --git a/layout/base/AccessibleCaretManager.h b/layout/base/AccessibleCaretManager.h
+--- a/layout/base/AccessibleCaretManager.h
++++ b/layout/base/AccessibleCaretManager.h
+@@ -54,54 +54,67 @@ public:
+   // Called by AccessibleCaretEventHub to inform us that PresShell is destroyed.
+   void Terminate();
+ 
+   // The aPoint in the following public methods should be relative to root
+   // frame.
+ 
+   // Press caret on the given point. Return NS_OK if the point is actually on
+   // one of the carets.
+-  virtual nsresult PressCaret(const nsPoint& aPoint, EventClassID aEventClass);
++  MOZ_CAN_RUN_SCRIPT
++  virtual nsresult PressCaret(const nsPoint& aPoint,
++                              EventClassID aEventClass);
+ 
+   // Drag caret to the given point. It's required to call PressCaret()
+   // beforehand.
++  MOZ_CAN_RUN_SCRIPT
+   virtual nsresult DragCaret(const nsPoint& aPoint);
+ 
+   // Release caret from he previous press action. It's required to call
+   // PressCaret() beforehand.
++  MOZ_CAN_RUN_SCRIPT
+   virtual nsresult ReleaseCaret();
+ 
+   // A quick single tap on caret on given point without dragging.
++  MOZ_CAN_RUN_SCRIPT
+   virtual nsresult TapCaret(const nsPoint& aPoint);
+ 
+   // Select a word or bring up paste shortcut (if Gaia is listening) under the
+   // given point.
++  MOZ_CAN_RUN_SCRIPT
+   virtual nsresult SelectWordOrShortcut(const nsPoint& aPoint);
+ 
+   // Handle scroll-start event.
++  MOZ_CAN_RUN_SCRIPT
+   virtual void OnScrollStart();
+ 
+   // Handle scroll-end event.
++  MOZ_CAN_RUN_SCRIPT
+   virtual void OnScrollEnd();
+ 
+   // Handle ScrollPositionChanged from nsIScrollObserver. This might be called
+   // at anytime, not necessary between OnScrollStart and OnScrollEnd.
++  MOZ_CAN_RUN_SCRIPT
+   virtual void OnScrollPositionChanged();
+ 
+   // Handle reflow event from nsIReflowObserver.
++  MOZ_CAN_RUN_SCRIPT
+   virtual void OnReflow();
+ 
+   // Handle blur event from nsFocusManager.
++  MOZ_CAN_RUN_SCRIPT
+   virtual void OnBlur();
+ 
+   // Handle NotifySelectionChanged event from nsISelectionListener.
++  MOZ_CAN_RUN_SCRIPT
+   virtual nsresult OnSelectionChanged(nsIDOMDocument* aDoc,
+                                       nsISelection* aSel,
+                                       int16_t aReason);
+   // Handle key event.
++  MOZ_CAN_RUN_SCRIPT
+   virtual void OnKeyboardEvent();
+ 
+   // The canvas frame holding the accessible caret anonymous content elements
+   // was reconstructed, resulting in the content elements getting cloned.
+   virtual void OnFrameReconstruction();
+ 
+   // Update the manager with the last input source that was observed. This
+   // is used in part to determine if the carets should be shown or hidden.
+@@ -140,22 +153,28 @@ protected:
+   using UpdateCaretsHintSet = mozilla::EnumSet<UpdateCaretsHint>;
+ 
+   friend std::ostream& operator<<(std::ostream& aStream,
+                                   const UpdateCaretsHint& aResult);
+ 
+   // Update carets based on current selection status. This function will flush
+   // layout, so caller must ensure the PresShell is still valid after calling
+   // this method.
+-  void UpdateCarets(const UpdateCaretsHintSet& aHints = UpdateCaretsHint::Default);
++  MOZ_CAN_RUN_SCRIPT
++  void UpdateCarets(
++    const UpdateCaretsHintSet& aHints = UpdateCaretsHint::Default);
+ 
+   // Force hiding all carets regardless of the current selection status.
++  MOZ_CAN_RUN_SCRIPT
+   void HideCarets();
+ 
++  MOZ_CAN_RUN_SCRIPT
+   void UpdateCaretsForCursorMode(const UpdateCaretsHintSet& aHints);
++
++  MOZ_CAN_RUN_SCRIPT
+   void UpdateCaretsForSelectionMode(const UpdateCaretsHintSet& aHints);
+ 
+   // Provide haptic / touch feedback, primarily for select on longpress.
+   void ProvideHapticFeedback();
+ 
+   // Get the nearest enclosing focusable frame of aFrame.
+   // @return focusable frame if there is any; nullptr otherwise.
+   nsIFrame* GetFocusableFrame(nsIFrame* aFrame) const;
+@@ -200,17 +219,18 @@ protected:
+   void ClearMaintainedSelection() const;
+ 
+   // This method could kill the shell, so callers to methods that call
+   // FlushLayout should ensure the event hub that owns us is still alive.
+   //
+   // See the mRefCnt assertions in AccessibleCaretEventHub.
+   //
+   // Returns whether mPresShell we're holding is still valid.
+-  MOZ_MUST_USE bool FlushLayout();
++  MOZ_MUST_USE MOZ_CAN_RUN_SCRIPT
++  bool FlushLayout();
+ 
+   dom::Element* GetEditingHostForFrame(nsIFrame* aFrame) const;
+   dom::Selection* GetSelection() const;
+   already_AddRefed<nsFrameSelection> GetFrameSelection() const;
+   nsAutoString StringifiedSelection() const;
+ 
+   // Get the union of all the child frame scrollable overflow rects for aFrame,
+   // which is used as a helper function to restrict the area where the caret can
+@@ -256,16 +276,17 @@ protected:
+   // @param aOutOffset returns frame offset as well.
+   virtual bool IsCaretDisplayableInCursorMode(nsIFrame** aOutFrame = nullptr,
+                                               int32_t* aOutOffset = nullptr) const;
+ 
+   virtual bool HasNonEmptyTextContent(nsINode* aNode) const;
+ 
+   // This function will flush layout, so caller must ensure the PresShell is
+   // still valid after calling this method.
++  MOZ_CAN_RUN_SCRIPT
+   virtual void DispatchCaretStateChangedEvent(dom::CaretChangedReason aReason);
+ 
+   // ---------------------------------------------------------------------------
+   // Member variables
+   //
+   nscoord mOffsetYToCaretLogicalPosition = NS_UNCONSTRAINEDSIZE;
+ 
+   // AccessibleCaretEventHub owns us by a UniquePtr. When it's destroyed, we'll
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -7025,43 +7025,40 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+       // Prevent application crashes, in case damaged frame.
+       if (!frameKeeper.IsAlive()) {
+         NS_WARNING("Nothing to handle this event!");
+         return NS_OK;
+       }
+     }
+ 
+     // Only capture mouse events and pointer events.
+-    nsIContent* pointerCapturingContent =
++    nsCOMPtr<nsIContent> pointerCapturingContent =
+       PointerEventHandler::GetPointerCapturingContent(aEvent);
+ 
+     if (pointerCapturingContent) {
+-      nsIFrame* pointerCapturingFrame =
+-        pointerCapturingContent->GetPrimaryFrame();
+-
+-      if (!pointerCapturingFrame) {
++      frame = pointerCapturingContent->GetPrimaryFrame();
++
++      if (!frame) {
+         // Dispatch events to the capturing content even it's frame is
+         // destroyed.
+         PointerEventHandler::DispatchPointerFromMouseOrTouch(
+-          this, nullptr, pointerCapturingContent, aEvent, false, aEventStatus,
+-          nullptr);
+-
+-        PresShell* shell = GetShellForEventTarget(nullptr,
+-                                                  pointerCapturingContent);
++          this, nullptr, pointerCapturingContent, aEvent, false,
++          aEventStatus, nullptr);
++
++        RefPtr<PresShell> shell =
++          GetShellForEventTarget(nullptr, pointerCapturingContent);
+ 
+         if (!shell) {
+           // The capturing element could be changed when dispatch pointer
+           // events.
+           return NS_OK;
+         }
+         return shell->HandleEventWithTarget(aEvent, nullptr,
+                                             pointerCapturingContent,
+                                             aEventStatus, true);
+-      } else {
+-        frame = pointerCapturingFrame;
+       }
+     }
+ 
+     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+     bool isWindowLevelMouseExit = (aEvent->mMessage == eMouseExitFromWidget) &&
+       (mouseEvent && mouseEvent->mExitFrom == WidgetMouseEvent::eTopLevel);
+ 
+     // Get the frame at the event point. However, don't do this if we're
+@@ -7126,17 +7123,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+       return NS_OK;
+     }
+ 
+     if (!frame) {
+       NS_WARNING("Nothing to handle this event!");
+       return NS_OK;
+     }
+ 
+-    PresShell* shell = static_cast<PresShell*>(frame->PresShell());
++    RefPtr<PresShell> shell = static_cast<PresShell*>(frame->PresShell());
+     // Check if we have an active EventStateManager which isn't the
+     // EventStateManager of the current PresContext.
+     // If that is the case, and mouse is over some ancestor document,
+     // forward event handling to the active document.
+     // This way content can get mouse events even when
+     // mouse is over the chrome or outside the window.
+     //
+     // Note, currently for backwards compatibility we don't forward mouse events
+@@ -7229,27 +7226,25 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+     if (aEvent->mClass == eTouchEventClass) {
+       if (aEvent->mMessage == eTouchStart) {
+         WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+         if (nsIFrame* newFrame =
+               TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
+                 touchEvent)) {
+           frame = newFrame;
+           frame->GetContentForEvent(aEvent, getter_AddRefs(targetElement));
+-          shell = static_cast<PresShell*>(frame->PresContext()->PresShell());
++          shell = static_cast<PresShell*>(frame->PresShell());
+         }
+       } else if (PresShell* newShell = GetShellForTouchEvent(aEvent)) {
+         // Touch events (except touchstart) are dispatching to the captured
+         // element. Get correct shell from it.
+         shell = newShell;
+       }
+     }
+ 
+-    // Prevent deletion until we're done with event handling (bug 336582)
+-    nsCOMPtr<nsIPresShell> kungFuDeathGrip(shell);
+     nsresult rv;
+ 
+     // Handle the event in the correct shell.
+     // We pass the subshell's root frame as the frame to start from. This is
+     // the only correct alternative; if the event was captured then it
+     // must have been captured by us or some ancestor shell and we
+     // now ask the subshell to dispatch it normally.
+     shell->PushCurrentEventInfo(frame, targetElement);
+diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h
+--- a/layout/base/PresShell.h
++++ b/layout/base/PresShell.h
+@@ -221,20 +221,20 @@ public:
+   virtual float GetCumulativeNonRootScaleResolution() override;
+   virtual void SetRestoreResolution(float aResolution,
+                                     mozilla::LayoutDeviceIntSize aDisplaySize) override;
+ 
+   //nsIViewObserver interface
+ 
+   virtual void Paint(nsView* aViewToPaint, const nsRegion& aDirtyRegion,
+                      uint32_t aFlags) override;
+-  virtual nsresult HandleEvent(nsIFrame* aFrame,
+-                               mozilla::WidgetGUIEvent* aEvent,
+-                               bool aDontRetargetEvents,
+-                               nsEventStatus* aEventStatus) override;
++  MOZ_CAN_RUN_SCRIPT nsresult HandleEvent(nsIFrame* aFrame,
++                                          WidgetGUIEvent* aEvent,
++                                          bool aDontRetargetEvents,
++                                          nsEventStatus* aEventStatus) override;
+   virtual nsresult HandleDOMEventWithTarget(
+                                  nsIContent* aTargetContent,
+                                  mozilla::WidgetEvent* aEvent,
+                                  nsEventStatus* aStatus) override;
+   virtual nsresult HandleDOMEventWithTarget(nsIContent* aTargetContent,
+                                                         nsIDOMEvent* aEvent,
+                                                         nsEventStatus* aStatus) override;
+   virtual bool ShouldIgnoreInvalidation() override;
+@@ -666,18 +666,18 @@ protected:
+ 
+   void QueryIsActive();
+   nsresult UpdateImageLockingState();
+ 
+   bool InZombieDocument(nsIContent *aContent);
+   already_AddRefed<nsIPresShell> GetParentPresShellForEventHandling();
+   nsIContent* GetCurrentEventContent();
+   nsIFrame* GetCurrentEventFrame();
+-  nsresult RetargetEventToParent(mozilla::WidgetGUIEvent* aEvent,
+-                                 nsEventStatus* aEventStatus);
++  MOZ_CAN_RUN_SCRIPT nsresult
++  RetargetEventToParent(WidgetGUIEvent* aEvent, nsEventStatus* aEventStatus);
+   void PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent);
+   void PopCurrentEventInfo();
+   /**
+    * @param aIsHandlingNativeEvent      true when the caller (perhaps) handles
+    *                                    an event which is caused by native
+    *                                    event.  Otherwise, false.
+    */
+   nsresult HandleEventInternal(mozilla::WidgetEvent* aEvent,
+diff --git a/layout/base/gtest/TestAccessibleCaretEventHub.cpp b/layout/base/gtest/TestAccessibleCaretEventHub.cpp
+--- a/layout/base/gtest/TestAccessibleCaretEventHub.cpp
++++ b/layout/base/gtest/TestAccessibleCaretEventHub.cpp
+@@ -192,77 +192,90 @@ public:
+ 
+   static UniquePtr<WidgetEvent> CreateWheelEvent(EventMessage aMessage)
+   {
+     auto event = MakeUnique<WidgetWheelEvent>(true, aMessage, nullptr);
+ 
+     return Move(event);
+   }
+ 
++  MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestAsyncPanZoomScroll();
++
++  MOZ_CAN_RUN_SCRIPT_BOUNDARY
+   void HandleEventAndCheckState(UniquePtr<WidgetEvent> aEvent,
+                                 MockAccessibleCaretEventHub::State* aExpectedState,
+                                 nsEventStatus aExpectedEventStatus)
+   {
+     nsEventStatus rv = mHub->HandleEvent(aEvent.get());
+     EXPECT_EQ(mHub->GetState(), aExpectedState);
+     EXPECT_EQ(rv, aExpectedEventStatus);
+   }
+ 
+   void CheckState(MockAccessibleCaretEventHub::State* aExpectedState)
+   {
+     EXPECT_EQ(mHub->GetState(), aExpectedState);
+   }
+ 
+-  template <typename PressEventCreator, typename ReleaseEventCreator>
+-  void TestPressReleaseOnNoCaret(PressEventCreator aPressEventCreator,
+-                                 ReleaseEventCreator aReleaseEventCreator);
+-
+-  template <typename PressEventCreator, typename ReleaseEventCreator>
+-  void TestPressReleaseOnCaret(PressEventCreator aPressEventCreator,
+-                               ReleaseEventCreator aReleaseEventCreator);
++  template<typename PressEventCreator, typename ReleaseEventCreator>
++  MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestPressReleaseOnNoCaret(
++    PressEventCreator aPressEventCreator,
++    ReleaseEventCreator aReleaseEventCreator);
+ 
+-  template <typename PressEventCreator, typename MoveEventCreator,
+-            typename ReleaseEventCreator>
+-  void TestPressMoveReleaseOnNoCaret(PressEventCreator aPressEventCreator,
+-                                     MoveEventCreator aMoveEventCreator,
+-                                     ReleaseEventCreator aReleaseEventCreator);
+-
+-  template <typename PressEventCreator, typename MoveEventCreator,
+-            typename ReleaseEventCreator>
+-  void TestPressMoveReleaseOnCaret(PressEventCreator aPressEventCreator,
+-                                   MoveEventCreator aMoveEventCreator,
+-                                   ReleaseEventCreator aReleaseEventCreator);
+-
+-  template <typename PressEventCreator, typename ReleaseEventCreator>
+-  void TestLongTapWithSelectWordSuccessful(
++  template<typename PressEventCreator, typename ReleaseEventCreator>
++  MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestPressReleaseOnCaret(
+     PressEventCreator aPressEventCreator,
+     ReleaseEventCreator aReleaseEventCreator);
+ 
+-  template <typename PressEventCreator, typename ReleaseEventCreator>
+-  void TestLongTapWithSelectWordFailed(
++  template<typename PressEventCreator,
++           typename MoveEventCreator,
++           typename ReleaseEventCreator>
++  MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestPressMoveReleaseOnNoCaret(
++    PressEventCreator aPressEventCreator,
++    MoveEventCreator aMoveEventCreator,
++    ReleaseEventCreator aReleaseEventCreator);
++
++  template<typename PressEventCreator,
++           typename MoveEventCreator,
++           typename ReleaseEventCreator>
++  MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestPressMoveReleaseOnCaret(
++    PressEventCreator aPressEventCreator,
++    MoveEventCreator aMoveEventCreator,
++    ReleaseEventCreator aReleaseEventCreator);
++
++  template<typename PressEventCreator, typename ReleaseEventCreator>
++  MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestLongTapWithSelectWordSuccessful(
+     PressEventCreator aPressEventCreator,
+     ReleaseEventCreator aReleaseEventCreator);
+ 
+-  template <typename PressEventCreator, typename MoveEventCreator,
+-            typename ReleaseEventCreator>
+-  void TestEventDrivenAsyncPanZoomScroll(
+-    PressEventCreator aPressEventCreator, MoveEventCreator aMoveEventCreator,
++  template<typename PressEventCreator, typename ReleaseEventCreator>
++  MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestLongTapWithSelectWordFailed(
++    PressEventCreator aPressEventCreator,
++    ReleaseEventCreator aReleaseEventCreator);
++
++  template<typename PressEventCreator,
++           typename MoveEventCreator,
++           typename ReleaseEventCreator>
++  MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestEventDrivenAsyncPanZoomScroll(
++    PressEventCreator aPressEventCreator,
++    MoveEventCreator aMoveEventCreator,
+     ReleaseEventCreator aReleaseEventCreator);
+ 
+   // Member variables
+   RefPtr<MockAccessibleCaretEventHub> mHub{new MockAccessibleCaretEventHub()};
+ 
+ }; // class AccessibleCaretEventHubTester
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestMousePressReleaseOnNoCaret)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestPressReleaseOnNoCaret(CreateMousePressEvent, CreateMouseReleaseEvent);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestTouchPressReleaseOnNoCaret)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestPressReleaseOnNoCaret(CreateTouchStartEvent, CreateTouchEndEvent);
+ }
+ 
+ template <typename PressEventCreator, typename ReleaseEventCreator>
+ void
+ AccessibleCaretEventHubTester::TestPressReleaseOnNoCaret(
+   PressEventCreator aPressEventCreator,
+@@ -280,21 +293,23 @@ AccessibleCaretEventHubTester::TestPress
+                            nsEventStatus_eIgnore);
+ 
+   HandleEventAndCheckState(aReleaseEventCreator(0, 0),
+                            MockAccessibleCaretEventHub::NoActionState(),
+                            nsEventStatus_eIgnore);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestMousePressReleaseOnCaret)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestPressReleaseOnCaret(CreateMousePressEvent, CreateMouseReleaseEvent);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestTouchPressReleaseOnCaret)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestPressReleaseOnCaret(CreateTouchStartEvent, CreateTouchEndEvent);
+ }
+ 
+ template <typename PressEventCreator, typename ReleaseEventCreator>
+ void
+ AccessibleCaretEventHubTester::TestPressReleaseOnCaret(
+   PressEventCreator aPressEventCreator,
+@@ -322,22 +337,24 @@ AccessibleCaretEventHubTester::TestPress
+                            nsEventStatus_eConsumeNoDefault);
+ 
+   HandleEventAndCheckState(aReleaseEventCreator(0, 0),
+                            MockAccessibleCaretEventHub::NoActionState(),
+                            nsEventStatus_eConsumeNoDefault);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestMousePressMoveReleaseOnNoCaret)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestPressMoveReleaseOnNoCaret(CreateMousePressEvent, CreateMouseMoveEvent,
+                                 CreateMouseReleaseEvent);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestTouchPressMoveReleaseOnNoCaret)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestPressMoveReleaseOnNoCaret(CreateTouchStartEvent, CreateTouchMoveEvent,
+                                 CreateTouchEndEvent);
+ }
+ 
+ template <typename PressEventCreator, typename MoveEventCreator,
+           typename ReleaseEventCreator>
+ void
+@@ -376,22 +393,24 @@ AccessibleCaretEventHubTester::TestPress
+                            nsEventStatus_eIgnore);
+ 
+   HandleEventAndCheckState(aReleaseEventCreator(x3, y3),
+                            MockAccessibleCaretEventHub::NoActionState(),
+                            nsEventStatus_eIgnore);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestMousePressMoveReleaseOnCaret)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestPressMoveReleaseOnCaret(CreateMousePressEvent, CreateMouseMoveEvent,
+                               CreateMouseReleaseEvent);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestTouchPressMoveReleaseOnCaret)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestPressMoveReleaseOnCaret(CreateTouchStartEvent, CreateTouchMoveEvent,
+                               CreateTouchEndEvent);
+ }
+ 
+ template <typename PressEventCreator, typename MoveEventCreator,
+           typename ReleaseEventCreator>
+ void
+@@ -443,16 +462,17 @@ AccessibleCaretEventHubTester::TestPress
+ 
+   HandleEventAndCheckState(aReleaseEventCreator(x3, y3),
+                            MockAccessibleCaretEventHub::NoActionState(),
+                            nsEventStatus_eConsumeNoDefault);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester,
+        TestTouchStartMoveEndOnCaretWithTouchCancelIgnored)
++MOZ_CAN_RUN_SCRIPT
+ {
+   nscoord x0 = 0, y0 = 0;
+   nscoord x1 = 100, y1 = 100;
+   nscoord x2 = 300, y2 = 300;
+   nscoord x3 = 400, y3 = 400;
+ 
+   {
+     InSequence dummy;
+@@ -501,22 +521,24 @@ TEST_F(AccessibleCaretEventHubTester,
+                            MockAccessibleCaretEventHub::NoActionState(),
+                            nsEventStatus_eConsumeNoDefault);
+ 
+   HandleEventAndCheckState(CreateTouchCancelEvent(x3, y3),
+                            MockAccessibleCaretEventHub::NoActionState(),
+                            nsEventStatus_eIgnore);}
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestMouseLongTapWithSelectWordSuccessful)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestLongTapWithSelectWordSuccessful(CreateMousePressEvent,
+                                       CreateMouseReleaseEvent);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestTouchLongTapWithSelectWordSuccessful)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestLongTapWithSelectWordSuccessful(CreateTouchStartEvent,
+                                       CreateTouchEndEvent);
+ }
+ 
+ template <typename PressEventCreator, typename ReleaseEventCreator>
+ void
+ AccessibleCaretEventHubTester::TestLongTapWithSelectWordSuccessful(
+@@ -585,22 +607,24 @@ AccessibleCaretEventHubTester::TestLongT
+   EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+ 
+   HandleEventAndCheckState(aReleaseEventCreator(1, 1),
+                            MockAccessibleCaretEventHub::NoActionState(),
+                            nsEventStatus_eIgnore);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestMouseLongTapWithSelectWordFailed)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestLongTapWithSelectWordFailed(CreateMousePressEvent,
+                                   CreateMouseReleaseEvent);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestTouchLongTapWithSelectWordFailed)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestLongTapWithSelectWordFailed(CreateTouchStartEvent,
+                                   CreateTouchEndEvent);
+ }
+ 
+ template <typename PressEventCreator, typename ReleaseEventCreator>
+ void
+ AccessibleCaretEventHubTester::TestLongTapWithSelectWordFailed(
+@@ -626,22 +650,24 @@ AccessibleCaretEventHubTester::TestLongT
+                            nsEventStatus_eIgnore);
+ 
+   HandleEventAndCheckState(aReleaseEventCreator(0, 0),
+                            MockAccessibleCaretEventHub::NoActionState(),
+                            nsEventStatus_eIgnore);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestTouchEventDrivenAsyncPanZoomScroll)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestEventDrivenAsyncPanZoomScroll(CreateTouchStartEvent, CreateTouchMoveEvent,
+                                     CreateTouchEndEvent);
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestMouseEventDrivenAsyncPanZoomScroll)
++MOZ_CAN_RUN_SCRIPT
+ {
+   TestEventDrivenAsyncPanZoomScroll(CreateMousePressEvent, CreateMouseMoveEvent,
+                                     CreateMouseReleaseEvent);
+ }
+ 
+ template <typename PressEventCreator, typename MoveEventCreator,
+           typename ReleaseEventCreator>
+ void
+@@ -734,17 +760,23 @@ AccessibleCaretEventHubTester::TestEvent
+ 
+   check.Call("4");
+ 
+   // Simulate scroll end fired by timer.
+   MockAccessibleCaretEventHub::FireScrollEnd(nullptr, mHub);
+   EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+ }
+ 
+-TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScroll)
++TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScroll) MOZ_CAN_RUN_SCRIPT
++{
++  TestAsyncPanZoomScroll();
++}
++
++void
++AccessibleCaretEventHubTester::TestAsyncPanZoomScroll()
+ {
+   MockFunction<void(::std::string aCheckPointName)> check;
+   {
+     InSequence dummy;
+ 
+     EXPECT_CALL(check, Call("1"));
+     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart());
+ 
+@@ -778,16 +810,17 @@ TEST_F(AccessibleCaretEventHubTester, Te
+   check.Call("2");
+ 
+   // Simulate scroll end fired by timer.
+   MockAccessibleCaretEventHub::FireScrollEnd(nullptr, mHub);
+   EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScrollStartedThenBlur)
++MOZ_CAN_RUN_SCRIPT
+ {
+   {
+     InSequence dummy;
+ 
+     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart());
+     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd()).Times(0);
+     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnBlur());
+   }
+@@ -798,16 +831,17 @@ TEST_F(AccessibleCaretEventHubTester, Te
+   mHub->ScrollPositionChanged();
+   EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+ 
+   mHub->NotifyBlur(true);
+   EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+ }
+ 
+ TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScrollEndedThenBlur)
++MOZ_CAN_RUN_SCRIPT
+ {
+   {
+     InSequence dummy;
+ 
+     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart());
+     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd()).Times(0);
+     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnBlur());
+   }
+diff --git a/layout/base/gtest/TestAccessibleCaretManager.cpp b/layout/base/gtest/TestAccessibleCaretManager.cpp
+--- a/layout/base/gtest/TestAccessibleCaretManager.cpp
++++ b/layout/base/gtest/TestAccessibleCaretManager.cpp
+@@ -141,16 +141,17 @@ public:
+   }
+ 
+   // Member variables
+   MockAccessibleCaretManager mManager;
+ 
+ }; // class AccessibleCaretManagerTester
+ 
+ TEST_F(AccessibleCaretManagerTester, TestUpdatesInSelectionMode)
++MOZ_CAN_RUN_SCRIPT
+ {
+   EXPECT_CALL(mManager, GetCaretMode())
+     .WillRepeatedly(Return(CaretMode::Selection));
+ 
+   EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+                 CaretChangedReason::Updateposition)).Times(3);
+ 
+   mManager.UpdateCarets();
+@@ -162,16 +163,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
+   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
+ 
+   mManager.OnScrollPositionChanged();
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
+ }
+ 
+ TEST_F(AccessibleCaretManagerTester, TestSingleTapOnNonEmptyInput)
++MOZ_CAN_RUN_SCRIPT
+ {
+   EXPECT_CALL(mManager, GetCaretMode())
+     .WillRepeatedly(Return(CaretMode::Cursor));
+ 
+   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
+     .WillRepeatedly(Return(true));
+ 
+   MockFunction<void(std::string aCheckPointName)> check;
+@@ -232,16 +234,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+   check.Call("reflow2");
+ 
+   mManager.OnScrollPositionChanged();
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+ }
+ 
+ TEST_F(AccessibleCaretManagerTester, TestSingleTapOnEmptyInput)
++MOZ_CAN_RUN_SCRIPT
+ {
+   EXPECT_CALL(mManager, GetCaretMode())
+     .WillRepeatedly(Return(CaretMode::Cursor));
+ 
+   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
+     .WillRepeatedly(Return(false));
+ 
+   MockFunction<void(std::string aCheckPointName)> check;
+@@ -301,17 +304,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
+   mManager.OnReflow();
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
+   check.Call("reflow2");
+ 
+   mManager.OnScrollPositionChanged();
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
+ }
+ 
+-TEST_F(AccessibleCaretManagerTester, TestTypingAtEndOfInput)
++TEST_F(AccessibleCaretManagerTester, TestTypingAtEndOfInput) MOZ_CAN_RUN_SCRIPT
+ {
+   EXPECT_CALL(mManager, GetCaretMode())
+     .WillRepeatedly(Return(CaretMode::Cursor));
+ 
+   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
+     .WillRepeatedly(Return(true));
+ 
+   MockFunction<void(std::string aCheckPointName)> check;
+@@ -344,16 +347,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
+                               nsISelectionListener::NO_REASON);
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
+ 
+   mManager.OnScrollPositionChanged();
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
+ }
+ 
+ TEST_F(AccessibleCaretManagerTester, TestScrollInSelectionMode)
++MOZ_CAN_RUN_SCRIPT
+ {
+   // Simulate caret hiding when scrolling.
+   AutoRestore<bool> savesCaretsAlwaysShowWhenScrolling(
+     MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling);
+   MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false;
+ 
+   EXPECT_CALL(mManager, GetCaretMode())
+     .WillRepeatedly(Return(CaretMode::Selection));
+@@ -422,17 +426,19 @@ TEST_F(AccessibleCaretManagerTester, Tes
+   EXPECT_EQ(SecondCaretAppearance(), Appearance::None);
+ 
+   mManager.OnScrollEnd();
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
+   check.Call("scrollend2");
+ }
+ 
+-TEST_F(AccessibleCaretManagerTester, TestScrollInSelectionModeWithAlwaysTiltPref)
++TEST_F(AccessibleCaretManagerTester,
++       TestScrollInSelectionModeWithAlwaysTiltPref)
++MOZ_CAN_RUN_SCRIPT
+ {
+   // Simulate Firefox Android preference.
+   AutoRestore<bool> saveCaretsAlwaysTilt(
+     MockAccessibleCaretManager::sCaretsAlwaysTilt);
+   MockAccessibleCaretManager::sCaretsAlwaysTilt = true;
+ 
+   EXPECT_CALL(mManager, GetCaretMode())
+     .WillRepeatedly(Return(CaretMode::Selection));
+@@ -528,16 +534,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
+ 
+   mManager.OnScrollEnd();
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::Left);
+   EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
+   check.Call("scrollend2");
+ }
+ 
+ TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenLogicallyVisible)
++MOZ_CAN_RUN_SCRIPT
+ {
+   // Simulate caret hiding when scrolling.
+   AutoRestore<bool> savesCaretsAlwaysShowWhenScrolling(
+     MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling);
+   MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false;
+ 
+   EXPECT_CALL(mManager, GetCaretMode())
+     .WillRepeatedly(Return(CaretMode::Cursor));
+@@ -593,16 +600,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
+   check.Call("scrollstart2");
+ 
+   mManager.OnScrollEnd();
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+   check.Call("scrollend2");
+ }
+ 
+ TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenHidden)
++MOZ_CAN_RUN_SCRIPT
+ {
+   // Simulate caret hiding when scrolling.
+   AutoRestore<bool> savesCaretsAlwaysShowWhenScrolling(
+     MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling);
+   MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false;
+ 
+   EXPECT_CALL(mManager, GetCaretMode())
+     .WillRepeatedly(Return(CaretMode::Cursor));
+@@ -652,16 +660,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
+ 
+   mManager.OnScrollEnd();
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
+   check.Call("scrollend2");
+ }
+ 
+ TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeOnEmptyContent)
++MOZ_CAN_RUN_SCRIPT
+ {
+   // Simulate caret hiding when scrolling.
+   AutoRestore<bool> savesCaretsAlwaysShowWhenScrolling(
+     MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling);
+   MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false;
+ 
+   EXPECT_CALL(mManager, GetCaretMode())
+     .WillRepeatedly(Return(CaretMode::Cursor));
+@@ -728,16 +737,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
+   check.Call("scrollstart3");
+   mManager.OnScrollEnd();
+   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
+   check.Call("scrollend3");
+ }
+ 
+ TEST_F(AccessibleCaretManagerTester,
+        TestScrollInCursorModeWithCaretShownWhenLongTappingOnEmptyContentPref)
++MOZ_CAN_RUN_SCRIPT
+ {
+   // Simulate Firefox Android preference.
+   AutoRestore<bool> savesCaretShownWhenLongTappingOnEmptyContent(
+     MockAccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent);
+   MockAccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent = true;
+ 
+   EXPECT_CALL(mManager, GetCaretMode())
+     .WillRepeatedly(Return(CaretMode::Cursor));

+ 182 - 0
mozilla-release/patches/1440257-60a1.patch

@@ -0,0 +1,182 @@
+# HG changeset patch
+# User James Teh <jteh@mozilla.com>
+# Date 1519346647 -36000
+# Node ID 8702f2b2c64818748fdfe7a530a5a3e02d1fddc5
+# Parent  33134d27bbeec1417cae50d88bd71bb8ca2ef33f
+Bug 1440257: Ensure that mscom::MainThreadInvoker gets cleaned up very soon after it finishes executing. r=aklotz
+
+MainThreadInvoker queues both a Gecko runnable and an APC to the main thread to deal with different ways in which the main thread can block.
+However, the main thread doesn't check for APCs very often any more.
+This means that the APC's reference to the SyncRunnable doesn't get cleaned up for a long time, thus leaking memory.
+To work around this, we:
+1. Queue an APC wich does the actual work.
+2. Post a Gecko runnable (which always runs).
+If the APC hasn't run, the Gecko runnable runs it.
+Otherwise, it does nothing.
+
+MozReview-Commit-ID: L0P4rMBnlaZ
+
+diff --git a/ipc/mscom/MainThreadInvoker.cpp b/ipc/mscom/MainThreadInvoker.cpp
+--- a/ipc/mscom/MainThreadInvoker.cpp
++++ b/ipc/mscom/MainThreadInvoker.cpp
+@@ -11,52 +11,67 @@
+ #include "mozilla/Assertions.h"
+ #include "mozilla/ClearOnShutdown.h"
+ #include "mozilla/DebugOnly.h"
+ #include "mozilla/HangMonitor.h"
+ #include "mozilla/mscom/SpinEvent.h"
+ #include "mozilla/RefPtr.h"
+ #include "mozilla/SystemGroup.h"
+ #include "private/prpriv.h" // For PR_GetThreadID
+-#include "WinUtils.h"
++#include <winternl.h> // For NTSTATUS and NTAPI
+ 
+ namespace {
+ 
++typedef NTSTATUS (NTAPI* NtTestAlertPtr)(VOID);
++
+ /**
+  * SyncRunnable implements different code paths depending on whether or not
+  * we are running on a multiprocessor system. In the multiprocessor case, we
+  * leave the thread in a spin loop while waiting for the main thread to execute
+  * our runnable. Since spinning is pointless in the uniprocessor case, we block
+  * on an event that is set by the main thread once it has finished the runnable.
+  */
+ class SyncRunnable : public mozilla::Runnable
+ {
+ public:
+   explicit SyncRunnable(already_AddRefed<nsIRunnable> aRunnable)
+     : mozilla::Runnable("MainThreadInvoker")
+     , mRunnable(aRunnable)
+-  {}
++  {
++    static const bool gotStatics = InitStatics();
++    MOZ_ASSERT(gotStatics);
++  }
+ 
+   ~SyncRunnable() = default;
+ 
+   NS_IMETHOD Run() override
+   {
+     if (mHasRun) {
++      // The APC already ran, so we have nothing to do.
+       return NS_OK;
+     }
++
++    // Run the pending APC in the queue.
++    MOZ_ASSERT(sNtTestAlert);
++    sNtTestAlert();
++    return NS_OK;
++  }
++
++  // This is called by MainThreadInvoker::MainThreadAPC.
++  void APCRun()
++  {
+     mHasRun = true;
+ 
+     TimeStamp runStart(TimeStamp::Now());
+     mRunnable->Run();
+     TimeStamp runEnd(TimeStamp::Now());
+ 
+     mDuration = runEnd - runStart;
+ 
+     mEvent.Signal();
+-    return NS_OK;
+   }
+ 
+   bool WaitUntilComplete()
+   {
+     return mEvent.Wait(mozilla::mscom::MainThreadInvoker::GetTargetThread());
+   }
+ 
+   const mozilla::TimeDuration& GetDuration() const
+@@ -64,18 +79,31 @@ public:
+     return mDuration;
+   }
+ 
+ private:
+   bool                      mHasRun = false;
+   nsCOMPtr<nsIRunnable>     mRunnable;
+   mozilla::mscom::SpinEvent mEvent;
+   mozilla::TimeDuration     mDuration;
++
++  static NtTestAlertPtr sNtTestAlert;
++
++  static bool InitStatics()
++  {
++    sNtTestAlert = reinterpret_cast<NtTestAlertPtr>(
++      ::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), "NtTestAlert"));
++    MOZ_ASSERT(sNtTestAlert);
++    return sNtTestAlert;
++  }
++
+ };
+ 
++NtTestAlertPtr SyncRunnable::sNtTestAlert = nullptr;
++
+ } // anonymous namespace
+ 
+ namespace mozilla {
+ namespace mscom {
+ 
+ HANDLE MainThreadInvoker::sMainThread = nullptr;
+ 
+ /* static */ bool
+@@ -115,39 +143,50 @@ MainThreadInvoker::Invoke(already_AddRef
+ 
+   if (NS_IsMainThread()) {
+     runnable->Run();
+     return true;
+   }
+ 
+   RefPtr<SyncRunnable> syncRunnable = new SyncRunnable(runnable.forget());
+ 
+-  if (NS_FAILED(SystemGroup::Dispatch(
+-                  TaskCategory::Other, do_AddRef(syncRunnable)))) {
+-    return false;
+-  }
+-
++  // The main thread could be either blocked on a condition variable waiting
++  // for a Gecko event, or it could be blocked waiting on a Windows HANDLE in
++  // IPC code (doing a sync message send). In the former case, we wake it by
++  // posting a Gecko runnable to the main thread. In the latter case, we wake
++  // it using an APC. However, the latter case doesn't happen very often now
++  // and APCs aren't otherwise run by the main thread. To ensure the
++  // SyncRunnable is cleaned up, we need both to run consistently.
++  // To do this, we:
++  // 1. Queue an APC which does the actual work.
+   // This ref gets released in MainThreadAPC when it runs.
+   SyncRunnable* syncRunnableRef = syncRunnable.get();
+   NS_ADDREF(syncRunnableRef);
+   if (!::QueueUserAPC(&MainThreadAPC, sMainThread,
+                       reinterpret_cast<UINT_PTR>(syncRunnableRef))) {
+     return false;
+   }
+ 
++  // 2. Post a Gecko runnable (which always runs). If the APC hasn't run, the
++  // Gecko runnable runs it. Otherwise, it does nothing.
++  if (NS_FAILED(SystemGroup::Dispatch(
++                  TaskCategory::Other, do_AddRef(syncRunnable)))) {
++    return false;
++  }
++
+   bool result = syncRunnable->WaitUntilComplete();
+   mDuration = syncRunnable->GetDuration();
+   return result;
+ }
+ 
+ /* static */ VOID CALLBACK
+ MainThreadInvoker::MainThreadAPC(ULONG_PTR aParam)
+ {
+   AUTO_PROFILER_THREAD_WAKE;
+   mozilla::HangMonitor::NotifyActivity(mozilla::HangMonitor::kGeneralActivity);
+   MOZ_ASSERT(NS_IsMainThread());
+   auto runnable = reinterpret_cast<SyncRunnable*>(aParam);
+-  runnable->Run();
++  runnable->APCRun();
+   NS_RELEASE(runnable);
+ }
+ 
+ } // namespace mscom
+ } // namespace mozilla
+

+ 677 - 0
mozilla-release/patches/1447993-1-61a1.patch

@@ -0,0 +1,677 @@
+# HG changeset patch
+# User Olli Pettay <Olli.Pettay@helsinki.fi>
+# Date 1522340579 -10800
+# Node ID b8712aa41c6a9e39ade9a04057403a485f6aab11
+# Parent  52b64e72d72d49cacda6b68143d5219262fb0498
+Bug 1447993, when handling pointerup while there is pointercapture, do a hit test in order to find the click target, r=masayuki
+
+diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
+--- a/dom/events/EventStateManager.cpp
++++ b/dom/events/EventStateManager.cpp
+@@ -500,17 +500,18 @@ IsMessageGamepadUserActivity(EventMessag
+          aMessage == eGamepadAxisMove;
+ }
+ 
+ nsresult
+ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
+                                   WidgetEvent* aEvent,
+                                   nsIFrame* aTargetFrame,
+                                   nsIContent* aTargetContent,
+-                                  nsEventStatus* aStatus)
++                                  nsEventStatus* aStatus,
++                                  nsIContent* aOverrideClickTarget)
+ {
+   NS_ENSURE_ARG_POINTER(aStatus);
+   NS_ENSURE_ARG(aPresContext);
+   if (!aEvent) {
+     NS_ERROR("aEvent is null.  This should never happen.");
+     return NS_ERROR_NULL_POINTER;
+   }
+ 
+@@ -649,17 +650,18 @@ EventStateManager::PreHandleEvent(nsPres
+           KillClickHoldTimer();
+         }
+         StopTrackingDragGesture();
+         sNormalLMouseEventInProcess = false;
+         // then fall through...
+         MOZ_FALLTHROUGH;
+       case WidgetMouseEvent::eRightButton:
+       case WidgetMouseEvent::eMiddleButton:
+-        SetClickCount(mouseEvent, aStatus);
++        RefPtr<EventStateManager> esm = ESMFromContentOrThis(aOverrideClickTarget);
++        esm->SetClickCount(mouseEvent, aStatus, aOverrideClickTarget);
+         NotifyTargetUserActivation(aEvent, aTargetContent);
+         break;
+     }
+     break;
+   }
+   case eMouseEnterIntoWidget:
+     // In some cases on e10s eMouseEnterIntoWidget
+     // event was sent twice into child process of content.
+@@ -916,16 +918,36 @@ EventStateManager::NotifyTargetUserActiv
+   }
+ 
+   MOZ_ASSERT(aEvent->mMessage == eKeyUp   ||
+              aEvent->mMessage == eMouseUp ||
+              aEvent->mMessage == eTouchEnd);
+   doc->NotifyUserActivation();
+ }
+ 
++already_AddRefed<EventStateManager>
++EventStateManager::ESMFromContentOrThis(nsIContent* aContent)
++{
++  if (aContent) {
++    nsIPresShell* shell = aContent->OwnerDoc()->GetShell();
++    if (shell) {
++      nsPresContext* prescontext = shell->GetPresContext();
++      if (prescontext) {
++        RefPtr<EventStateManager> esm = prescontext->EventStateManager();
++        if (esm) {
++          return esm.forget();
++        }
++      }
++    }
++  }
++
++  RefPtr<EventStateManager> esm = this;
++  return esm.forget();
++}
++
+ void
+ EventStateManager::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
+ {
+   switch (aEvent->mMessage) {
+     case eQuerySelectedText:
+     case eQueryTextContent:
+     case eQueryCaretRect:
+     case eQueryTextRect:
+@@ -3025,17 +3047,18 @@ EventStateManager::PostHandleKeyboardEve
+       break;
+   }
+ }
+ 
+ nsresult
+ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
+                                    WidgetEvent* aEvent,
+                                    nsIFrame* aTargetFrame,
+-                                   nsEventStatus* aStatus)
++                                   nsEventStatus* aStatus,
++                                   nsIContent* aOverrideClickTarget)
+ {
+   NS_ENSURE_ARG(aPresContext);
+   NS_ENSURE_ARG_POINTER(aStatus);
+ 
+   mCurrentTarget = aTargetFrame;
+   mCurrentTargetContent = nullptr;
+ 
+   HandleCrossProcessEvent(aEvent, aStatus);
+@@ -3287,17 +3310,20 @@ EventStateManager::PostHandleEvent(nsPre
+       ClearGlobalActiveContent(this);
+       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+       if (mouseEvent && mouseEvent->IsReal()) {
+         if (!mCurrentTarget) {
+           GetEventTarget();
+         }
+         // Make sure to dispatch the click even if there is no frame for
+         // the current target element. This is required for Web compatibility.
+-        ret = CheckForAndDispatchClick(mouseEvent, aStatus);
++        RefPtr<EventStateManager> esm =
++          ESMFromContentOrThis(aOverrideClickTarget);
++        ret = esm->CheckForAndDispatchClick(mouseEvent, aStatus,
++                                            aOverrideClickTarget);
+       }
+ 
+       nsIPresShell *shell = presContext->GetPresShell();
+       if (shell) {
+         RefPtr<nsFrameSelection> frameSelection = shell->FrameSelection();
+         frameSelection->SetDragState(false);
+       }
+     }
+@@ -4731,21 +4757,22 @@ EventStateManager::UpdateDragDataTransfe
+       dragEvent->mDataTransfer->GetMozCursor(mozCursor);
+       initialDataTransfer->SetMozCursor(mozCursor);
+     }
+   }
+ }
+ 
+ nsresult
+ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
+-                                 nsEventStatus* aStatus)
++                                 nsEventStatus* aStatus,
++                                 nsIContent* aOverrideClickTarget)
+ {
+-  nsCOMPtr<nsIContent> mouseContent;
++  nsCOMPtr<nsIContent> mouseContent = aOverrideClickTarget;
+   nsIContent* mouseContentParent = nullptr;
+-  if (mCurrentTarget) {
++  if (!mouseContent && mCurrentTarget) {
+     mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(mouseContent));
+   }
+   if (mouseContent) {
+     if (mouseContent->IsNodeOfType(nsINode::eTEXT)) {
+       mouseContent = mouseContent->GetParent();
+     }
+     if (mouseContent && mouseContent->IsRootOfNativeAnonymousSubtree()) {
+       mouseContentParent = mouseContent->GetParent();
+@@ -4813,39 +4840,47 @@ EventStateManager::SetClickCount(WidgetM
+ 
+ nsresult
+ EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
+                                              nsEventStatus* aStatus,
+                                              EventMessage aMessage,
+                                              nsIPresShell* aPresShell,
+                                              nsIContent* aMouseTarget,
+                                              AutoWeakFrame aCurrentTarget,
+-                                             bool aNoContentDispatch)
++                                             bool aNoContentDispatch,
++                                             nsIContent* aOverrideClickTarget)
+ {
+   WidgetMouseEvent event(aEvent->IsTrusted(), aMessage,
+                          aEvent->mWidget, WidgetMouseEvent::eReal);
+ 
+   event.mRefPoint = aEvent->mRefPoint;
+   event.mClickCount = aEvent->mClickCount;
+   event.mModifiers = aEvent->mModifiers;
+   event.buttons = aEvent->buttons;
+   event.mTime = aEvent->mTime;
+   event.mTimeStamp = aEvent->mTimeStamp;
+   event.mFlags.mNoContentDispatch = aNoContentDispatch;
+   event.button = aEvent->button;
+   event.pointerId = aEvent->pointerId;
+   event.inputSource = aEvent->inputSource;
+-
+-  return aPresShell->HandleEventWithTarget(&event, aCurrentTarget,
+-                                           aMouseTarget, aStatus);
++  nsIContent* target = aMouseTarget;
++  nsIFrame* targetFrame = aCurrentTarget;
++  if (aOverrideClickTarget) {
++    target = aOverrideClickTarget;
++    targetFrame = aOverrideClickTarget->GetPrimaryFrame();
++  }
++
++  return aPresShell->HandleEventWithTarget(&event, targetFrame,
++                                           target, aStatus);
+ }
+ 
+ nsresult
+ EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
+-                                            nsEventStatus* aStatus)
++                                            nsEventStatus* aStatus,
++                                            nsIContent* aOverrideClickTarget)
+ {
+   nsresult ret = NS_OK;
+ 
+   //If mouse is still over same element, clickcount will be > 1.
+   //If it has moved it will be zero, so no click.
+   if (aEvent->mClickCount) {
+     //Check that the window isn't disabled before firing a click
+     //(see bug 366544).
+@@ -4865,38 +4900,40 @@ EventStateManager::CheckForAndDispatchCl
+       // Click events apply to *elements* not nodes. At this point the target
+       // content may have been reset to some non-element content, and so we need
+       // to walk up the closest ancestor element, just like we do in
+       // nsPresShell::HandleEvent.
+       while (mouseContent && !mouseContent->IsElement()) {
+         mouseContent = mouseContent->GetParent();
+       }
+ 
+-      if (!mouseContent && !mCurrentTarget) {
++      if (!mouseContent && !mCurrentTarget && !aOverrideClickTarget) {
+         return NS_OK;
+       }
+ 
+       // HandleEvent clears out mCurrentTarget which we might need again
+       AutoWeakFrame currentTarget = mCurrentTarget;
+       ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseClick,
+                                       presShell, mouseContent, currentTarget,
+-                                      notDispatchToContents);
++                                      notDispatchToContents,
++                                      aOverrideClickTarget);
+ 
+       if (NS_SUCCEEDED(ret) && aEvent->mClickCount == 2 &&
+           mouseContent && mouseContent->IsInComposedDoc()) {
+         //fire double click
+         ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseDoubleClick,
+                                         presShell, mouseContent, currentTarget,
+-                                        notDispatchToContents);
++                                        notDispatchToContents,
++                                        aOverrideClickTarget);
+       }
+       if (NS_SUCCEEDED(ret) && mouseContent && fireAuxClick &&
+           mouseContent->IsInComposedDoc()) {
+         ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseAuxClick,
+                                         presShell, mouseContent, currentTarget,
+-                                        false);
++                                        false, aOverrideClickTarget);
+       }
+     }
+   }
+   return ret;
+ }
+ 
+ nsIFrame*
+ EventStateManager::GetEventTarget()
+diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h
+--- a/dom/events/EventStateManager.h
++++ b/dom/events/EventStateManager.h
+@@ -88,32 +88,38 @@ public:
+   nsresult Shutdown();
+ 
+   /* The PreHandleEvent method is called before event dispatch to either
+    * the DOM or frames.  Any processing which must not be prevented or
+    * cancelled should occur here.  Any processing which is intended to
+    * be conditional based on either DOM or frame processing should occur in
+    * PostHandleEvent.  Any centralized event processing which must occur before
+    * DOM or frame event handling should occur here as well.
++   *
++   * aOverrideClickTarget can be used to indicate which element should be
++   * used as the *up target when deciding whether to send click event.
++   * This is used when releasing pointer capture. Otherwise null.
+    */
+   nsresult PreHandleEvent(nsPresContext* aPresContext,
+                           WidgetEvent* aEvent,
+                           nsIFrame* aTargetFrame,
+                           nsIContent* aTargetContent,
+-                          nsEventStatus* aStatus);
++                          nsEventStatus* aStatus,
++                          nsIContent* aOverrideClickTarget);
+ 
+   /* The PostHandleEvent method should contain all system processing which
+    * should occur conditionally based on DOM or frame processing.  It should
+    * also contain any centralized event processing which must occur after
+    * DOM and frame processing.
+    */
+   nsresult PostHandleEvent(nsPresContext* aPresContext,
+                            WidgetEvent* aEvent,
+                            nsIFrame* aTargetFrame,
+-                           nsEventStatus* aStatus);
++                           nsEventStatus* aStatus,
++                           nsIContent* aOverrideClickTarget);
+ 
+   void PostHandleKeyboardEvent(WidgetKeyboardEvent* aKeyboardEvent,
+                                nsIFrame* aTargetFrame, nsEventStatus& aStatus);
+ 
+   /**
+    * DispatchLegacyMouseScrollEvents() dispatches eLegacyMouseLineOrPageScroll
+    * event and eLegacyMousePixelScroll event for compatibility with old Gecko.
+    */
+@@ -438,20 +444,23 @@ protected:
+   void UpdateDragDataTransfer(WidgetDragEvent* dragEvent);
+ 
+   static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
+                                             nsEventStatus* aStatus,
+                                             EventMessage aMessage,
+                                             nsIPresShell* aPresShell,
+                                             nsIContent* aMouseTarget,
+                                             AutoWeakFrame aCurrentTarget,
+-                                            bool aNoContentDispatch);
+-  nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus);
++                                            bool aNoContentDispatch,
++                                            nsIContent* aOverrideClickTarget);
++  nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus,
++                         nsIContent* aOverrideClickTarget = nullptr);
+   nsresult CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
+-                                    nsEventStatus* aStatus);
++                                    nsEventStatus* aStatus,
++                                    nsIContent* aOverrideClickTarget);
+   void EnsureDocument(nsPresContext* aPresContext);
+   void FlushPendingEvents(nsPresContext* aPresContext);
+ 
+   /**
+    * The phases of WalkESMTreeToHandleAccessKey processing. See below.
+    */
+   typedef enum {
+     eAccessKeyProcessingNormal = 0,
+@@ -1012,16 +1021,18 @@ private:
+ 
+   /**
+    * Notify target when user has been interaction with some speicific user
+    * gestures which are eKeyUp, eMouseUp, eTouchEnd.
+    */
+   void NotifyTargetUserActivation(WidgetEvent* aEvent,
+                                   nsIContent* aTargetContent);
+ 
++  already_AddRefed<EventStateManager> ESMFromContentOrThis(nsIContent* aContent);
++
+   int32_t     mLockCursor;
+   bool mLastFrameConsumedSetCursor;
+ 
+   // Last mouse event mRefPoint (the offset from the widget's origin in
+   // device pixels) when mouse was locked, used to restore mouse position
+   // after unlocking.
+   static LayoutDeviceIntPoint sPreLockPoint;
+ 
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -7030,33 +7030,39 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+     // Only capture mouse events and pointer events.
+     nsCOMPtr<nsIContent> pointerCapturingContent =
+       PointerEventHandler::GetPointerCapturingContent(aEvent);
+ 
+     if (pointerCapturingContent) {
+       frame = pointerCapturingContent->GetPrimaryFrame();
+ 
+       if (!frame) {
++        RefPtr<PresShell> shell =
++          GetShellForEventTarget(nullptr, pointerCapturingContent);
++        if (!shell) {
++          // If we can't process event for the capturing content, release
++          // the capture.
++          PointerEventHandler::ReleaseIfCaptureByDescendant(
++            pointerCapturingContent);
++          return NS_OK;
++        }
++
++        nsCOMPtr<nsIContent> overrideClickTarget =
++          GetOverrideClickTarget(aEvent, aFrame);
++
+         // Dispatch events to the capturing content even it's frame is
+         // destroyed.
+         PointerEventHandler::DispatchPointerFromMouseOrTouch(
+-          this, nullptr, pointerCapturingContent, aEvent, false,
++          shell, nullptr, pointerCapturingContent, aEvent, false,
+           aEventStatus, nullptr);
+ 
+-        RefPtr<PresShell> shell =
+-          GetShellForEventTarget(nullptr, pointerCapturingContent);
+-
+-        if (!shell) {
+-          // The capturing element could be changed when dispatch pointer
+-          // events.
+-          return NS_OK;
+-        }
+         return shell->HandleEventWithTarget(aEvent, nullptr,
+                                             pointerCapturingContent,
+-                                            aEventStatus, true);
++                                            aEventStatus, true, nullptr,
++                                            overrideClickTarget);
+       }
+     }
+ 
+     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+     bool isWindowLevelMouseExit = (aEvent->mMessage == eMouseExitFromWidget) &&
+       (mouseEvent && mouseEvent->mExitFrom == WidgetMouseEvent::eTopLevel);
+ 
+     // Get the frame at the event point. However, don't do this if we're
+@@ -7177,31 +7183,47 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+       }
+ 
+       // If we found an element, target it.  Otherwise, target *nothing*.
+       if (!targetElement) {
+         return NS_OK;
+       }
+     }
+ 
++    nsCOMPtr<nsIContent> overrideClickTarget;
+     if (PointerEventHandler::IsPointerEventEnabled()) {
+       // Dispatch pointer events from the mouse or touch events. Regarding
+       // pointer events from mouse, we should dispatch those pointer events to
+       // the same target as the source mouse events. We pass the frame found
+       // in hit test to PointerEventHandler and dispatch pointer events to it.
+       //
+       // Regarding pointer events from touch, the behavior is different. Touch
+       // events are dispatched to the same target as the target of touchstart.
+       // Multiple touch points must be dispatched to the same document. Pointer
+       // events from touch can be dispatched to different documents. We Pass the
+       // original frame to PointerEventHandler, reentry PresShell::HandleEvent,
+       // and do hit test for each point.
+       nsIFrame* targetFrame =
+         aEvent->mClass == eTouchEventClass ? aFrame : frame;
+ 
++      if (pointerCapturingContent) {
++        overrideClickTarget = GetOverrideClickTarget(aEvent, aFrame);
++        shell = GetShellForEventTarget(nullptr, pointerCapturingContent);
++        if (!shell) {
++          // If we can't process event for the capturing content, release
++          // the capture.
++          PointerEventHandler::ReleaseIfCaptureByDescendant(
++            pointerCapturingContent);
++          return NS_OK;
++        }
++
++        targetFrame = pointerCapturingContent->GetPrimaryFrame();
++        frame = targetFrame;
++      }
++
+       AutoWeakFrame weakFrame(targetFrame);
+       nsCOMPtr<nsIContent> targetContent;
+       PointerEventHandler::DispatchPointerFromMouseOrTouch(
+                              shell, targetFrame, targetElement, aEvent,
+                              aDontRetargetEvents, aEventStatus,
+                              getter_AddRefs(targetContent));
+ 
+       if (!weakFrame.IsAlive() && aEvent->mClass == eMouseEventClass) {
+@@ -7241,17 +7263,18 @@ PresShell::HandleEvent(nsIFrame* aFrame,
+     nsresult rv;
+ 
+     // Handle the event in the correct shell.
+     // We pass the subshell's root frame as the frame to start from. This is
+     // the only correct alternative; if the event was captured then it
+     // must have been captured by us or some ancestor shell and we
+     // now ask the subshell to dispatch it normally.
+     shell->PushCurrentEventInfo(frame, targetElement);
+-    rv = shell->HandleEventInternal(aEvent, aEventStatus, true);
++    rv = shell->HandleEventInternal(aEvent, aEventStatus, true,
++                                    overrideClickTarget);
+ #ifdef DEBUG
+     shell->ShowEventTargetDebug();
+ #endif
+     shell->PopCurrentEventInfo();
+     return rv;
+   }
+ 
+   nsresult rv = NS_OK;
+@@ -7398,40 +7421,43 @@ PresShell::ShowEventTargetDebug()
+   }
+ }
+ #endif
+ 
+ nsresult
+ PresShell::HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame,
+                                  nsIContent* aContent, nsEventStatus* aStatus,
+                                  bool aIsHandlingNativeEvent,
+-                                 nsIContent** aTargetContent)
++                                 nsIContent** aTargetContent,
++                                 nsIContent* aOverrideClickTarget)
+ {
+ #if DEBUG
+   MOZ_ASSERT(!aFrame || aFrame->PresContext()->GetPresShell() == this,
+              "wrong shell");
+   if (aContent) {
+     nsIDocument* doc = aContent->GetComposedDoc();
+     NS_ASSERTION(doc, "event for content that isn't in a document");
+     // NOTE: We don't require that the document still have a PresShell.
+     // See bug 1375940.
+   }
+ #endif
+   NS_ENSURE_STATE(!aContent || aContent->GetComposedDoc() == mDocument);
+   AutoPointerEventTargetUpdater updater(this, aEvent, aFrame, aTargetContent);
+   PushCurrentEventInfo(aFrame, aContent);
+-  nsresult rv = HandleEventInternal(aEvent, aStatus, false);
++  nsresult rv =
++    HandleEventInternal(aEvent, aStatus, false, aOverrideClickTarget);
+   PopCurrentEventInfo();
+   return rv;
+ }
+ 
+ nsresult
+ PresShell::HandleEventInternal(WidgetEvent* aEvent,
+                                nsEventStatus* aStatus,
+-                               bool aIsHandlingNativeEvent)
++                               bool aIsHandlingNativeEvent,
++                               nsIContent* aOverrideClickTarget)
+ {
+   RefPtr<EventStateManager> manager = mPresContext->EventStateManager();
+   nsresult rv = NS_OK;
+ 
+   if (!NS_EVENT_NEEDS_FRAME(aEvent) || GetCurrentEventFrame() || GetCurrentEventContent()) {
+     bool touchIsNew = false;
+     bool isHandlingUserInput = false;
+ 
+@@ -7569,17 +7595,18 @@ PresShell::HandleEventInternal(WidgetEve
+ 
+     // FIXME. If the event was reused, we need to clear the old target,
+     // bug 329430
+     aEvent->mTarget = nullptr;
+ 
+     // 1. Give event to event manager for pre event state changes and
+     //    generation of synthetic events.
+     rv = manager->PreHandleEvent(mPresContext, aEvent, mCurrentEventFrame,
+-                                 mCurrentEventContent, aStatus);
++                                 mCurrentEventContent, aStatus,
++                                 aOverrideClickTarget);
+ 
+     // 2. Give event to the DOM for third party and JS use.
+     if (NS_SUCCEEDED(rv)) {
+       bool wasHandlingKeyBoardEvent =
+         nsContentUtils::IsHandlingKeyBoardEvent();
+       if (aEvent->mClass == eKeyboardEventClass) {
+         nsContentUtils::SetIsHandlingKeyBoardEvent(true);
+       }
+@@ -7623,17 +7650,18 @@ PresShell::HandleEventInternal(WidgetEve
+         PointerEventHandler::ReleasePointerCaptureById(pointerEvent->pointerId);
+         PointerEventHandler::CheckPointerCaptureState(pointerEvent);
+       }
+ 
+       // 3. Give event to event manager for post event state changes and
+       //    generation of synthetic events.
+       if (!mIsDestroying && NS_SUCCEEDED(rv)) {
+         rv = manager->PostHandleEvent(mPresContext, aEvent,
+-                                      GetCurrentEventFrame(), aStatus);
++                                      GetCurrentEventFrame(), aStatus,
++                                      aOverrideClickTarget);
+       }
+     }
+ 
+     if (!mIsDestroying && aIsHandlingNativeEvent) {
+       // Ensure that notifications to IME should be sent before getting next
+       // native event from the event queue.
+       // XXX Should we check the event message or event class instead of
+       //     using aIsHandlingNativeEvent?
+@@ -10567,8 +10595,39 @@ PresShell::NotifyStyleSheetServiceSheetR
+                                                uint32_t aSheetType)
+ {
+   if (!mStyleSet) {
+     return;
+   }
+ 
+   RemoveSheet(ToSheetType(aSheetType), aSheet);
+ }
++
++nsIContent*
++PresShell::GetOverrideClickTarget(WidgetGUIEvent* aEvent,
++                                  nsIFrame* aFrame)
++{
++  if (aEvent->mMessage != eMouseUp) {
++    return nullptr;
++  }
++
++  MOZ_ASSERT(aEvent->mClass == eMouseEventClass);
++  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
++
++  uint32_t flags = 0;
++  nsPoint eventPoint =
++    nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, aFrame);
++  if (mouseEvent->mIgnoreRootScrollFrame) {
++    flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
++  }
++
++  nsIFrame* target =
++    FindFrameTargetedByInputEvent(aEvent, aFrame, eventPoint, flags);
++  if (!target) {
++    return nullptr;
++  }
++
++  nsIContent* overrideClickTarget = target->GetContent();
++  while (overrideClickTarget && !overrideClickTarget->IsElement()) {
++    overrideClickTarget = overrideClickTarget->GetFlattenedTreeParent();
++  }
++  return overrideClickTarget;
++}
+diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h
+--- a/layout/base/PresShell.h
++++ b/layout/base/PresShell.h
+@@ -166,17 +166,19 @@ public:
+   virtual nsresult RemoveOverrideStyleSheet(mozilla::StyleSheet* aSheet) override;
+ 
+   virtual nsresult HandleEventWithTarget(
+                                  mozilla::WidgetEvent* aEvent,
+                                  nsIFrame* aFrame,
+                                  nsIContent* aContent,
+                                  nsEventStatus* aStatus,
+                                  bool aIsHandlingNativeEvent = false,
+-                                 nsIContent** aTargetContent = nullptr) override;
++                                 nsIContent** aTargetContent = nullptr,
++                                 nsIContent* aOverrideClickTarget = nullptr)
++                                 override;
+   virtual nsIFrame* GetEventTargetFrame() override;
+   virtual already_AddRefed<nsIContent> GetEventTargetContent(
+                                                      mozilla::WidgetEvent* aEvent) override;
+ 
+   virtual void NotifyCounterStylesAreDirty() override;
+ 
+   virtual void ReconstructFrames(void) override;
+   virtual void Freeze() override;
+@@ -677,17 +679,18 @@ protected:
+   void PopCurrentEventInfo();
+   /**
+    * @param aIsHandlingNativeEvent      true when the caller (perhaps) handles
+    *                                    an event which is caused by native
+    *                                    event.  Otherwise, false.
+    */
+   nsresult HandleEventInternal(mozilla::WidgetEvent* aEvent,
+                                nsEventStatus* aStatus,
+-                               bool aIsHandlingNativeEvent);
++                               bool aIsHandlingNativeEvent,
++                               nsIContent* aOverrideClickTarget = nullptr);
+ 
+   /*
+    * This and the next two helper methods are used to target and position the
+    * context menu when the keyboard shortcut is used to open it.
+    *
+    * If another menu is open, the context menu is opened relative to the
+    * active menuitem within the menu, or the menu itself if no item is active.
+    * Otherwise, if the caret is visible, the menu is opened near the caret.
+@@ -758,16 +761,18 @@ protected:
+   nsRevocableEventPtr<nsRunnableMethod<PresShell>> mUpdateApproximateFrameVisibilityEvent;
+ 
+   // A set of frames that were visible or could be visible soon at the time
+   // that we last did an approximate frame visibility update.
+   VisibleFrames mApproximatelyVisibleFrames;
+ 
+   nsresult SetResolutionImpl(float aResolution, bool aScaleToResolution);
+ 
++  nsIContent* GetOverrideClickTarget(WidgetGUIEvent* aEvent,
++                                     nsIFrame* aFrame);
+ #ifdef DEBUG
+   // The reflow root under which we're currently reflowing.  Null when
+   // not in reflow.
+   nsIFrame*                 mCurrentReflowRoot;
+   uint32_t                  mUpdateCount;
+ #endif
+ 
+ #ifdef MOZ_REFLOW_PERF
+diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h
+--- a/layout/base/nsIPresShell.h
++++ b/layout/base/nsIPresShell.h
+@@ -885,17 +885,18 @@ public:
+     * @note The caller must have a strong reference to the PresShell.
+     */
+   virtual nsresult HandleEventWithTarget(
+                                  mozilla::WidgetEvent* aEvent,
+                                  nsIFrame* aFrame,
+                                  nsIContent* aContent,
+                                  nsEventStatus* aStatus,
+                                  bool aIsHandlingNativeEvent = false,
+-                                 nsIContent** aTargetContent = nullptr) = 0;
++                                 nsIContent** aTargetContent = nullptr,
++                                 nsIContent* aOverrideClickTarget = nullptr) = 0;
+ 
+   /**
+    * Dispatch event to content only (NOT full processing)
+    * @note The caller must have a strong reference to the PresShell.
+    */
+   virtual nsresult HandleDOMEventWithTarget(
+                                  nsIContent* aTargetContent,
+                                  mozilla::WidgetEvent* aEvent,

+ 389 - 0
mozilla-release/patches/1447993-2-61a1.patch

@@ -0,0 +1,389 @@
+# HG changeset patch
+# User Olli Pettay <Olli.Pettay@helsinki.fi>
+# Date 1522342285 -10800
+# Node ID 19d82cc09359ed0737317c579418b57a0aa607ad
+# Parent  2f34a7d5a4948163f61b4931477533591b3bfc2d
+Bug 1447993, when handling pointerup while there is pointercapture, do a hit test in order to find the click target, tests, r=masayuki
+
+diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini
+--- a/dom/events/test/mochitest.ini
++++ b/dom/events/test/mochitest.ini
+@@ -185,9 +185,12 @@ skip-if = headless # Bug 1405869
+ [test_bug1305458.html]
+ [test_bug1298970.html]
+ [test_bug1304044.html]
+ [test_bug1332699.html]
+ [test_bug1339758.html]
+ [test_bug1369072.html]
+ support-files = window_bug1369072.html
+ skip-if = toolkit == 'android'
++[test_bug1447993.html]
++support-files = window_bug1447993.html
++skip-if = toolkit == 'android'
+ [test_dnd_with_modifiers.html]
+diff --git a/dom/events/test/test_bug1447993.html b/dom/events/test/test_bug1447993.html
+new file mode 100644
+--- /dev/null
++++ b/dom/events/test/test_bug1447993.html
+@@ -0,0 +1,42 @@
++<!DOCTYPE HTML>
++<html>
++<!--
++https://bugzilla.mozilla.org/show_bug.cgi?id=1447993
++-->
++<head>
++  <meta charset="utf-8">
++  <title>Test for Bug 1447993</title>
++  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
++  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
++  <script type="application/javascript">
++
++  /** Test for Bug 1447993 **/
++  SimpleTest.waitForExplicitFinish();
++
++  var win;
++  function start() {
++    SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true],
++                                       ["dom.w3c_touch_events.enabled", 1]]},
++    function() {
++      win = window.open("window_bug1447993.html", "testwindow",
++                        "width=" + window.screen.width +
++                        ",height=" + window.screen.height);
++    });
++  }
++
++  function done() {
++    setTimeout("SimpleTest.finish();");
++  }
++
++  </script>
++</head>
++<body onload="start();">
++<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1447993">Mozilla Bug 1447993</a>
++<p id="display"></p>
++<div id="content" style="display: none">
++
++</div>
++<pre id="test">
++</pre>
++</body>
++</html>
+diff --git a/dom/events/test/window_bug1447993.html b/dom/events/test/window_bug1447993.html
+new file mode 100644
+--- /dev/null
++++ b/dom/events/test/window_bug1447993.html
+@@ -0,0 +1,314 @@
++<!DOCTYPE HTML>
++<html>
++  <head>
++    <title>Test for Bug 1447993</title>
++    <style>
++      #area {
++        background: green;
++        border: 1px solid black;
++        width: 40px;
++        height: 40px;
++      }
++
++      #target {
++        background: blue;
++        border: 1px solid black;
++        width: 20px;
++        height: 20px;
++        margin: 10px;
++      }
++    </style>
++    <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
++    <script>
++
++    var tests = [
++      topLevelDocumentEventHandling,
++      topLevelDocumentEventHandlingWithTouch,
++      iframeEventHandling,
++      iframeEventHandlingWithCapturingInParent,
++      iframeEventHandlingwithHiddenCapturingInParent
++    ];
++
++    function next() {
++      if (!tests.length) {
++        opener.done();
++        window.close();
++      } else {
++        var test = tests.shift();
++        requestAnimationFrame(function() { setTimeout(test); });
++      }
++    }
++
++    function start() {
++      next();
++    }
++
++    function topLevelDocumentEventHandling() {
++      var pid;
++      var area = document.getElementById("area");
++      var target = document.getElementById("target");
++      var body = document.body;
++      var html = document.documentElement;
++      var eventLog = [];
++      function captureEvent(e) {
++        eventLog.push([e.type, e.composedPath()]);
++      }
++      var expectedEvents = [
++        ["pointerdown", [ target, area, body, html, document, window ]],
++        ["mousedown", [ target, area, body, html, document, window ]],
++        ["pointerup", [ area, body, html, document, window ]],
++        ["mouseup", [ area, body, html, document, window ]],
++        ["click", [ target, area, body, html, document, window ]],
++        ];
++
++      window.addEventListener("pointerdown",
++        function(e) {
++          captureEvent(e);
++          pid = e.pointerId;
++          area.setPointerCapture(pid);
++        }, { once: true});
++      window.addEventListener("mousedown",
++        function(e) {
++          captureEvent(e);
++        }, { once: true});
++      window.addEventListener("pointerup",
++        function(e) {
++          captureEvent(e);
++          area.releasePointerCapture(pid);
++        }, { once: true});
++      window.addEventListener("mouseup", function(e) {
++          captureEvent(e);
++        }, { once: true});
++      window.addEventListener("click", function(e) {
++          captureEvent(e);
++        }, { once: true});
++
++      synthesizeMouseAtCenter(target, {}, window);
++
++      opener.is(eventLog.length, expectedEvents.length,
++                "[topLevelDocumentEventHandling] Same number events expected.");
++      for (var i = 0; i < eventLog.length; ++i) {
++        opener.is(eventLog[i][0], expectedEvents[i][0],
++                  `topLevelDocumentEventHandling ${i}`);
++        for (var j = 0; j < eventLog[i][1].length; ++j) {
++          opener.is(eventLog[i][1][j], expectedEvents[i][1][j],
++                    `topLevelDocumentEventHandling ${i} ${j}`);
++        }
++      }
++      next();
++    }
++
++    function topLevelDocumentEventHandlingWithTouch() {
++      var pid;
++      var area = document.getElementById("area");
++      var target = document.getElementById("target");
++      var body = document.body;
++      var html = document.documentElement;
++      var eventLog = [];
++      function captureEvent(e) {
++        eventLog.push([e.type, e.composedPath()]);
++      }
++      var expectedEvents = [
++        ["pointerdown", [ target, area, body, html, document, window ]],
++        ["touchstart", [ target, area, body, html, document, window ]],
++        ["pointerup", [ area, body, html, document, window ]],
++        ["touchend", [ target, area, body, html, document, window ]],
++        /*
++        // Right now touch event initated mousedown/up (and then click) are
++        // dispatched in APZ, and there isn't JS exposed way to test that.
++        ["mousedown", [ target, area, body, html, document, window ]],
++        ["mouseup", [ target, area, body, html, document, window ]],
++        ["click", [ target, area, body, html, document, window ]],*/
++        ];
++
++      window.addEventListener("pointerdown",
++        function(e) {
++          captureEvent(e);
++          pid = e.pointerId;
++          area.setPointerCapture(pid);
++        }, { once: true});
++      window.addEventListener("touchstart", function(e) {
++          captureEvent(e);
++        }, { once: true});
++      window.addEventListener("pointerup",
++        function(e) {
++          captureEvent(e);
++          try {
++            area.releasePointerCapture(pid);
++          } catch(ex) {}
++        }, { once: true});
++      window.addEventListener("touchend", function(e) {
++          captureEvent(e);
++        }, { once: true});
++      /*window.addEventListener("mousedown",
++        function(e) {
++          captureEvent(e);
++        }, { once: true});
++      window.addEventListener("mouseup", function(e) {
++          captureEvent(e);
++        }, { once: true});
++      window.addEventListener("click", function(e) {
++          captureEvent(e);
++        }, { once: true});*/
++
++      synthesizeTouchAtCenter(target, {}, window);
++
++      opener.is(eventLog.length, expectedEvents.length,
++                "[topLevelDocumentEventHandlingWithTouch] Same number events expected.");
++      for (var i = 0; i < eventLog.length; ++i) {
++        opener.is(eventLog[i][0], expectedEvents[i][0],
++                  `topLevelDocumentEventHandlingWithTouch ${i}`);
++        for (var j = 0; j < eventLog[i][1].length; ++j) {
++          opener.is(eventLog[i][1][j], expectedEvents[i][1][j],
++                    `topLevelDocumentEventHandlingWithTouch ${i} ${j}`);
++        }
++      }
++      next();
++    }
++
++    function iframeEventHandling() {
++      var pid;
++      var iframe = document.getElementById("iframe");
++      var doc = iframe.contentDocument;
++      doc.head.innerHTML = "<style>" + document.getElementsByTagName("style")[0].textContent + "</style>";
++      var area = doc.createElement("div");
++      area.id = "area";
++      var target = doc.createElement("div");
++      target.id = "target";
++      area.appendChild(target);
++      doc.body.appendChild(area);
++      var body = doc.body;
++      var html = doc.documentElement;
++      var win = doc.defaultView;
++      var eventLog = [];
++      function captureEvent(e) {
++        eventLog.push([e.type, e.composedPath()]);
++      }
++      var expectedEvents = [
++        ["pointerdown", [ target, area, body, html, doc, win ]],
++        ["mousedown", [ target, area, body, html, doc, win ]],
++        ["pointerup", [ area, body, html, doc, win ]],
++        ["mouseup", [ area, body, html, doc, win ]],
++        ["click", [ target, area, body, html, doc, win ]],
++        ];
++
++      win.addEventListener("pointerdown",
++        function(e) {
++          captureEvent(e);
++          pid = e.pointerId;
++          area.setPointerCapture(pid);
++        }, { once: true});
++      win.addEventListener("mousedown",
++        function(e) {
++          captureEvent(e);
++        }, { once: true});
++      win.addEventListener("pointerup",
++        function(e) {
++          captureEvent(e);
++          area.releasePointerCapture(pid);
++        }, { once: true});
++      win.addEventListener("mouseup", function(e) {
++          captureEvent(e);
++        }, { once: true});
++      win.addEventListener("click", function(e) {
++          captureEvent(e);
++        }, { once: true});
++
++      synthesizeMouseAtCenter(target, {}, win);
++
++      opener.is(eventLog.length, expectedEvents.length,
++                "[iframeEventHandling] Same number events expected.");
++      for (var i = 0; i < eventLog.length; ++i) {
++        opener.is(eventLog[i][0], expectedEvents[i][0],
++                  `iframeEventHandling ${i}`);
++        for (var j = 0; j < eventLog[i][1].length; ++j) {
++          opener.is(eventLog[i][1][j], expectedEvents[i][1][j],
++                    `iframeEventHandling ${i} ${j}`);
++        }
++      }
++      next();
++    }
++
++    function iframeEventHandlingWithCapturingInParent(
++      name = "iframeEventHandlingWithCapturingInParent") {
++      var pid;
++      var iframe = document.getElementById("iframe");
++      var doc = iframe.contentDocument;
++      doc.body.innerHTML = "";
++      doc.head.innerHTML =
++        "<style>" + document.getElementsByTagName("style")[0].textContent + "</style>";
++      var area = doc.createElement("div");
++      area.id = "area";
++      var target = doc.createElement("div");
++      target.id = "target";
++      area.appendChild(target);
++      doc.body.appendChild(area);
++      var body = doc.body;
++      var html = doc.documentElement;
++      var win = doc.defaultView;
++      var targetOutsideIframe = document.getElementById("targetOutsideIframe");
++      var eventLog = [];
++      function captureEvent(e) {
++        eventLog.push([e.type, e.composedPath()]);
++      }
++      var expectedEvents = [
++        ["pointerdown", [ target, area, body, html, doc, win ]],
++        ["mousedown", [ target, area, body, html, doc, win ]],
++        ["pointerup", [ targetOutsideIframe, document.body, document.documentElement, document, window ]],
++        ["mouseup", [ targetOutsideIframe, document.body, document.documentElement, document, window ]],
++        ["click", [ target, area, body, html, doc, win ]],
++        ];
++
++      win.addEventListener("pointerdown",
++        function(e) {
++          captureEvent(e);
++          pid = e.pointerId;
++          targetOutsideIframe.setPointerCapture(pid);
++        }, { once: true});
++      win.addEventListener("mousedown",
++        function(e) {
++          captureEvent(e);
++        }, { once: true});
++      window.addEventListener("pointerup",
++        function(e) {
++          captureEvent(e);
++          targetOutsideIframe.releasePointerCapture(pid);
++        }, { once: true});
++      window.addEventListener("mouseup", function(e) {
++          captureEvent(e);
++        }, { once: true});
++      win.addEventListener("click", function(e) {
++          captureEvent(e);
++        }, { once: true});
++
++      synthesizeMouseAtCenter(target, {}, win);
++
++      opener.is(eventLog.length, expectedEvents.length,
++                `[${name}] Same number events expected.`);
++      for (var i = 0; i < eventLog.length; ++i) {
++        opener.is(eventLog[i][0], expectedEvents[i][0],
++                  `${name} ${i}`);
++        for (var j = 0; j < eventLog[i][1].length; ++j) {
++          opener.is(eventLog[i][1][j], expectedEvents[i][1][j],
++                    `${name} ${i} ${j}`);
++        }
++      }
++      next();
++    }
++
++    function iframeEventHandlingwithHiddenCapturingInParent() {
++      document.getElementById("targetOutsideIframe").style.display = "none";
++      document.getElementById("targetOutsideIframe").offsetLeft;
++      iframeEventHandlingWithCapturingInParent("iframeEventHandlingwithHiddenCapturingInParent");
++    }
++
++    </script>
++  </head>
++  <body onload="start();">
++    <div id="area">
++      <div id="target"></div>
++    </div>
++    <iframe id="iframe"></iframe>
++    <h5 id="targetOutsideIframe"></h5>
++  </body>
++</html>

+ 4 - 4
mozilla-release/patches/1457338-62a1.patch

@@ -2,7 +2,7 @@
 # User Johann Hofmann <jhofmann@mozilla.com>
 # User Johann Hofmann <jhofmann@mozilla.com>
 # Date 1525439197 -7200
 # Date 1525439197 -7200
 # Node ID 1aeaff1e13f80f4ca820497345b279e67667f23b
 # Node ID 1aeaff1e13f80f4ca820497345b279e67667f23b
-# Parent  779ebe4379bd1dac988498f403ac331513382fff
+# Parent  a92406bef33e999ceae8845b7105b1dda5d463ce
 Bug 1457338 - Stop touch drag on mouseUp. r=kats
 Bug 1457338 - Stop touch drag on mouseUp. r=kats
 
 
 MozReview-Commit-ID: 4uPibB3DR1D
 MozReview-Commit-ID: 4uPibB3DR1D
@@ -10,7 +10,7 @@ MozReview-Commit-ID: 4uPibB3DR1D
 diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
 diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
 --- a/dom/events/EventStateManager.cpp
 --- a/dom/events/EventStateManager.cpp
 +++ b/dom/events/EventStateManager.cpp
 +++ b/dom/events/EventStateManager.cpp
-@@ -643,16 +643,17 @@ EventStateManager::PreHandleEvent(nsPres
+@@ -644,16 +644,17 @@ EventStateManager::PreHandleEvent(nsPres
      break;
      break;
    }
    }
    case eMouseUp: {
    case eMouseUp: {
@@ -26,5 +26,5 @@ diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
          MOZ_FALLTHROUGH;
          MOZ_FALLTHROUGH;
        case WidgetMouseEvent::eRightButton:
        case WidgetMouseEvent::eRightButton:
        case WidgetMouseEvent::eMiddleButton:
        case WidgetMouseEvent::eMiddleButton:
-         SetClickCount(mouseEvent, aStatus);
-         NotifyTargetUserActivation(aEvent, aTargetContent);
+         RefPtr<EventStateManager> esm = ESMFromContentOrThis(aOverrideClickTarget);
+         esm->SetClickCount(mouseEvent, aStatus, aOverrideClickTarget);

+ 22 - 22
mozilla-release/patches/1465585-3-std-62a1.patch

@@ -2,7 +2,7 @@
 # User Emilio Cobos Alvarez <emilio@crisal.io>
 # User Emilio Cobos Alvarez <emilio@crisal.io>
 # Date 1527707735 -7200
 # Date 1527707735 -7200
 # Node ID b54db66223586b4e04f5cb926fccdacf8a176b91
 # Node ID b54db66223586b4e04f5cb926fccdacf8a176b91
-# Parent  c1820eaab39335c88ae7de577048e54c4fb52136
+# Parent  d602326c54e6ef5cb413805b8d764304eb6effd5
 Bug 1465585: Switch from mozilla::Move to std::move. r=froydnj
 Bug 1465585: Switch from mozilla::Move to std::move. r=froydnj
 
 
 This was done automatically replacing:
 This was done automatically replacing:
@@ -126,7 +126,7 @@ diff --git a/accessible/base/EventTree.cpp b/accessible/base/EventTree.cpp
 diff --git a/accessible/base/NotificationController.cpp b/accessible/base/NotificationController.cpp
 diff --git a/accessible/base/NotificationController.cpp b/accessible/base/NotificationController.cpp
 --- a/accessible/base/NotificationController.cpp
 --- a/accessible/base/NotificationController.cpp
 +++ b/accessible/base/NotificationController.cpp
 +++ b/accessible/base/NotificationController.cpp
-@@ -752,17 +752,17 @@ NotificationController::WillRefresh(mozi
+@@ -781,17 +781,17 @@ NotificationController::WillRefresh(mozi
      }
      }
  
  
      nsIContent* ownerContent = mDocument->DocumentNode()->
      nsIContent* ownerContent = mDocument->DocumentNode()->
@@ -145,7 +145,7 @@ diff --git a/accessible/base/NotificationController.cpp b/accessible/base/Notifi
  
  
        // Failed to bind the child document, destroy it.
        // Failed to bind the child document, destroy it.
        childDoc->Shutdown();
        childDoc->Shutdown();
-@@ -824,17 +824,17 @@ NotificationController::WillRefresh(mozi
+@@ -853,17 +853,17 @@ NotificationController::WillRefresh(mozi
    // events causes script to run.
    // events causes script to run.
    mObservingState = eRefreshProcessing;
    mObservingState = eRefreshProcessing;
  
  
@@ -167,7 +167,7 @@ diff --git a/accessible/base/NotificationController.cpp b/accessible/base/Notifi
 diff --git a/accessible/base/NotificationController.h b/accessible/base/NotificationController.h
 diff --git a/accessible/base/NotificationController.h b/accessible/base/NotificationController.h
 --- a/accessible/base/NotificationController.h
 --- a/accessible/base/NotificationController.h
 +++ b/accessible/base/NotificationController.h
 +++ b/accessible/base/NotificationController.h
-@@ -282,17 +282,17 @@ private:
+@@ -288,17 +288,17 @@ private:
    virtual void WillRefresh(mozilla::TimeStamp aTime) override;
    virtual void WillRefresh(mozilla::TimeStamp aTime) override;
  
  
    /**
    /**
@@ -255,7 +255,7 @@ diff --git a/accessible/base/TextRange.h b/accessible/base/TextRange.h
 diff --git a/accessible/generic/HyperTextAccessible.cpp b/accessible/generic/HyperTextAccessible.cpp
 diff --git a/accessible/generic/HyperTextAccessible.cpp b/accessible/generic/HyperTextAccessible.cpp
 --- a/accessible/generic/HyperTextAccessible.cpp
 --- a/accessible/generic/HyperTextAccessible.cpp
 +++ b/accessible/generic/HyperTextAccessible.cpp
 +++ b/accessible/generic/HyperTextAccessible.cpp
-@@ -1783,17 +1783,17 @@ HyperTextAccessible::SelectionRanges(nsT
+@@ -1790,17 +1790,17 @@ HyperTextAccessible::SelectionRanges(nsT
        startContainer->DOMPointToOffset(DOMRange->GetStartContainer(),
        startContainer->DOMPointToOffset(DOMRange->GetStartContainer(),
                                         DOMRange->StartOffset(), false);
                                         DOMRange->StartOffset(), false);
      int32_t endOffset =
      int32_t endOffset =
@@ -604,7 +604,7 @@ diff --git a/accessible/ipc/win/DocAccessibleChild.h b/accessible/ipc/win/DocAcc
 diff --git a/accessible/ipc/win/HandlerProvider.cpp b/accessible/ipc/win/HandlerProvider.cpp
 diff --git a/accessible/ipc/win/HandlerProvider.cpp b/accessible/ipc/win/HandlerProvider.cpp
 --- a/accessible/ipc/win/HandlerProvider.cpp
 --- a/accessible/ipc/win/HandlerProvider.cpp
 +++ b/accessible/ipc/win/HandlerProvider.cpp
 +++ b/accessible/ipc/win/HandlerProvider.cpp
-@@ -36,17 +36,17 @@
+@@ -38,17 +38,17 @@
  namespace mozilla {
  namespace mozilla {
  namespace a11y {
  namespace a11y {
  
  
@@ -623,7 +623,7 @@ diff --git a/accessible/ipc/win/HandlerProvider.cpp b/accessible/ipc/win/Handler
  {
  {
    if (!ppv) {
    if (!ppv) {
      return E_INVALIDARG;
      return E_INVALIDARG;
-@@ -493,31 +493,31 @@ HandlerProvider::GetEffectiveOutParamIid
+@@ -497,31 +497,31 @@ HandlerProvider::GetEffectiveOutParamIid
    return IID_IUnknown;
    return IID_IUnknown;
  }
  }
  
  
@@ -657,7 +657,7 @@ diff --git a/accessible/ipc/win/HandlerProvider.cpp b/accessible/ipc/win/Handler
  {
  {
    MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    MOZ_ASSERT(mscom::IsCurrentThreadMTA());
  
  
-@@ -525,17 +525,17 @@ HandlerProvider::put_HandlerControl(long
+@@ -529,17 +529,17 @@ HandlerProvider::put_HandlerControl(long
      return E_INVALIDARG;
      return E_INVALIDARG;
    }
    }
  
  
@@ -676,7 +676,7 @@ diff --git a/accessible/ipc/win/HandlerProvider.cpp b/accessible/ipc/win/Handler
  
  
  HRESULT
  HRESULT
  HandlerProvider::Refresh(DynamicIA2Data* aOutData)
  HandlerProvider::Refresh(DynamicIA2Data* aOutData)
-@@ -553,17 +553,17 @@ HandlerProvider::Refresh(DynamicIA2Data*
+@@ -561,17 +561,17 @@ HandlerProvider::Refresh(DynamicIA2Data*
  
  
  template<typename Interface>
  template<typename Interface>
  HRESULT
  HRESULT
@@ -30251,7 +30251,7 @@ diff --git a/ipc/mscom/MainThreadHandoff.h b/ipc/mscom/MainThreadHandoff.h
 diff --git a/ipc/mscom/MainThreadInvoker.cpp b/ipc/mscom/MainThreadInvoker.cpp
 diff --git a/ipc/mscom/MainThreadInvoker.cpp b/ipc/mscom/MainThreadInvoker.cpp
 --- a/ipc/mscom/MainThreadInvoker.cpp
 --- a/ipc/mscom/MainThreadInvoker.cpp
 +++ b/ipc/mscom/MainThreadInvoker.cpp
 +++ b/ipc/mscom/MainThreadInvoker.cpp
-@@ -103,17 +103,17 @@ MainThreadInvoker::MainThreadInvoker()
+@@ -131,17 +131,17 @@ MainThreadInvoker::MainThreadInvoker()
  {
  {
    static const bool gotStatics = InitStatics();
    static const bool gotStatics = InitStatics();
    MOZ_ASSERT(gotStatics);
    MOZ_ASSERT(gotStatics);
@@ -40634,7 +40634,7 @@ diff --git a/layout/base/GeckoRestyleManager.cpp b/layout/base/GeckoRestyleManag
 diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
 diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
 --- a/layout/base/PresShell.cpp
 --- a/layout/base/PresShell.cpp
 +++ b/layout/base/PresShell.cpp
 +++ b/layout/base/PresShell.cpp
-@@ -4130,17 +4130,17 @@ PresShell::DoFlushPendingNotifications(m
+@@ -4172,17 +4172,17 @@ PresShell::DoFlushPendingNotifications(m
          mPresContext->EffectCompositor()->PostRestyleForThrottledAnimations();
          mPresContext->EffectCompositor()->PostRestyleForThrottledAnimations();
        }
        }
  
  
@@ -40653,7 +40653,7 @@ diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
      }
      }
  
  
      // Process whatever XBL constructors those restyles queued up.  This
      // Process whatever XBL constructors those restyles queued up.  This
-@@ -4156,17 +4156,17 @@ PresShell::DoFlushPendingNotifications(m
+@@ -4198,17 +4198,17 @@ PresShell::DoFlushPendingNotifications(m
      // date.  If it's not, then style context reparenting, which can
      // date.  If it's not, then style context reparenting, which can
      // happen during reflow, might suddenly pick up the new rules and
      // happen during reflow, might suddenly pick up the new rules and
      // we'll end up with frames whose style doesn't match the frame
      // we'll end up with frames whose style doesn't match the frame
@@ -40672,7 +40672,7 @@ diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
  
  
      didStyleFlush = true;
      didStyleFlush = true;
  
  
-@@ -4176,17 +4176,17 @@ PresShell::DoFlushPendingNotifications(m
+@@ -4218,17 +4218,17 @@ PresShell::DoFlushPendingNotifications(m
      // be good.
      // be good.
  
  
      if (flushType >= (SuppressInterruptibleReflows()
      if (flushType >= (SuppressInterruptibleReflows()
@@ -40691,7 +40691,7 @@ diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
        if (ProcessReflowCommands(flushType < FlushType::Layout) &&
        if (ProcessReflowCommands(flushType < FlushType::Layout) &&
            mContentToScrollTo) {
            mContentToScrollTo) {
          // We didn't get interrupted.  Go ahead and scroll to our content
          // We didn't get interrupted.  Go ahead and scroll to our content
-@@ -5098,17 +5098,17 @@ PresShell::RenderNode(nsIDOMNode* aNode,
+@@ -5140,17 +5140,17 @@ PresShell::RenderNode(nsIDOMNode* aNode,
    if (!node->IsInUncomposedDoc())
    if (!node->IsInUncomposedDoc())
      return nullptr;
      return nullptr;
  
  
@@ -40710,7 +40710,7 @@ diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
      nsIntRect rrectPixels = aRegion->GetBounds();
      nsIntRect rrectPixels = aRegion->GetBounds();
  
  
      nsRect rrect = ToAppUnits(rrectPixels, nsPresContext::AppUnitsPerCSSPixel());
      nsRect rrect = ToAppUnits(rrectPixels, nsPresContext::AppUnitsPerCSSPixel());
-@@ -5146,17 +5146,17 @@ PresShell::RenderSelection(nsISelection*
+@@ -5188,17 +5188,17 @@ PresShell::RenderSelection(nsISelection*
    NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection");
    NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection");
  
  
    for (int32_t r = 0; r < numRanges; r++)
    for (int32_t r = 0; r < numRanges; r++)
@@ -40729,7 +40729,7 @@ diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
                               aScreenRect, aFlags);
                               aScreenRect, aFlags);
  }
  }
  
  
-@@ -5490,17 +5490,17 @@ void PresShell::SynthesizeMouseMove(bool
+@@ -5532,17 +5532,17 @@ void PresShell::SynthesizeMouseMove(bool
          new nsSynthMouseMoveEvent(this, aFromScroll);
          new nsSynthMouseMoveEvent(this, aFromScroll);
  
  
      if (!GetPresContext()->RefreshDriver()
      if (!GetPresContext()->RefreshDriver()
@@ -40748,7 +40748,7 @@ diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
   * view tree that contains the point. Thus more deeply nested floating views
   * view tree that contains the point. Thus more deeply nested floating views
   * are preferred over their ancestors, and floating views earlier in the
   * are preferred over their ancestors, and floating views earlier in the
   * view hierarchy (i.e., added later) are preferred over their siblings.
   * view hierarchy (i.e., added later) are preferred over their siblings.
-@@ -6043,17 +6043,17 @@ PresShell::ScheduleApproximateFrameVisib
+@@ -6085,17 +6085,17 @@ PresShell::ScheduleApproximateFrameVisib
    RefPtr<nsRunnableMethod<PresShell>> event =
    RefPtr<nsRunnableMethod<PresShell>> event =
      NewRunnableMethod("PresShell::UpdateApproximateFrameVisibility",
      NewRunnableMethod("PresShell::UpdateApproximateFrameVisibility",
                        this,
                        this,
@@ -40767,7 +40767,7 @@ diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
  {
  {
    if (!aFrame->TrackingVisibility()) {
    if (!aFrame->TrackingVisibility()) {
      return;
      return;
-@@ -6227,17 +6227,17 @@ PresShell::Paint(nsView*         aViewTo
+@@ -6269,17 +6269,17 @@ PresShell::Paint(nsView*         aViewTo
        bool computeInvalidRect = computeInvalidFunc ||
        bool computeInvalidRect = computeInvalidFunc ||
                                  (layerManager->GetBackendType() == LayersBackend::LAYERS_BASIC);
                                  (layerManager->GetBackendType() == LayersBackend::LAYERS_BASIC);
  
  
@@ -40903,12 +40903,12 @@ diff --git a/layout/base/gtest/TestAccessibleCaretEventHub.cpp b/layout/base/gte
 +    return std::move(event);
 +    return std::move(event);
    }
    }
  
  
+   MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestAsyncPanZoomScroll();
+ 
+   MOZ_CAN_RUN_SCRIPT_BOUNDARY
    void HandleEventAndCheckState(UniquePtr<WidgetEvent> aEvent,
    void HandleEventAndCheckState(UniquePtr<WidgetEvent> aEvent,
                                  MockAccessibleCaretEventHub::State* aExpectedState,
                                  MockAccessibleCaretEventHub::State* aExpectedState,
                                  nsEventStatus aExpectedEventStatus)
                                  nsEventStatus aExpectedEventStatus)
-   {
-     nsEventStatus rv = mHub->HandleEvent(aEvent.get());
-     EXPECT_EQ(mHub->GetState(), aExpectedState);
 diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
 diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
 --- a/layout/base/nsCSSFrameConstructor.cpp
 --- a/layout/base/nsCSSFrameConstructor.cpp
 +++ b/layout/base/nsCSSFrameConstructor.cpp
 +++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -49432,7 +49432,7 @@ diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNS
 diff --git a/security/manager/ssl/nsNSSIOLayer.cpp b/security/manager/ssl/nsNSSIOLayer.cpp
 diff --git a/security/manager/ssl/nsNSSIOLayer.cpp b/security/manager/ssl/nsNSSIOLayer.cpp
 --- a/security/manager/ssl/nsNSSIOLayer.cpp
 --- a/security/manager/ssl/nsNSSIOLayer.cpp
 +++ b/security/manager/ssl/nsNSSIOLayer.cpp
 +++ b/security/manager/ssl/nsNSSIOLayer.cpp
-@@ -2232,17 +2232,17 @@ ClientAuthDataRunnable::RunOnTargetThrea
+@@ -2230,17 +2230,17 @@ ClientAuthDataRunnable::RunOnTargetThrea
        }
        }
        if (PR_GetError() == SEC_ERROR_BAD_PASSWORD) {
        if (PR_GetError() == SEC_ERROR_BAD_PASSWORD) {
          // problem with password: bail
          // problem with password: bail

+ 4 - 4
mozilla-release/patches/1675319-84a1.patch

@@ -2,7 +2,7 @@
 # User Andrew Halberstadt <ahal@mozilla.com>
 # User Andrew Halberstadt <ahal@mozilla.com>
 # Date 1604606108 0
 # Date 1604606108 0
 # Node ID ecb229f8ba6f38de279dd9ebbd2ad641a53a25d2
 # Node ID ecb229f8ba6f38de279dd9ebbd2ad641a53a25d2
-# Parent  4cb37db2f8c06e74a70b056014ad68663ab67d7d
+# Parent  9acd6d4f77acf761dae60663a29efc4259d9468c
 Bug 1675319 - [manifestparser] Properly support multiline skip-if statements, r=jmaher,extension-reviewers,zombie
 Bug 1675319 - [manifestparser] Properly support multiline skip-if statements, r=jmaher,extension-reviewers,zombie
 
 
 This supports one manifestparser expression per line in the 'skip-if',
 This supports one manifestparser expression per line in the 'skip-if',
@@ -135,7 +135,7 @@ diff --git a/docshell/test/navigation/mochitest.ini b/docshell/test/navigation/m
 diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini
 diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini
 --- a/dom/base/test/mochitest.ini
 --- a/dom/base/test/mochitest.ini
 +++ b/dom/base/test/mochitest.ini
 +++ b/dom/base/test/mochitest.ini
-@@ -802,11 +802,14 @@ tags = audiochannel
+@@ -803,11 +803,14 @@ tags = audiochannel
  [test_window_keys.html]
  [test_window_keys.html]
  [test_window_named_frame_enumeration.html]
  [test_window_named_frame_enumeration.html]
  [test_window_orientation.html]
  [test_window_orientation.html]
@@ -153,14 +153,14 @@ diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini
 diff --git a/dom/events/test/pointerevents/mochitest.ini b/dom/events/test/pointerevents/mochitest.ini
 diff --git a/dom/events/test/pointerevents/mochitest.ini b/dom/events/test/pointerevents/mochitest.ini
 --- a/dom/events/test/pointerevents/mochitest.ini
 --- a/dom/events/test/pointerevents/mochitest.ini
 +++ b/dom/events/test/pointerevents/mochitest.ini
 +++ b/dom/events/test/pointerevents/mochitest.ini
-@@ -131,11 +131,13 @@ support-files =
-     pointerevent_touch-action-pan-left-css_touch-manual.html
+@@ -142,11 +142,13 @@ support-files =
      pointerevent_touch-action-pan-right-css_touch-manual.html
      pointerevent_touch-action-pan-right-css_touch-manual.html
      pointerevent_touch-action-pan-up-css_touch-manual.html
      pointerevent_touch-action-pan-up-css_touch-manual.html
  [test_trigger_fullscreen_by_pointer_events.html]
  [test_trigger_fullscreen_by_pointer_events.html]
    support-files =
    support-files =
      file_test_trigger_fullscreen.html
      file_test_trigger_fullscreen.html
  [test_trigger_popup_by_pointer_events.html]
  [test_trigger_popup_by_pointer_events.html]
+ [test_remove_frame_when_got_pointer_capture.html]
  [test_getCoalescedEvents.html]
  [test_getCoalescedEvents.html]
 -  skip-if = !e10s
 -  skip-if = !e10s
 +skip-if =
 +skip-if =

+ 6 - 6
mozilla-release/patches/TOP-NOBUG-enableCE-25318.patch

@@ -1,7 +1,7 @@
 # HG changeset patch
 # HG changeset patch
 # User Frank-Rainer Grahl <frgrahl@gmx.net>
 # User Frank-Rainer Grahl <frgrahl@gmx.net>
 # Date 1687984439 -7200
 # Date 1687984439 -7200
-# Parent  4621703876bbff10e220842a570e6f663c8e3053
+# Parent  c3ea527fb4ca650940cef8e76d3cfad46130e16a
 No Bug - Enable Custom Elements / Web Components. r=me a=me
 No Bug - Enable Custom Elements / Web Components. r=me a=me
 
 
 Shadow DOM is still mia but better than nothing.
 Shadow DOM is still mia but better than nothing.
@@ -9,14 +9,14 @@ Shadow DOM is still mia but better than nothing.
 diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
 diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
 --- a/modules/libpref/init/all.js
 --- a/modules/libpref/init/all.js
 +++ b/modules/libpref/init/all.js
 +++ b/modules/libpref/init/all.js
-@@ -1583,18 +1583,19 @@ pref("dom.event.contextmenu.enabled",   
+@@ -1578,18 +1578,19 @@ pref("privacy.trackingprotection.lower_n
+ pref("privacy.trackingprotection.lower_network_priority", false);
+ #endif
+ 
+ pref("dom.event.contextmenu.enabled",       true);
  pref("dom.event.clipboardevents.enabled",   true);
  pref("dom.event.clipboardevents.enabled",   true);
  pref("dom.event.highrestimestamp.enabled",  true);
  pref("dom.event.highrestimestamp.enabled",  true);
- #ifdef NIGHTLY_BUILD
  pref("dom.event.coalesce_mouse_move",       true);
  pref("dom.event.coalesce_mouse_move",       true);
- #else
- pref("dom.event.coalesce_mouse_move",       false);
- #endif
  
  
 -pref("dom.webcomponents.shadowdom.enabled", false);
 -pref("dom.webcomponents.shadowdom.enabled", false);
 -pref("dom.webcomponents.customelements.enabled", false);
 -pref("dom.webcomponents.customelements.enabled", false);

+ 34 - 0
mozilla-release/patches/series

@@ -31,6 +31,9 @@ NOBUG-20170803-promisehelper-57a1.patch
 1386275-57a1.patch
 1386275-57a1.patch
 1363723-1-FIX-57a1.patch
 1363723-1-FIX-57a1.patch
 1363723-2-57a1.patch
 1363723-2-57a1.patch
+1382092-1-57a1.patch
+1382092-2-57a1.patch
+1382092-3-57a1.patch
 1373563-1-57a1.patch
 1373563-1-57a1.patch
 1380617-01-57a1.patch
 1380617-01-57a1.patch
 1380617-02-57a1.patch
 1380617-02-57a1.patch
@@ -41,8 +44,12 @@ NOBUG-20170803-promisehelper-57a1.patch
 1380617-07-57a1.patch
 1380617-07-57a1.patch
 1380617-08no09-57a1.patch
 1380617-08no09-57a1.patch
 1388954-57a1.patch
 1388954-57a1.patch
+1381186-1-57a1.patch
+1381186-2-57a1.patch
 1372927-57a1.patch
 1372927-57a1.patch
 1380617-10-57a1.patch
 1380617-10-57a1.patch
+1382092-4-57a1.patch
+1390018-57a1.patch
 1380512-57a1.patch
 1380512-57a1.patch
 1390675-57a1.patch
 1390675-57a1.patch
 1390693-8-57a1.patch
 1390693-8-57a1.patch
@@ -75,6 +82,7 @@ NOBUG-20170803-promisehelper-57a1.patch
 1394804-3-57a1.patch
 1394804-3-57a1.patch
 1391421-2no1-58a1.patch
 1391421-2no1-58a1.patch
 1391421-4no3or5to9-58a1.patch
 1391421-4no3or5to9-58a1.patch
+1382388-58a1.patch
 1400846-58a1.patch
 1400846-58a1.patch
 1401187-1-58a1.patch
 1401187-1-58a1.patch
 1401187-2-58a1.patch
 1401187-2-58a1.patch
@@ -207,6 +215,7 @@ NOBUG-20170803-promisehelper-57a1.patch
 1407830-2-58a1.patch
 1407830-2-58a1.patch
 1407830-3-58a1.patch
 1407830-3-58a1.patch
 1387827-58a1.patch
 1387827-58a1.patch
+1399780-58a1.patch
 1408870-58a1.patch
 1408870-58a1.patch
 1407550-58a1.patch
 1407550-58a1.patch
 833747-58a1.patch
 833747-58a1.patch
@@ -366,6 +375,7 @@ NOBUG-20170803-promisehelper-57a1.patch
 1414340-2-fix-58a1.patch
 1414340-2-fix-58a1.patch
 1411402-58a1.patch
 1411402-58a1.patch
 1416358-58a1.patch
 1416358-58a1.patch
+1415572-59a1.patch
 1353319-1-59a1.patch
 1353319-1-59a1.patch
 1353319-2-59a1.patch
 1353319-2-59a1.patch
 1353319-3-59a1.patch
 1353319-3-59a1.patch
@@ -466,6 +476,7 @@ NOBUG-20170803-promisehelper-57a1.patch
 1419665-59a1.patch
 1419665-59a1.patch
 1381648-59a1.patch
 1381648-59a1.patch
 1419416-59a1.patch
 1419416-59a1.patch
+1417657-59a1.patch
 1419075-59a1.patch
 1419075-59a1.patch
 1408944-59a1.patch
 1408944-59a1.patch
 1419382-1-59a1.patch
 1419382-1-59a1.patch
@@ -590,6 +601,7 @@ NOBUG-20170803-promisehelper-57a1.patch
 1420594-3-59a1.patch
 1420594-3-59a1.patch
 1419592-59a1.patch
 1419592-59a1.patch
 1422983-59a1.patch
 1422983-59a1.patch
+1422869-59a1.patch
 1419369-59a1.patch
 1419369-59a1.patch
 1220758-59a1.patch
 1220758-59a1.patch
 1408949-1-59a1.patch
 1408949-1-59a1.patch
@@ -711,6 +723,7 @@ NOBUG-20170803-promisehelper-57a1.patch
 1421491-59a1.patch
 1421491-59a1.patch
 1426251-59a1.patch
 1426251-59a1.patch
 1424452-59a1.patch
 1424452-59a1.patch
+1419902-59a1.patch
 1425876-59a1.patch
 1425876-59a1.patch
 1425316-1-59a1.patch
 1425316-1-59a1.patch
 1425316-2-59a1.patch
 1425316-2-59a1.patch
@@ -718,10 +731,20 @@ NOBUG-20170803-promisehelper-57a1.patch
 1425316-4-59a1.patch
 1425316-4-59a1.patch
 1425316-5-59a1.patch
 1425316-5-59a1.patch
 1425316-6-59a1.patch
 1425316-6-59a1.patch
+1420589-1-59a1.patch
+1420589-2-59a1.patch
+1420589-3-59a1.patch
+1420589-4-59a1.patch
+1420589-5-59a1.patch
+1420589-6-59a1.patch
+1420589-7-59a1.patch
+1420589-8-59a1.patch
+1420589-9-59a1.patch
 1426253-1-59a1.patch
 1426253-1-59a1.patch
 1426253-2-59a1.patch
 1426253-2-59a1.patch
 1426253-3-59a1.patch
 1426253-3-59a1.patch
 1426253-4-59a1.patch
 1426253-4-59a1.patch
+1426388-59a1.patch
 1426018-59a1.patch
 1426018-59a1.patch
 1419366-59a1.patch
 1419366-59a1.patch
 1421992-4c-59a1.patch
 1421992-4c-59a1.patch
@@ -729,16 +752,19 @@ NOBUG-20170803-promisehelper-57a1.patch
 1420289-0-59a1.patch
 1420289-0-59a1.patch
 1420289-1-59a1.patch
 1420289-1-59a1.patch
 1425897-59a1.patch
 1425897-59a1.patch
+1309198-59a1.patch
 1365574-59a1.patch
 1365574-59a1.patch
 1402485-59a1.patch
 1402485-59a1.patch
 1404823-59a1.patch
 1404823-59a1.patch
 1425653-59a1.patch
 1425653-59a1.patch
+1425932-59a1.patch
 1425839-59a1.patch
 1425839-59a1.patch
 1426462-59a1.patch
 1426462-59a1.patch
 1405796-59a1.patch
 1405796-59a1.patch
 1227746-59a1.patch
 1227746-59a1.patch
 1424690-59a1.patch
 1424690-59a1.patch
 1427051-59a1.patch
 1427051-59a1.patch
+1426868-59a1.patch
 1426996-59a1.patch
 1426996-59a1.patch
 1427155-59a1.patch
 1427155-59a1.patch
 1427595-59a1.patch
 1427595-59a1.patch
@@ -761,6 +787,9 @@ NOBUG-20170803-promisehelper-57a1.patch
 1425440-08-59a1.patch
 1425440-08-59a1.patch
 1421678-59a1.patch
 1421678-59a1.patch
 1426989-59a1.patch
 1426989-59a1.patch
+1426728-59a1.patch
+1426527-59a1.patch
+1403743-59a1.patch
 1426094-59a1.patch
 1426094-59a1.patch
 1427825-59a1.patch
 1427825-59a1.patch
 1426809-59a1.patch
 1426809-59a1.patch
@@ -1464,6 +1493,7 @@ servo-19824-60a1.patch
 1413112-5-60a1.patch
 1413112-5-60a1.patch
 1413112-6-60a1.patch
 1413112-6-60a1.patch
 1413112-7-60a1.patch
 1413112-7-60a1.patch
+1432870-60a1.patch
 1425340-60a1.patch
 1425340-60a1.patch
 1434219-60a1.patch
 1434219-60a1.patch
 1255379-60a1.patch
 1255379-60a1.patch
@@ -2488,6 +2518,7 @@ servo-20104-60a1.patch
 1439565-2-60a1.patch
 1439565-2-60a1.patch
 1432232-60a1.patch
 1432232-60a1.patch
 1440670-60a1.patch
 1440670-60a1.patch
+1440257-60a1.patch
 1438848-60a1.patch
 1438848-60a1.patch
 1426006-60a1.patch
 1426006-60a1.patch
 1438841-60a1.patch
 1438841-60a1.patch
@@ -3168,6 +3199,7 @@ NOBUG-20180313-inspector-61a1.patch
 1445188-61a1.patch
 1445188-61a1.patch
 1445766-1a-61a1.patch
 1445766-1a-61a1.patch
 1445008-61a1.patch
 1445008-61a1.patch
+1433671-2-1444375-61a1.patch
 1432039-61a1.patch
 1432039-61a1.patch
 1437032-61a1.patch
 1437032-61a1.patch
 1445556-1-61a1.patch
 1445556-1-61a1.patch
@@ -3536,6 +3568,8 @@ NOBUG-20180329-extensions-61a1.patch
 1441796-3-61a1.patch
 1441796-3-61a1.patch
 1449640-61a1.patch
 1449640-61a1.patch
 1440827-61a1.patch
 1440827-61a1.patch
+1447993-1-61a1.patch
+1447993-2-61a1.patch
 1449337-61a1.patch
 1449337-61a1.patch
 1444119-61a1.patch
 1444119-61a1.patch
 1337157-61a1.patch
 1337157-61a1.patch