Merge autoland to mozilla-central. a=merge
authorDaniel Varga <dvarga@mozilla.com>
Wed, 10 Oct 2018 19:07:16 +0300
changeset 496188 0f1d5395f8013b4dafcd4c849a8cccdcf3c26587
parent 496159 8dfeff72def34dea3ee1a59185a73d2840ae77f3 (current diff)
parent 496187 9e84f8d5b086020dba510678222a6c3ec568edf2 (diff)
child 496253 1a9cbc785296806682eb165e0307b05b8f45b7e7
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
Pipfile
Pipfile.lock
deleted file mode 100644
--- a/Pipfile
+++ /dev/null
@@ -1,20 +0,0 @@
-[[source]]
-url = "https://pypi.org/simple"
-verify_ssl = true
-name = "pypi"
-
-[dev-packages]
-
-[packages]
-attrs = "==18.1.0"
-blessings = "==1.7"
-jsmin = "==2.1.0"
-json-e = "==2.7.0"
-pip-tools = "==3.0.0"
-pipenv = "==2018.5.18"
-pytest = "==3.6.2"
-python-hglib = "==2.4"
-requests = "==2.9.1"
-six = "==1.10.0"
-virtualenv = "==15.2.0"
-voluptuous = "==0.11.5"
deleted file mode 100644
--- a/Pipfile.lock
+++ /dev/null
@@ -1,172 +0,0 @@
-{
-    "_meta": {
-        "hash": {
-            "sha256": "e756c316803705f9230eb8dd3b53fd9a9aa0a146c7387e3caffb668e0f7ea223"
-        },
-        "pipfile-spec": 6,
-        "requires": {},
-        "sources": [
-            {
-                "name": "pypi",
-                "url": "https://pypi.org/simple",
-                "verify_ssl": true
-            }
-        ]
-    },
-    "default": {
-        "atomicwrites": {
-            "hashes": [
-                "sha256:240831ea22da9ab882b551b31d4225591e5e447a68c5e188db5b89ca1d487585",
-                "sha256:a24da68318b08ac9c9c45029f4a10371ab5b20e4226738e150e6e7c571630ae6"
-            ],
-            "version": "==1.1.5"
-        },
-        "attrs": {
-            "hashes": [
-                "sha256:4b90b09eeeb9b88c35bc642cbac057e45a5fd85367b985bd2809c62b7b939265",
-                "sha256:e0d0eb91441a3b53dab4d9b743eafc1ac44476296a2053b6ca3af0b139faf87b"
-            ],
-            "index": "pypi",
-            "version": "==18.1.0"
-        },
-        "blessings": {
-            "hashes": [
-                "sha256:98e5854d805f50a5b58ac2333411b0482516a8210f23f43308baeb58d77c157d",
-                "sha256:b1fdd7e7a675295630f9ae71527a8ebc10bfefa236b3d6aa4932ee4462c17ba3",
-                "sha256:caad5211e7ba5afe04367cdd4cfc68fa886e2e08f6f35e76b7387d2109ccea6e"
-            ],
-            "index": "pypi",
-            "version": "==1.7"
-        },
-        "certifi": {
-            "hashes": [
-                "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7",
-                "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0"
-            ],
-            "version": "==2018.4.16"
-        },
-        "click": {
-            "hashes": [
-                "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
-                "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
-            ],
-            "version": "==7.0"
-        },
-        "funcsigs": {
-            "hashes": [
-                "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca",
-                "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"
-            ],
-            "markers": "python_version < '3.0'",
-            "version": "==1.0.2"
-        },
-        "jsmin": {
-            "hashes": [
-                "sha256:5d07bf0251a4128e5e8e8eef603849b6b5741c337bff087731a248f9cc774f56"
-            ],
-            "index": "pypi",
-            "version": "==2.1.0"
-        },
-        "json-e": {
-            "hashes": [
-                "sha256:d8c1ec3f5bbc7728c3a504ebe58829f283c64eca230871e4eefe974b4cdaae4a"
-            ],
-            "index": "pypi",
-            "version": "==2.7.0"
-        },
-        "more-itertools": {
-            "hashes": [
-                "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092",
-                "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e",
-                "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"
-            ],
-            "version": "==4.3.0"
-        },
-        "pip-tools": {
-            "hashes": [
-                "sha256:4a94997602848f77ff02f660c0fcdfeaf316924ebb236c865f9742ce212aa6f9",
-                "sha256:e45e5198ce3799068642ebb0e7c9be5520bcff944c0186f79c1199a2759c970a"
-            ],
-            "index": "pypi",
-            "version": "==3.0.0"
-        },
-        "pipenv": {
-            "hashes": [
-                "sha256:04b9a8b02a3ff12a5502b335850cfdb192adcfd1d6bbdb7a7c47cae9ab9ddece",
-                "sha256:e96d5bfa6822a17b2200d455aa5f9002c14361c50df1b1e51921479d7c09e741"
-            ],
-            "index": "pypi",
-            "version": "==2018.5.18"
-        },
-        "pluggy": {
-            "hashes": [
-                "sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff",
-                "sha256:d345c8fe681115900d6da8d048ba67c25df42973bda370783cd58826442dcd7c",
-                "sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5"
-            ],
-            "version": "==0.6.0"
-        },
-        "py": {
-            "hashes": [
-                "sha256:3fd59af7435864e1a243790d322d763925431213b6b8529c6ca71081ace3bbf7",
-                "sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e"
-            ],
-            "version": "==1.5.4"
-        },
-        "pytest": {
-            "hashes": [
-                "sha256:8ea01fc4fcc8e1b1e305252b4bc80a1528019ab99fd3b88666c9dc38d754406c",
-                "sha256:90898786b3d0b880b47645bae7b51aa9bbf1e9d1e4510c2cfd15dd65c70ea0cd"
-            ],
-            "index": "pypi",
-            "version": "==3.6.2"
-        },
-        "python-hglib": {
-            "hashes": [
-                "sha256:693d6ed92a6566e78802c7a03c256cda33d08c63ad3f00fcfa11379b184b9462"
-            ],
-            "index": "pypi",
-            "version": "==2.4"
-        },
-        "requests": {
-            "hashes": [
-                "sha256:113fbba5531a9e34945b7d36b33a084e8ba5d0664b703c81a7c572d91919a5b8",
-                "sha256:c577815dd00f1394203fc44eb979724b098f88264a9ef898ee45b8e5e9cf587f"
-            ],
-            "index": "pypi",
-            "version": "==2.9.1"
-        },
-        "six": {
-            "hashes": [
-                "sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1",
-                "sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a"
-            ],
-            "index": "pypi",
-            "version": "==1.10.0"
-        },
-        "virtualenv": {
-            "hashes": [
-                "sha256:1d7e241b431e7afce47e77f8843a276f652699d1fa4f93b9d8ce0076fd7b0b54",
-                "sha256:e8e05d4714a1c51a2f5921e62f547fcb0f713ebbe959e0a7f585cc8bef71d11f"
-            ],
-            "index": "pypi",
-            "version": "==15.2.0"
-        },
-        "virtualenv-clone": {
-            "hashes": [
-                "sha256:4507071d81013fd03ea9930ec26bc8648b997927a11fa80e8ee81198b57e0ac7",
-                "sha256:b5cfe535d14dc68dfc1d1bb4ac1209ea28235b91156e2bba8e250d291c3fb4f8"
-            ],
-            "version": "==0.3.0"
-        },
-        "voluptuous": {
-            "hashes": [
-                "sha256:303542b3fc07fb52ec3d7a1c614b329cdbee13a9d681935353d8ea56a7bfa9f1",
-                "sha256:567a56286ef82a9d7ae0628c5842f65f516abcb496e74f3f59f1d7b28df314ef"
-            ],
-            "index": "pypi",
-            "version": "==0.11.5"
-        }
-    },
-    "develop": {}
-}
--- a/accessible/generic/HyperTextAccessible-inl.h
+++ b/accessible/generic/HyperTextAccessible-inl.h
@@ -6,17 +6,16 @@
 #ifndef mozilla_a11y_HyperTextAccessible_inl_h__
 #define mozilla_a11y_HyperTextAccessible_inl_h__
 
 #include "HyperTextAccessible.h"
 
 #include "nsAccUtils.h"
 
 #include "nsIClipboard.h"
-#include "nsIEditor.h"
 #include "nsIPersistentProperties2.h"
 #include "nsFrameSelection.h"
 
 #include "mozilla/TextEditor.h"
 
 namespace mozilla {
 namespace a11y {
 
@@ -117,17 +116,17 @@ HyperTextAccessible::DeleteText(int32_t 
 }
 
 inline void
 HyperTextAccessible::PasteText(int32_t aPosition)
 {
   RefPtr<TextEditor> textEditor = GetEditor();
   if (textEditor) {
     SetSelectionRange(aPosition, aPosition);
-    textEditor->PasteAsAction(nsIClipboard::kGlobalClipboard);
+    textEditor->PasteAsAction(nsIClipboard::kGlobalClipboard, true);
   }
 }
 
 inline index_t
 HyperTextAccessible::ConvertMagicOffset(int32_t aOffset) const
 {
   if (aOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT)
     return CharacterCount();
--- a/browser/components/controlcenter/content/panel.inc.xul
+++ b/browser/components/controlcenter/content/panel.inc.xul
@@ -91,17 +91,17 @@
               <label flex="1" class="identity-popup-content-blocking-category-label">&contentBlocking.fastBlock.label;</label>
               <label flex="1" class="identity-popup-content-blocking-category-state-label">&contentBlocking.fastBlock.blocked.label;</label>
               <label flex="1" class="identity-popup-content-blocking-category-add-blocking text-link"
                      onclick="ContentBlocking.openPreferences('identityPopup-CB-fastblock');">&contentBlocking.fastBlock.add.label;</label>
             </hbox>
             <hbox id="identity-popup-content-blocking-category-tracking-protection"
                   class="identity-popup-content-blocking-category" align="center" role="group">
               <image class="identity-popup-content-blocking-category-icon tracking-protection-icon"/>
-              <label flex="1" class="identity-popup-content-blocking-category-label">&contentBlocking.trackingProtection2.label;</label>
+              <label flex="1" class="identity-popup-content-blocking-category-label">&contentBlocking.trackingProtection3.label;</label>
               <label flex="1" class="identity-popup-content-blocking-category-state-label">&contentBlocking.trackingProtection.blocked.label;</label>
               <label flex="1" class="identity-popup-content-blocking-category-add-blocking text-link"
                      onclick="ContentBlocking.openPreferences('identityPopup-CB-tracking-protection');">&contentBlocking.trackingProtection.add.label;</label>
             </hbox>
             <hbox id="identity-popup-content-blocking-category-3rdpartycookies"
                   class="identity-popup-content-blocking-category" align="center" role="group">
               <image class="identity-popup-content-blocking-category-icon thirdpartycookies-icon"/>
               <label flex="1" class="identity-popup-content-blocking-category-label">&contentBlocking.3rdPartyCookies.label;</label>
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -83,17 +83,17 @@
         </vbox>
       </hbox>
       <vbox>
         <hbox class="content-blocking-category tracking-protection-ui">
           <vbox>
             <checkbox id="contentBlockingTrackingProtectionCheckbox"
                       class="content-blocking-checkbox" flex="1"
                       src="chrome://browser/skin/controlcenter/trackers.svg"
-                      data-l10n-id="content-blocking-tracking-protection-all-detected-trackers-label"/>
+                      data-l10n-id="content-blocking-tracking-protection-trackers-label"/>
             <vbox class="content-blocking-category-labels" flex="1">
               <hbox id="contentBlockingTrackingProtectionExtensionContentLabel"
                     align="center" hidden="true" class="extension-controlled">
                 <description control="contentBlockingDisableTrackingProtectionExtension" flex="1"/>
                 <button id="contentBlockingDisableTrackingProtectionExtension"
                         class="extension-controlled-button accessory-button"
                         data-l10n-id="disable-extension" hidden="true"/>
               </hbox>
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -45,57 +45,46 @@ class UrlbarInput {
     this.panel = options.panel;
     this.window = this.textbox.ownerGlobal;
     this.controller = options.controller || new UrlbarController();
     this.view = new UrlbarView(this);
     this.valueIsTyped = false;
     this.userInitiatedFocus = false;
     this.isPrivate = PrivateBrowsingUtils.isWindowPrivate(this.window);
 
+    // Forward textbox methods and properties.
     const METHODS = ["addEventListener", "removeEventListener",
       "setAttribute", "hasAttribute", "removeAttribute", "getAttribute",
       "focus", "blur", "select"];
-    const READ_ONLY_PROPERTIES = ["focused", "inputField", "editor"];
-    const READ_WRITE_PROPERTIES = ["value", "placeholder", "readOnly",
+    const READ_ONLY_PROPERTIES = ["inputField", "editor"];
+    const READ_WRITE_PROPERTIES = ["placeholder", "readOnly",
       "selectionStart", "selectionEnd"];
 
     for (let method of METHODS) {
       this[method] = (...args) => {
         this.textbox[method](...args);
       };
     }
 
     for (let property of READ_ONLY_PROPERTIES) {
       Object.defineProperty(this, property, {
         enumerable: true,
         get() {
-          let getter = "_get_" + property;
-          if (getter in this) {
-            return this[getter]();
-          }
           return this.textbox[property];
         },
       });
     }
 
     for (let property of READ_WRITE_PROPERTIES) {
       Object.defineProperty(this, property, {
         enumerable: true,
         get() {
-          let getter = "_get_" + property;
-          if (getter in this) {
-            return this[getter]();
-          }
           return this.textbox[property];
         },
         set(val) {
-          let setter = "_set_" + property;
-          if (setter in this) {
-            return this[setter](val);
-          }
           return this.textbox[property] = val;
         },
       });
     }
 
     XPCOMUtils.defineLazyGetter(this, "valueFormatter", () => {
       return new UrlbarValueFormatter(this);
     });
@@ -108,22 +97,25 @@ class UrlbarInput {
     this.inputField.addEventListener("overflow", this);
     this.inputField.addEventListener("underflow", this);
     this.inputField.addEventListener("scrollend", this);
     this.inputField.addEventListener("select", this);
 
     this.inputField.controllers.insertControllerAt(0, new CopyCutController(this));
   }
 
-  /* Shortens the given value, usually by removing http:// and trailing slashes,
+  /**
+   * Shortens the given value, usually by removing http:// and trailing slashes,
    * such that calling nsIURIFixup::createFixupURI with the result will produce
    * the same URI.
    *
    * @param {string} val
    *   The string to be trimmed if it appears to be URI
+   * @returns {string}
+   *   The trimmed string
    */
   trimValue(val) {
     return UrlbarPrefs.get("trimURLs") ? this.window.trimURL(val) : val;
   }
 
   /**
    * Applies styling to the text in the urlbar input, depending on the text.
    */
@@ -174,21 +166,25 @@ class UrlbarInput {
       this[methodName](event);
     } else {
       throw "Unrecognized urlbar event: " + event.type;
     }
   }
 
   // Getters and Setters below.
 
-  _get_focused() {
-    return this.inputField.getAttribute("focused") == "true";
+  get focused() {
+    return this.textbox.getAttribute("focused") == "true";
   }
 
-  _set_value(val) {
+  get value() {
+    return this.inputField.value;
+  }
+
+  set value(val) {
     val = this.trimValue(val);
 
     this.valueIsTyped = false;
     this.inputField.value = val;
     this.formatValue();
 
     return val;
   }
--- a/browser/components/urlbar/UrlbarValueFormatter.jsm
+++ b/browser/components/urlbar/UrlbarValueFormatter.jsm
@@ -21,28 +21,33 @@ XPCOMUtils.defineLazyModuleGetters(this,
 class UrlbarValueFormatter {
   /**
    * @param {UrlbarInput} urlbarInput
    */
   constructor(urlbarInput) {
     this.urlbarInput = urlbarInput;
     this.window = this.urlbarInput.window;
     this.document = this.window.document;
-    this.editor = this.urlbarInput.editor;
-    this.inputField = this.urlbarInput.inputField;
-    this.scheme =
-      this.document.getAnonymousElementByAttribute(this.urlbarInput.textbox, "anonid", "scheme");
 
     // This is used only as an optimization to avoid removing formatting in
     // the _remove* format methods when no formatting is actually applied.
     this._formattingApplied = false;
   }
 
+  get inputField() {
+    return this.urlbarInput.inputField;
+  }
+
+  get scheme() {
+    return this.document.getAnonymousElementByAttribute(
+      this.urlbarInput.textbox, "anonid", "scheme");
+  }
+
   update() {
-    if (!this.editor || !this.editor.rootElement.firstChild.textContent) {
+    if (!this.inputField.value) {
       return;
     }
 
     // Remove the current formatting.
     this._removeURLFormat();
     this._removeSearchAliasFormat();
 
     // Apply new formatting.  Formatter methods should return true if they
@@ -127,17 +132,17 @@ class UrlbarValueFormatter {
     return { preDomain, schemeWSlashes, domain, url, uriInfo, trimmedLength };
   }
 
   _removeURLFormat() {
     this.scheme.value = "";
     if (!this._formattingApplied) {
       return;
     }
-    let controller = this.editor.selectionController;
+    let controller = this.urlbarInput.editor.selectionController;
     let strikeOut =
       controller.getSelection(controller.SELECTION_URLSTRIKEOUT);
     strikeOut.removeAllRanges();
     let selection =
       controller.getSelection(controller.SELECTION_URLSECONDARY);
     selection.removeAllRanges();
     this._formatScheme(controller.SELECTION_URLSTRIKEOUT, true);
     this._formatScheme(controller.SELECTION_URLSECONDARY, true);
@@ -168,21 +173,22 @@ class UrlbarValueFormatter {
     }
 
     this.ensureFormattedHostVisible(urlMetaData);
 
     if (!UrlbarPrefs.get("formatting.enabled")) {
       return false;
     }
 
-    let controller = this.editor.selectionController;
+    let editor = this.urlbarInput.editor;
+    let controller = editor.selectionController;
 
     this._formatScheme(controller.SELECTION_URLSECONDARY);
 
-    let textNode = this.editor.rootElement.firstChild;
+    let textNode = editor.rootElement.firstChild;
 
     // Strike out the "https" part if mixed active content is loaded.
     if (this.urlbarInput.getAttribute("pageproxystate") == "valid" &&
         url.startsWith("https:") &&
         this.window.gBrowser.securityUI.state &
           Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT) {
       let range = this.document.createRange();
       range.setStart(textNode, 0);
@@ -243,17 +249,17 @@ class UrlbarValueFormatter {
       selection.addRange(r);
     }
   }
 
   _removeSearchAliasFormat() {
     if (!this._formattingApplied) {
       return;
     }
-    let selection = this.editor.selectionController.getSelection(
+    let selection = this.urlbarInput.editor.selectionController.getSelection(
       Ci.nsISelectionController.SELECTION_FIND
     );
     selection.removeAllRanges();
   }
 
   /**
    * If the input value starts with a search alias, this formatter method
    * highlights it.
@@ -284,27 +290,28 @@ class UrlbarValueFormatter {
       !popup.oneOffSearchButtons.selectedButton &&
       heuristicItem &&
       heuristicItem.getAttribute("actiontype") == "searchengine" &&
       this.urlbarInput._parseActionUrl(heuristicItem.getAttribute("url")).params.alias;
     if (!alias) {
       return false;
     }
 
-    let textNode = this.editor.rootElement.firstChild;
+    let editor = this.urlbarInput.editor;
+    let textNode = editor.rootElement.firstChild;
     let value = textNode.textContent;
 
     let index = value.indexOf(alias);
     if (index < 0) {
       return false;
     }
 
     // We abuse the SELECTION_FIND selection type to do our highlighting.
     // It's the only type that works with Selection.setColors().
-    let selection = this.editor.selectionController.getSelection(
+    let selection = editor.selectionController.getSelection(
       Ci.nsISelectionController.SELECTION_FIND
     );
 
     let range = this.document.createRange();
     range.setStart(textNode, index);
     range.setEnd(textNode, index + alias.length);
     selection.addRange(range);
 
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -834,16 +834,19 @@ content-blocking-category-label = Choose
 
 # "Slow" in this instance means "slow to load on the network".
 # FastBlock is a feature that blocks requests to tracking sites if they
 # have not finished loading after a certain threshold of seconds.
 content-blocking-fastblock-slow-loading-trackers-label =
   .label = Slow-Loading Trackers
   .accesskey = S
 content-blocking-fastblock-new-description = Block just the trackers that keep pages from loading quickly.
+content-blocking-tracking-protection-trackers-label =
+  .label = Trackers
+  .accesskey = T
 content-blocking-tracking-protection-all-detected-trackers-label =
   .label = All Detected Trackers
   .accesskey = T
 content-blocking-tracking-protection-new-description = Block all known trackers. (May prevent some pages from loading.)
 content-blocking-tracking-protection-option-always =
   .label = Always
   .accesskey = A
 content-blocking-tracking-protection-option-private =
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -945,16 +945,17 @@ you can use these alternative items. Oth
 <!ENTITY contentBlocking.fastBlock.blocked.label "Blocked">
 <!-- LOCALIZATION NOTE (contentBlocking.fastBlock.add.label):
      This is displayed as a link to preferences, where the user can add
      this specific type of content blocking. When this text is shown
      the type of content blocking is currently not enabled. -->
 <!ENTITY contentBlocking.fastBlock.add.label "Add Blocking…">
 
 <!ENTITY contentBlocking.trackingProtection2.label "All Detected Trackers">
+<!ENTITY contentBlocking.trackingProtection3.label "Trackers">
 <!-- LOCALIZATION NOTE (contentBlocking.trackingProtection.blocked.label):
      This label signals that this type of content blocking is turned
      ON and is successfully blocking tracker content, so this is
      a positive thing. It forms the end of the (imaginary) sentence
      "Trackers [are] Blocked"-->
 <!ENTITY contentBlocking.trackingProtection.blocked.label "Blocked">
 <!-- LOCALIZATION NOTE (contentBlocking.trackingProtection.add.label):
      This is displayed as a link to preferences, where the user can add
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -4,16 +4,17 @@ mozilla.pth:python/mozbuild
 mozilla.pth:python/mozlint
 mozilla.pth:python/mozrelease
 mozilla.pth:python/mozterm
 mozilla.pth:python/mozversioncontrol
 mozilla.pth:python/l10n
 mozilla.pth:third_party/python/atomicwrites
 mozilla.pth:third_party/python/attrs/src
 mozilla.pth:third_party/python/blessings
+mozilla.pth:third_party/python/Click
 mozilla.pth:third_party/python/compare-locales
 mozilla.pth:third_party/python/configobj
 mozilla.pth:third_party/python/cram
 mozilla.pth:third_party/python/dlmanager
 mozilla.pth:third_party/python/fluent
 mozilla.pth:third_party/python/funcsigs
 mozilla.pth:third_party/python/futures
 mozilla.pth:third_party/python/more-itertools
--- a/devtools/client/netmonitor/src/components/CustomRequestPanel.js
+++ b/devtools/client/netmonitor/src/components/CustomRequestPanel.js
@@ -45,16 +45,17 @@ class CustomRequestPanel extends Compone
       request: PropTypes.object,
       sendCustomRequest: PropTypes.func.isRequired,
       updateRequest: PropTypes.func.isRequired,
     };
   }
 
   componentDidMount() {
     const { request, connector } = this.props;
+    this.initialRequestMethod = request.method;
     fetchNetworkUpdatePacket(connector.requestData, request, [
       "requestHeaders",
       "responseHeaders",
       "requestPostData",
     ]);
   }
 
   componentWillReceiveProps(nextProps) {
@@ -104,17 +105,21 @@ class CustomRequestPanel extends Compone
           requestHeaders: {
             customHeadersValue: val || "",
             // Parse text representation of multiple HTTP headers
             headers: this.parseRequestText(val, "\\S+?", ":")
           },
         };
         break;
       case "custom-method-value":
-        data = { method: val.trim() };
+        // If val is empty when leaving the "method" field, set the method to
+        // its original value
+        data = (evt.type === "blur" && val === "") ?
+          { method: this.initialRequestMethod } :
+          { method: val.trim() };
         break;
       case "custom-postdata-value":
         data = {
           requestPostData: {
             postData: { text: val },
           }
         };
         break;
@@ -207,17 +212,19 @@ class CustomRequestPanel extends Compone
           className: "tabpanel-summary-container custom-method-and-url",
           id: "custom-method-and-url",
         },
           input({
             className: "custom-method-value",
             id: "custom-method-value",
             onChange: (evt) =>
               this.updateCustomRequestFields(evt, request, updateRequest),
-            value: method || "GET",
+            onBlur: (evt) =>
+              this.updateCustomRequestFields(evt, request, updateRequest),
+            value: method,
           }),
           input({
             className: "custom-url-value",
             id: "custom-url-value",
             onChange: (evt) =>
               this.updateCustomRequestFields(evt, request, updateRequest),
             value: url || "http://",
           }),
--- a/devtools/client/netmonitor/test/browser_net_edit_resend_caret.js
+++ b/devtools/client/netmonitor/test/browser_net_edit_resend_caret.js
@@ -2,17 +2,18 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
  * Tests if position of caret does not change (resets to the end) after setting
- * header's value to empty string.
+ * header's value to empty string. Also make sure the "method" field stays empty
+ * after removing it and resets to its original value when it looses focus.
  */
 
 add_task(async function() {
   const { tab, monitor } = await initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   const { document, store, windowRequire, parent } = monitor.panelWin;
   const parentDocument = parent.document;
@@ -32,16 +33,19 @@ add_task(async function() {
   await waitForRequestData(store, ["requestHeaders"]);
   EventUtils.sendMouseEvent({ type: "contextmenu" }, firstRequest);
 
   // Open "New Request" form
   const contextResend =  parentDocument.querySelector("#request-list-context-resend");
   contextResend.click();
   await waitUntil(() => document.querySelector("#custom-headers-value"));
   const headersTextarea = document.querySelector("#custom-headers-value");
+  await waitUntil(() => document.querySelector("#custom-method-value"));
+  const methodField = document.querySelector("#custom-method-value");
+  const originalMethodValue = methodField.value;
   const {
     getSelectedRequest
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
   const request = getSelectedRequest(store.getState());
   const hostHeader = request.requestHeaders.headers[0];
 
   // Close the open context menu, otherwise sendString will not work
   EventUtils.synthesizeKey("VK_ESCAPE", {});
@@ -49,18 +53,32 @@ add_task(async function() {
 
   // Clean value of host header
   const headersContent = headersTextarea.value;
   const start = "Host: ".length;
   const end = headersContent.indexOf("\n");
   headersTextarea.setSelectionRange(start, end);
   EventUtils.synthesizeKey("VK_DELETE", {});
 
+  methodField.focus();
+  methodField.select();
+  EventUtils.synthesizeKey("VK_DELETE", {});
+
   ok(getSelectedRequest(store.getState()).requestHeaders.headers[0] !== hostHeader,
     "Value of Host header was edited and should change"
   );
 
   ok(headersTextarea.selectionStart === start && headersTextarea.selectionEnd === start,
     "Position of caret should not change"
   );
 
+  ok(getSelectedRequest(store.getState()).method === "",
+    "Value of method header was deleted and should be empty"
+  );
+
+  headersTextarea.focus();
+
+  ok(getSelectedRequest(store.getState()).method === originalMethodValue,
+    "Value of method header should reset to its original value"
+  );
+
   return teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js
+++ b/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js
@@ -21,32 +21,33 @@ add_task(async function() {
   // Post data may be fetched by the Header panel,
   // so set the Timings panel as the new default.
   store.getState().ui.detailsPanelSelectedTab = "timings";
 
   // Open GET request in new tab
   await performRequest("GET");
   newTab = await openLastRequestInTab();
   await checkTabResponse(newTab, "GET");
+  gBrowser.removeCurrentTab();
 
   // Open POST request in new tab
   await performRequest("POST", "application/x-www-form-urlencoded", "foo=bar&baz=42");
   newTab = await openLastRequestInTab();
   await checkTabResponse(newTab, "POST", "application/x-www-form-urlencoded",
     "foo=bar&amp;baz=42");
+  gBrowser.removeCurrentTab();
 
   // Open POST application/json request in new tab
   await performRequest("POST", "application/json", '{"foo":"bar"}');
   newTab = await openLastRequestInTab();
   await checkTabResponse(newTab, "POST", "application/json", '{"foo":"bar"}');
+  gBrowser.removeCurrentTab();
 
   await teardown(monitor);
 
-  gBrowser.removeCurrentTab();
-
   async function openLastRequestInTab() {
     const wait = waitForDOM(contextMenuDoc, "#request-list-context-newtab");
     const requestItems = document.querySelectorAll(".request-list-item");
     const lastRequest = requestItems[requestItems.length - 1];
     EventUtils.sendMouseEvent({ type: "mousedown" }, lastRequest);
     EventUtils.sendMouseEvent({ type: "contextmenu" }, lastRequest);
     await wait;
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -308,16 +308,17 @@ DecreasePrivateDocShellCount()
     if (obsvc) {
       obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
     }
   }
 }
 
 nsDocShell::nsDocShell()
   : nsDocLoader()
+  , mContentWindowID(NextWindowID())
   , mForcedCharset(nullptr)
   , mParentCharset(nullptr)
   , mTreeOwner(nullptr)
   , mChromeEventHandler(nullptr)
   , mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto)
   , mCharsetReloadState(eCharsetReloadInit)
   , mOrientationLock(hal::eScreenOrientation_None)
   , mParentCharsetSource(0)
@@ -1282,16 +1283,23 @@ nsDocShell::GetContentViewer(nsIContentV
   NS_ENSURE_ARG_POINTER(aContentViewer);
 
   *aContentViewer = mContentViewer;
   NS_IF_ADDREF(*aContentViewer);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::GetOuterWindowID(uint64_t *aWindowID)
+{
+  *aWindowID = mContentWindowID;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::SetChromeEventHandler(EventTarget* aChromeEventHandler)
 {
   // Weak reference. Don't addref.
   mChromeEventHandler = aChromeEventHandler;
 
   if (mScriptGlobal) {
     mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
   }
@@ -12721,21 +12729,19 @@ nsDocShell::EnsureScriptEnvironment()
   nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
   NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
 
   uint32_t chromeFlags;
   browserChrome->GetChromeFlags(&chromeFlags);
 
   // If our window is modal and we're not opened as chrome, make
   // this window a modal content window.
-  mScriptGlobal = NS_NewScriptGlobalObject(mItemType == typeChrome);
+  mScriptGlobal = nsGlobalWindowOuter::Create(this, mItemType == typeChrome);
   MOZ_ASSERT(mScriptGlobal);
 
-  mScriptGlobal->SetDocShell(this);
-
   // Ensure the script object is set up to run script.
   return mScriptGlobal->EnsureScriptEnvironment();
 }
 
 nsresult
 nsDocShell::EnsureEditorData()
 {
   MOZ_ASSERT(!mIsBeingDestroyed);
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -931,16 +931,17 @@ private: // data members
   RefPtr<nsDOMNavigationTiming> mTiming;
   RefPtr<nsDSURIContentListener> mContentListener;
   RefPtr<nsGlobalWindowOuter> mScriptGlobal;
   nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
   nsCOMPtr<nsILoadURIDelegate> mLoadURIDelegate;
   nsCOMPtr<nsIMutableArray> mRefreshURIList;
   nsCOMPtr<nsIMutableArray> mSavedRefreshURIList;
   nsCOMPtr<nsIDOMStorageManager> mSessionStorageManager;
+  uint64_t mContentWindowID;
   nsCOMPtr<nsIContentViewer> mContentViewer;
   nsCOMPtr<nsIWidget> mParentWidget;
   RefPtr<mozilla::dom::ChildSHistory> mSessionHistory;
   nsCOMPtr<nsIWebBrowserFind> mFind;
   nsCOMPtr<nsICommandManager> mCommandManager;
   RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
 
   // Dimensions of the docshell
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -261,16 +261,21 @@ interface nsIDocShell : nsIDocShellTreeI
 
   /**
    * Content Viewer that is currently loaded for this DocShell.  This may
    * change as the underlying content changes.
    */
   readonly attribute nsIContentViewer contentViewer;
 
   /**
+   * Get the id of the outer window that is or will be in this docshell.
+   */
+  [infallible] readonly attribute unsigned long long outerWindowID;
+
+  /**
    * This attribute allows chrome to tie in to handle DOM events that may
    * be of interest to chrome.
    */
   attribute EventTarget chromeEventHandler;
 
   /**
    * This allows chrome to set a custom User agent on a specific docshell
    */
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -807,18 +807,18 @@ NewOuterWindowProxy(JSContext *cx, JS::H
   MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
   return obj;
 }
 
 //*****************************************************************************
 //***    nsGlobalWindowOuter: Object Management
 //*****************************************************************************
 
-nsGlobalWindowOuter::nsGlobalWindowOuter()
-  : nsPIDOMWindowOuter(),
+nsGlobalWindowOuter::nsGlobalWindowOuter(uint64_t aWindowID)
+  : nsPIDOMWindowOuter(aWindowID),
     mIdleFuzzFactor(0),
     mIdleCallbackIndex(-1),
     mCurrentlyIdle(false),
     mAddActiveEventFuzzTime(true),
     mFullscreen(false),
     mFullscreenMode(false),
     mIsClosed(false),
     mInClose(false),
@@ -7656,22 +7656,24 @@ nsGlobalWindowOuter::TemporarilyDisableD
 
 mozilla::dom::TabGroup*
 nsPIDOMWindowOuter::TabGroup()
 {
   return nsGlobalWindowOuter::Cast(this)->TabGroupOuter();
 }
 
 /* static */ already_AddRefed<nsGlobalWindowOuter>
-nsGlobalWindowOuter::Create(bool aIsChrome)
-{
-  RefPtr<nsGlobalWindowOuter> window = new nsGlobalWindowOuter();
+nsGlobalWindowOuter::Create(nsIDocShell* aDocShell, bool aIsChrome)
+{
+  uint64_t outerWindowID = aDocShell->GetOuterWindowID();
+  RefPtr<nsGlobalWindowOuter> window = new nsGlobalWindowOuter(outerWindowID);
   if (aIsChrome) {
     window->mIsChrome = true;
   }
+  window->SetDocShell(aDocShell);
 
   window->InitWasOffline();
   return window.forget();
 }
 
 nsIURI*
 nsPIDOMWindowOuter::GetDocumentURI() const
 {
@@ -7710,42 +7712,32 @@ nsPIDOMWindowOuter::GetDocGroup() const
 {
   nsIDocument* doc = GetExtantDoc();
   if (doc) {
     return doc->GetDocGroup();
   }
   return nullptr;
 }
 
-// XXX: Can we define this in a header instead of here?
-namespace mozilla {
-namespace dom {
-extern uint64_t
-NextWindowID();
-} // namespace dom
-} // namespace mozilla
-
-nsPIDOMWindowOuter::nsPIDOMWindowOuter()
+nsPIDOMWindowOuter::nsPIDOMWindowOuter(uint64_t aWindowID)
   : mFrameElement(nullptr)
   , mDocShell(nullptr)
   , mModalStateDepth(0)
   , mIsActive(false)
   , mIsBackground(false)
   , mMediaSuspend(
       Preferences::GetBool("media.block-autoplay-until-in-foreground", true)
         ? nsISuspendedTypes::SUSPENDED_BLOCK
         : nsISuspendedTypes::NONE_SUSPENDED)
   , mAudioMuted(false)
   , mAudioVolume(1.0)
   , mDesktopModeViewport(false)
   , mIsRootOuterWindow(false)
   , mInnerWindow(nullptr)
-  ,
-  // Make sure no actual window ends up with mWindowID == 0
-  mWindowID(NextWindowID())
+  , mWindowID(aWindowID)
   , mMarkedCCGeneration(0)
   , mServiceWorkersTestingEnabled(false)
   , mLargeAllocStatus(LargeAllocStatus::NONE)
 {
 }
 
 nsPIDOMWindowOuter::~nsPIDOMWindowOuter() {}
 
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -214,17 +214,17 @@ public:
   }
 
   static nsGlobalWindowOuter *FromSupports(nsISupports *supports)
   {
     // Make sure this matches the casts we do in QueryInterface().
     return (nsGlobalWindowOuter *)(mozilla::dom::EventTarget *)supports;
   }
 
-  static already_AddRefed<nsGlobalWindowOuter> Create(bool aIsChrome);
+  static already_AddRefed<nsGlobalWindowOuter> Create(nsIDocShell* aDocShell, bool aIsChrome);
 
   // public methods
   nsPIDOMWindowOuter* GetPrivateParent();
 
   // callback for close event
   void ReallyCloseWindow();
 
   // nsISupports
@@ -315,17 +315,16 @@ public:
   virtual bool IsSuspended() const override;
   virtual bool IsFrozen() const override;
 
   virtual nsresult FireDelayedDOMEvents() override;
 
   // Outer windows only.
   bool WouldReuseInnerWindow(nsIDocument* aNewDocument);
 
-  void SetDocShell(nsIDocShell* aDocShell);
   void DetachFromDocShell();
 
   virtual nsresult SetNewDocument(nsIDocument *aDocument,
                                   nsISupports *aState,
                                   bool aForceReuseInnerWindow) override;
 
   // Outer windows only.
   void DispatchDOMWindowCreated();
@@ -576,17 +575,16 @@ public:
   void BlurOuter();
   already_AddRefed<nsPIDOMWindowOuter> GetFramesOuter();
   nsDOMWindowList* GetFrames() final;
   uint32_t Length();
   already_AddRefed<nsPIDOMWindowOuter> GetTopOuter();
 
   nsresult GetPrompter(nsIPrompt** aPrompt) override;
 protected:
-  explicit nsGlobalWindowOuter();
   nsPIDOMWindowOuter* GetOpenerWindowOuter();
   // Initializes the mWasOffline member variable
   void InitWasOffline();
 public:
   nsPIDOMWindowOuter*
   GetSanitizedOpener(nsPIDOMWindowOuter* aOpener);
 
   already_AddRefed<nsPIDOMWindowOuter> GetOpener() override;
@@ -826,16 +824,18 @@ protected:
   // Outer windows only.
   virtual nsresult
   OpenNoNavigate(const nsAString& aUrl,
                  const nsAString& aName,
                  const nsAString& aOptions,
                  nsPIDOMWindowOuter** _retval) override;
 
 private:
+  explicit nsGlobalWindowOuter(uint64_t aWindowID);
+
   /**
    * @param aUrl the URL we intend to load into the window.  If aNavigate is
    *        true, we'll actually load this URL into the window. Otherwise,
    *        aUrl is advisory; OpenInternal will not load the URL into the
    *        new window.
    *
    * @param aName the name to use for the new window
    *
@@ -1042,16 +1042,18 @@ private:
     eIgnoreOpener
   };
   // Called only on outer windows to compute the value that will be returned by
   // IsSecureContext() for the inner window that corresponds to aDocument.
   bool ComputeIsSecureContext(nsIDocument* aDocument,
                               SecureContextFlags aFlags =
                                 SecureContextFlags::eDefault);
 
+  void SetDocShell(nsIDocShell* aDocShell);
+
   // nsPIDOMWindow{Inner,Outer} should be able to see these helper methods.
   friend class nsPIDOMWindowInner;
   friend class nsPIDOMWindowOuter;
 
   mozilla::dom::TabGroup* TabGroupOuter();
 
   void SetIsBackgroundInternal(bool aIsBackground);
 
@@ -1253,16 +1255,9 @@ nsGlobalWindowOuter::IsFrame()
 inline void
 nsGlobalWindowOuter::MaybeClearInnerWindow(nsGlobalWindowInner* aExpectedInner)
 {
   if(mInnerWindow == aExpectedInner->AsInner()) {
     mInnerWindow = nullptr;
   }
 }
 
-/* factory function */
-inline already_AddRefed<nsGlobalWindowOuter>
-NS_NewScriptGlobalObject(bool aIsChrome)
-{
-  return nsGlobalWindowOuter::Create(aIsChrome);
-}
-
 #endif /* nsGlobalWindowOuter_h___ */
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -711,17 +711,17 @@ protected:
   mozilla::dom::Event* mEvent;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
 
 class nsPIDOMWindowOuter : public mozIDOMWindowProxy
 {
 protected:
-  explicit nsPIDOMWindowOuter();
+  explicit nsPIDOMWindowOuter(uint64_t aWindowID);
 
   ~nsPIDOMWindowOuter();
 
   void RefreshMediaElementsVolume();
   void RefreshMediaElementsSuspend(SuspendTypes aSuspend);
   bool IsDisposableSuspend(SuspendTypes aSuspend) const;
   void MaybeNotifyMediaResumedFromBlock(SuspendTypes aSuspend);
 
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
+#include "mozilla/HTMLEditor.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEditor.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
@@ -28,22 +29,25 @@
 #include "mozilla/dom/WheelEventBinding.h"
 
 #include "ContentEventHandler.h"
 #include "IMEContentObserver.h"
 #include "WheelHandlingHelper.h"
 
 #include "nsCommandParams.h"
 #include "nsCOMPtr.h"
+#include "nsCopySupport.h"
 #include "nsFocusManager.h"
 #include "nsGenericHTMLElement.h"
+#include "nsIClipboard.h"
 #include "nsIContent.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
+#include "nsITextControlElement.h"
 #include "nsIWidget.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsGkAtoms.h"
 #include "nsIFormControl.h"
 #include "nsComboboxControlFrame.h"
 #include "nsIScrollableFrame.h"
 #include "nsIDOMXULControlElement.h"
@@ -3373,27 +3377,24 @@ EventStateManager::PostHandleEvent(nsPre
       GenerateMouseEnterExit(pointerEvent);
       mPointersEnterLeaveHelper.Remove(pointerEvent->pointerId);
     }
     break;
   }
   case eMouseUp:
     {
       ClearGlobalActiveContent(this);
-      WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
-      if (mouseEvent && mouseEvent->IsReal()) {
-        if (!mCurrentTarget) {
-          GetEventTarget();
-        }
+      WidgetMouseEvent* mouseUpEvent = aEvent->AsMouseEvent();
+      if (mouseUpEvent && EventCausesClickEvents(*mouseUpEvent)) {
         // Make sure to dispatch the click even if there is no frame for
         // the current target element. This is required for Web compatibility.
         RefPtr<EventStateManager> esm =
           ESMFromContentOrThis(aOverrideClickTarget);
-        ret = esm->CheckForAndDispatchClick(mouseEvent, aStatus,
-                                            aOverrideClickTarget);
+        ret = esm->PostHandleMouseUp(mouseUpEvent, aStatus,
+                                     aOverrideClickTarget);
       }
 
       nsIPresShell *shell = presContext->GetPresShell();
       if (shell) {
         RefPtr<nsFrameSelection> frameSelection = shell->FrameSelection();
         frameSelection->SetDragState(false);
       }
     }
@@ -4950,111 +4951,343 @@ EventStateManager::SetClickCount(WidgetM
       mLastRightMouseDownContentParent = nullptr;
     }
     break;
   }
 
   return NS_OK;
 }
 
+// static
+bool
+EventStateManager::EventCausesClickEvents(const WidgetMouseEvent& aMouseEvent)
+{
+  if (NS_WARN_IF(aMouseEvent.mMessage != eMouseUp)) {
+    return false;
+  }
+  // If the mouseup event is synthesized event, we don't need to dispatch
+  // click events.
+  if (!aMouseEvent.IsReal()) {
+    return false;
+  }
+  // If mouse is still over same element, clickcount will be > 1.
+  // If it has moved it will be zero, so no click.
+  if (!aMouseEvent.mClickCount) {
+    return false;
+  }
+  // Check that the window isn't disabled before firing a click
+  // (see bug 366544).
+  return !(aMouseEvent.mWidget && !aMouseEvent.mWidget->IsEnabled());
+}
+
 nsresult
-EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
+EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aMouseUpEvent,
                                              nsEventStatus* aStatus,
                                              EventMessage aMessage,
                                              nsIPresShell* aPresShell,
-                                             nsIContent* aMouseTarget,
+                                             nsIContent* aMouseUpContent,
                                              AutoWeakFrame aCurrentTarget,
                                              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;
+  MOZ_ASSERT(aMouseUpEvent);
+  MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
+  MOZ_ASSERT(aMouseUpContent || aCurrentTarget || aOverrideClickTarget);
+
+  WidgetMouseEvent event(aMouseUpEvent->IsTrusted(), aMessage,
+                         aMouseUpEvent->mWidget, WidgetMouseEvent::eReal);
+
+  event.mRefPoint = aMouseUpEvent->mRefPoint;
+  event.mClickCount = aMouseUpEvent->mClickCount;
+  event.mModifiers = aMouseUpEvent->mModifiers;
+  event.buttons = aMouseUpEvent->buttons;
+  event.mTime = aMouseUpEvent->mTime;
+  event.mTimeStamp = aMouseUpEvent->mTimeStamp;
   event.mFlags.mNoContentDispatch = aNoContentDispatch;
-  event.button = aEvent->button;
-  event.pointerId = aEvent->pointerId;
-  event.inputSource = aEvent->inputSource;
-  nsIContent* target = aMouseTarget;
+  event.button = aMouseUpEvent->button;
+  event.pointerId = aMouseUpEvent->pointerId;
+  event.inputSource = aMouseUpEvent->inputSource;
+  nsIContent* target = aMouseUpContent;
   nsIFrame* targetFrame = aCurrentTarget;
   if (aOverrideClickTarget) {
     target = aOverrideClickTarget;
     targetFrame = aOverrideClickTarget->GetPrimaryFrame();
   }
 
-  return aPresShell->HandleEventWithTarget(&event, targetFrame,
-                                           target, aStatus);
+  // Use local event status for each click event dispatching since it'll be
+  // cleared by EventStateManager::PreHandleEvent().  Therefore, dispatching
+  // an event means that previous event status will be ignored.
+  nsEventStatus status = nsEventStatus_eIgnore;
+  nsresult rv = aPresShell->HandleEventWithTarget(&event, targetFrame,
+                                                  target, &status);
+  // Copy mMultipleActionsPrevented flag from a click event to the mouseup
+  // event only when it's set to true.  It may be set to true if an editor has
+  // already handled it.  This is important to avoid two or more default
+  // actions handled here.
+  aMouseUpEvent->mFlags.mMultipleActionsPrevented |=
+    event.mFlags.mMultipleActionsPrevented;
+  // If current status is nsEventStatus_eConsumeNoDefault, we don't need to
+  // overwrite it.
+  if (*aStatus == nsEventStatus_eConsumeNoDefault) {
+    return rv;
+  }
+  // If new status is nsEventStatus_eConsumeNoDefault or
+  // nsEventStatus_eConsumeDoDefault, use it.
+  if (status == nsEventStatus_eConsumeNoDefault ||
+      status == nsEventStatus_eConsumeDoDefault) {
+    *aStatus = status;
+    return rv;
+  }
+  // Otherwise, keep the original status.
+  return rv;
+}
+
+nsresult
+EventStateManager::PostHandleMouseUp(WidgetMouseEvent* aMouseUpEvent,
+                                     nsEventStatus* aStatus,
+                                     nsIContent* aOverrideClickTarget)
+{
+  MOZ_ASSERT(aMouseUpEvent);
+  MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
+  MOZ_ASSERT(aStatus);
+
+  nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
+  if (!presShell) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIContent> mouseUpContent = GetEventTargetContent(aMouseUpEvent);
+  // 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 (mouseUpContent && !mouseUpContent->IsElement()) {
+    mouseUpContent = mouseUpContent->GetFlattenedTreeParent();
+  }
+
+  if (!mouseUpContent && !mCurrentTarget && !aOverrideClickTarget) {
+    return NS_OK;
+  }
+
+  // Fire click events if the event target is still available.
+  // Note that do not include the eMouseUp event's status since we ignore it
+  // for compatibility with the other browsers.
+  nsEventStatus status = nsEventStatus_eIgnore;
+  nsresult rv = DispatchClickEvents(presShell, aMouseUpEvent, &status,
+                                    mouseUpContent, aOverrideClickTarget);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Do not do anything if preceding click events are consumed.
+  // Note that Chromium dispatches "paste" event and actually pates clipboard
+  // text into focused editor even if the preceding click events are consumed.
+  // However, this is different from our traditional behavior and does not
+  // conform to DOM events.  If we need to keep compatibility with Chromium,
+  // we should change it later.
+  if (status == nsEventStatus_eConsumeNoDefault) {
+    *aStatus = nsEventStatus_eConsumeNoDefault;
+    return NS_OK;
+  }
+
+  // Handle middle click paste if it's enabled and the mouse button is middle.
+  if (aMouseUpEvent->button != WidgetMouseEventBase::eMiddleButton ||
+      !WidgetMouseEvent::IsMiddleClickPasteEnabled()) {
+    return NS_OK;
+  }
+  DebugOnly<nsresult> rvIgnored =
+    HandleMiddleClickPaste(presShell, aMouseUpEvent, &status, nullptr);
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
+                       "Failed to paste for a middle click");
+
+  // If new status is nsEventStatus_eConsumeNoDefault or
+  // nsEventStatus_eConsumeDoDefault, use it.
+  if (*aStatus != nsEventStatus_eConsumeNoDefault &&
+      (status == nsEventStatus_eConsumeNoDefault ||
+       status == nsEventStatus_eConsumeDoDefault)) {
+    *aStatus = status;
+  }
+
+  // Don't return error even if middle mouse paste fails since we haven't
+  // handled it here.
+  return NS_OK;
 }
 
 nsresult
-EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
-                                            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).
-    if (aEvent->mWidget && !aEvent->mWidget->IsEnabled()) {
-      return ret;
+EventStateManager::DispatchClickEvents(nsIPresShell* aPresShell,
+                                       WidgetMouseEvent* aMouseUpEvent,
+                                       nsEventStatus* aStatus,
+                                       nsIContent* aMouseUpContent,
+                                       nsIContent* aOverrideClickTarget)
+{
+  MOZ_ASSERT(aPresShell);
+  MOZ_ASSERT(aMouseUpEvent);
+  MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
+  MOZ_ASSERT(aStatus);
+  MOZ_ASSERT(aMouseUpContent || mCurrentTarget || aOverrideClickTarget);
+
+  bool notDispatchToContents =
+   (aMouseUpEvent->button == WidgetMouseEvent::eMiddleButton ||
+    aMouseUpEvent->button == WidgetMouseEvent::eRightButton);
+
+  bool fireAuxClick = notDispatchToContents;
+
+  // HandleEvent clears out mCurrentTarget which we might need again
+  AutoWeakFrame currentTarget = mCurrentTarget;
+  nsresult rv =
+    InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseClick,
+                              aPresShell, aMouseUpContent, currentTarget,
+                              notDispatchToContents,
+                              aOverrideClickTarget);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Fire double click event if click count is 2.
+  if (aMouseUpEvent->mClickCount == 2 &&
+      aMouseUpContent && aMouseUpContent->IsInComposedDoc()) {
+    rv = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseDoubleClick,
+                                   aPresShell, aMouseUpContent, currentTarget,
+                                   notDispatchToContents,
+                                   aOverrideClickTarget);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  // Fire auxclick even if necessary.
+  if (fireAuxClick &&
+      aMouseUpContent && aMouseUpContent->IsInComposedDoc()) {
+    rv = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseAuxClick,
+                                   aPresShell, aMouseUpContent, currentTarget,
+                                   false, aOverrideClickTarget);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch eMouseAuxClick");
+  }
+
+  return rv;
+}
+
+nsresult
+EventStateManager::HandleMiddleClickPaste(nsIPresShell* aPresShell,
+                                          WidgetMouseEvent* aMouseEvent,
+                                          nsEventStatus* aStatus,
+                                          TextEditor* aTextEditor)
+{
+  MOZ_ASSERT(aPresShell);
+  MOZ_ASSERT(aMouseEvent);
+  MOZ_ASSERT((aMouseEvent->mMessage == eMouseClick &&
+              aMouseEvent->button == WidgetMouseEventBase::eMiddleButton) ||
+             EventCausesClickEvents(*aMouseEvent));
+  MOZ_ASSERT(aStatus);
+  MOZ_ASSERT(*aStatus != nsEventStatus_eConsumeNoDefault);
+
+  // Even if we're called twice or more for a mouse operation, we should
+  // handle only once.  Although mMultipleActionsPrevented may be set to
+  // true by different event handler in the future, we can use it for now.
+  if (aMouseEvent->mFlags.mMultipleActionsPrevented) {
+    return NS_OK;
+  }
+  aMouseEvent->mFlags.mMultipleActionsPrevented = true;
+
+  RefPtr<Selection> selection;
+  if (aTextEditor) {
+    selection = aTextEditor->GetSelection();
+    if (NS_WARN_IF(!selection)) {
+      return NS_ERROR_FAILURE;
+    }
+  } else {
+    nsIDocument* document = aPresShell->GetDocument();
+    if (NS_WARN_IF(!document)) {
+      return NS_ERROR_FAILURE;
     }
-    //fire click
-    bool notDispatchToContents =
-     (aEvent->button == WidgetMouseEvent::eMiddleButton ||
-      aEvent->button == WidgetMouseEvent::eRightButton);
-
-    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::HandleEvent.
-      while (mouseContent && !mouseContent->IsElement()) {
-        mouseContent = mouseContent->GetFlattenedTreeParent();
-      }
-
-      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,
-                                      aOverrideClickTarget);
-
-      if (NS_SUCCEEDED(ret) && aEvent->mClickCount == 2 &&
-          mouseContent && mouseContent->IsInComposedDoc()) {
-        //fire double click
-        ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseDoubleClick,
-                                        presShell, mouseContent, currentTarget,
-                                        notDispatchToContents,
-                                        aOverrideClickTarget);
-      }
-      if (NS_SUCCEEDED(ret) && mouseContent && fireAuxClick &&
-          mouseContent->IsInComposedDoc()) {
-        ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseAuxClick,
-                                        presShell, mouseContent, currentTarget,
-                                        false, aOverrideClickTarget);
-      }
+    nsCopySupport::GetSelectionForCopy(document, getter_AddRefs(selection));
+    if (NS_WARN_IF(!selection)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  // Move selection to the clicked point.
+  nsCOMPtr<nsIContent> container;
+  int32_t offset;
+  nsLayoutUtils::GetContainerAndOffsetAtEvent(aPresShell, aMouseEvent,
+                                              getter_AddRefs(container),
+                                              &offset);
+  if (container) {
+    // XXX If readonly or disabled <input> or <textarea> in contenteditable
+    //     designMode editor is clicked, the point is in the editor.
+    //     However, outer HTMLEditor and Selection should handle it.
+    //     So, in such case, Selection::Collapse() will fail.
+    DebugOnly<nsresult> rv = selection->Collapse(container, offset);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+      "Failed to collapse Selection at middle clicked");
+  }
+
+  int32_t clipboardType = nsIClipboard::kGlobalClipboard;
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsIClipboard> clipboardService =
+    do_GetService("@mozilla.org/widget/clipboard;1", &rv);
+  if (NS_SUCCEEDED(rv)) {
+    bool selectionSupported;
+    rv = clipboardService->SupportsSelectionClipboard(&selectionSupported);
+    if (NS_SUCCEEDED(rv) && selectionSupported) {
+      clipboardType = nsIClipboard::kSelectionClipboard;
     }
   }
-  return ret;
+
+  // Fire ePaste event by ourselves since we need to dispatch "paste" event
+  // even if the middle click event was consumed for compatibility with
+  // Chromium.
+  if (!nsCopySupport::FireClipboardEvent(ePaste, clipboardType,
+                                         aPresShell, selection)) {
+    *aStatus = nsEventStatus_eConsumeNoDefault;
+    return NS_OK;
+  }
+
+  // Although we've fired "paste" event, there is no editor to accept the
+  // clipboard content.
+  if (!aTextEditor) {
+    return NS_OK;
+  }
+
+  // Check if the editor is still the good target to paste.
+  if (aTextEditor->Destroyed() ||
+      aTextEditor->IsReadonly() ||
+      aTextEditor->IsDisabled()) {
+    // XXX Should we consume the event when the editor is readonly and/or
+    //     disabled?
+    return NS_OK;
+  }
+
+  // The selection may have been modified during reflow.  Therefore, we
+  // should adjust event target to pass IsAcceptableInputEvent().
+  nsRange* range = selection->GetRangeAt(0);
+  if (!range) {
+    return NS_OK;
+  }
+  WidgetMouseEvent mouseEvent(*aMouseEvent);
+  mouseEvent.mOriginalTarget = range->GetStartContainer();
+  if (NS_WARN_IF(!mouseEvent.mOriginalTarget) ||
+      !aTextEditor->IsAcceptableInputEvent(&mouseEvent)) {
+    return NS_OK;
+  }
+
+  // If Control key is pressed, we should paste clipboard content as
+  // quotation.  Otherwise, paste it as is.
+  if (aMouseEvent->IsControl()) {
+    DebugOnly<nsresult> rv =
+      aTextEditor->PasteAsQuotationAsAction(clipboardType, false);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to paste as quotation");
+  } else {
+    DebugOnly<nsresult> rv =
+      aTextEditor->PasteAsAction(clipboardType, false);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to paste");
+  }
+  *aStatus = nsEventStatus_eConsumeNoDefault;
+
+  return NS_OK;
 }
 
 nsIFrame*
 EventStateManager::GetEventTarget()
 {
   nsIPresShell *shell;
   if (mCurrentTarget ||
       !mPresContext ||
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -107,16 +107,17 @@ public:
                           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.
    */
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult PostHandleEvent(nsPresContext* aPresContext,
                            WidgetEvent* aEvent,
                            nsIFrame* aTargetFrame,
                            nsEventStatus* aStatus,
                            nsIContent* aOverrideClickTarget);
 
   void PostHandleKeyboardEvent(WidgetKeyboardEvent* aKeyboardEvent,
                                nsIFrame* aTargetFrame, nsEventStatus& aStatus);
@@ -357,16 +358,38 @@ public:
    * If the absolute values of mMultiplierX and/or mMultiplierY are equal or
    * larger than this value, the computed scroll amount isn't rounded down to
    * the page width or height.
    */
   enum {
     MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL = 1000
   };
 
+  /**
+   * HandleMiddleClickPaste() handles middle mouse button event as pasting
+   * clipboard text.  Note that if aTextEditor is nullptr, this only
+   * dispatches ePaste event because it's necessary for some web apps which
+   * want to implement their own editor and supports middle click paste.
+   *
+   * @param aPresShell              The PresShell for the ESM.  This lifetime
+   *                                should be guaranteed by the caller.
+   * @param aMouseEvent             The eMouseClick event which caused the
+   *                                paste.
+   * @param aStatus                 The event status of aMouseEvent.
+   * @param aTextEditor             TextEditor which may be pasted the
+   *                                clipboard text by the middle click.
+   *                                If there is no editor for aMouseEvent,
+   *                                set nullptr.
+   */
+  MOZ_CAN_RUN_SCRIPT
+  nsresult HandleMiddleClickPaste(nsIPresShell* aPresShell,
+                                  WidgetMouseEvent* aMouseEvent,
+                                  nsEventStatus* aStatus,
+                                  TextEditor* aTextEditor);
+
 protected:
   /**
    * Prefs class capsules preference management.
    */
   class Prefs
   {
   public:
     static bool KeyCausesActivation() { return sKeyCausesActivation; }
@@ -456,29 +479,103 @@ protected:
                            nsIContent* aTargetContent,
                            AutoWeakFrame& aTargetFrame);
   /**
    * Update the initial drag session data transfer with any changes that occur
    * on cloned data transfer objects used for events.
    */
   void UpdateDragDataTransfer(WidgetDragEvent* dragEvent);
 
-  static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
+  /**
+   * InitAndDispatchClickEvent() dispatches a click event.
+   *
+   * @param aMouseUpEvent           eMouseUp event which causes the click event.
+   *                                EventCausesClickEvents() must return true
+   *                                if this event is set to it.
+   * @param aStatus                 Returns the result of click event.
+   *                                If the status indicates consumed, the
+   *                                value won't be overwritten with
+   *                                nsEventStatus_eIgnore.
+   * @param aMessage                Should be eMouseClick, eMouseDoubleClick or
+   *                                eMouseAuxClick.
+   * @param aPresShell              The PresShell.
+   * @param aMouseUpContent         The event target of aMouseUpEvent.
+   * @param aCurrentTarget          Current target of the caller.
+   * @param aNoContentDispatch      true if the event shouldn't be exposed to
+   *                                web contents (although will be fired on
+   *                                document and window).
+   * @param aOverrideClickTarget    Preferred click event target.  If this is
+   *                                not nullptr, aMouseUpContent and
+   *                                aCurrentTarget are ignored.
+   */
+  MOZ_CAN_RUN_SCRIPT
+  static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aMouseUpEvent,
                                             nsEventStatus* aStatus,
                                             EventMessage aMessage,
                                             nsIPresShell* aPresShell,
-                                            nsIContent* aMouseTarget,
+                                            nsIContent* aMouseUpContent,
                                             AutoWeakFrame aCurrentTarget,
                                             bool aNoContentDispatch,
                                             nsIContent* aOverrideClickTarget);
+
   nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus,
                          nsIContent* aOverrideClickTarget = nullptr);
-  nsresult CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
-                                    nsEventStatus* aStatus,
-                                    nsIContent* aOverrideClickTarget);
+
+  /**
+   * EventCausesClickEvents() returns true when aMouseEvent is an eMouseUp
+   * event and it should cause eMouseClick, eMouseDoubleClick and/or
+   * eMouseAuxClick events.  Note that this method assumes that
+   * aMouseEvent.mClickCount has already been initialized with SetClickCount().
+   */
+  static bool EventCausesClickEvents(const WidgetMouseEvent& aMouseEvent);
+
+  /**
+   * PostHandleMouseUp() handles default actions of eMouseUp event.
+   *
+   * @param aMouseUpEvent           eMouseUp event which causes the click event.
+   *                                EventCausesClickEvents() must return true
+   *                                if this event is set to it.
+   * @param aStatus                 Returns the result of event status.
+   *                                If one of dispatching event is consumed or
+   *                                this does something as default action,
+   *                                returns nsEventStatus_eConsumeNoDefault.
+   * @param aOverrideClickTarget    Preferred click event target.  If nullptr,
+   *                                aMouseUpEvent target and current target
+   *                                are used.
+   */
+  MOZ_CAN_RUN_SCRIPT
+  nsresult PostHandleMouseUp(WidgetMouseEvent* aMouseUpEvent,
+                             nsEventStatus* aStatus,
+                             nsIContent* aOverrideClickTarget);
+
+  /**
+   * DispatchClickEvents() dispatches eMouseClick, eMouseDoubleClick and
+   * eMouseAuxClick events for aMouseUpEvent.  aMouseUpEvent should cause
+   * click event.
+   *
+   * @param aPresShell              The PresShell.
+   * @param aMouseUpEvent           eMouseUp event which causes the click event.
+   *                                EventCausesClickEvents() must return true
+   *                                if this event is set to it.
+   * @param aStatus                 Returns the result of event status.
+   *                                If one of dispatching click event is
+   *                                consumed, returns
+   *                                nsEventStatus_eConsumeNoDefault.
+   * @param aMouseUpContent         The event target of aMouseUpEvent.
+   * @param aOverrideClickTarget    Preferred click event target.  If this is
+   *                                not nullptr, aMouseUpContent and
+   *                                current target frame of the ESM are ignored.
+   */
+  MOZ_CAN_RUN_SCRIPT
+  nsresult DispatchClickEvents(nsIPresShell* aPresShell,
+                               WidgetMouseEvent* aMouseUpEvent,
+                               nsEventStatus* aStatus,
+                               nsIContent* aMouseUpContent,
+                               nsIContent* aOverrideClickTarget);
+
   void EnsureDocument(nsPresContext* aPresContext);
   void FlushPendingEvents(nsPresContext* aPresContext);
 
   /**
    * The phases of WalkESMTreeToHandleAccessKey processing. See below.
    */
   typedef enum {
     eAccessKeyProcessingNormal = 0,
--- a/dom/events/UIEvent.cpp
+++ b/dom/events/UIEvent.cpp
@@ -179,64 +179,44 @@ UIEvent::PageY() const
 
   return Event::GetPageCoords(mPresContext, mEvent, mEvent->mRefPoint,
                               mClientPoint).y;
 }
 
 already_AddRefed<nsINode>
 UIEvent::GetRangeParent()
 {
-  nsIFrame* targetFrame = nullptr;
-
-  if (mPresContext) {
-    nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
-    if (shell) {
-      shell->FlushPendingNotifications(FlushType::Layout);
-      targetFrame = mPresContext->EventStateManager()->GetEventTarget();
-    }
+  if (NS_WARN_IF(!mPresContext)) {
+    return nullptr;
   }
-
-  if (targetFrame) {
-    nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
-                                                              targetFrame);
-    nsCOMPtr<nsIContent> parent = targetFrame->GetContentOffsetsFromPoint(pt).content;
-    if (parent) {
-      if (parent->ChromeOnlyAccess() &&
-          !nsContentUtils::CanAccessNativeAnon()) {
-        return nullptr;
-      }
-      return parent.forget();
-    }
+  nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
+  if (NS_WARN_IF(!presShell)) {
+    return nullptr;
   }
-
-  return nullptr;
+  nsCOMPtr<nsIContent> container;
+  nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent,
+                                              getter_AddRefs(container),
+                                              nullptr);
+  return container.forget();
 }
 
 int32_t
 UIEvent::RangeOffset() const
 {
-  if (!mPresContext) {
-    return 0;
-  }
-
-  nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
-  if (!shell) {
+  if (NS_WARN_IF(!mPresContext)) {
     return 0;
   }
-
-  shell->FlushPendingNotifications(FlushType::Layout);
-
-  nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget();
-  if (!targetFrame) {
+  nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
+  if (NS_WARN_IF(!presShell)) {
     return 0;
   }
-
-  nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
-                                                            targetFrame);
-  return targetFrame->GetContentOffsetsFromPoint(pt).offset;
+  int32_t offset = 0;
+  nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent,
+                                              nullptr, &offset);
+  return offset;
 }
 
 nsIntPoint
 UIEvent::GetLayerPoint() const
 {
   if (mEvent->mFlags.mIsPositionless) {
     return nsIntPoint(0, 0);
   }
--- a/dom/events/UIEvent.h
+++ b/dom/events/UIEvent.h
@@ -83,18 +83,20 @@ public:
   {
     MOZ_ASSERT(mEvent->mClass != eKeyboardEventClass,
                "Key events should override Which()");
     MOZ_ASSERT(mEvent->mClass != eMouseEventClass,
                "Mouse events should override Which()");
     return 0;
   }
 
+  MOZ_CAN_RUN_SCRIPT
   already_AddRefed<nsINode> GetRangeParent();
 
+  MOZ_CAN_RUN_SCRIPT
   int32_t RangeOffset() const;
 
 protected:
   ~UIEvent() {}
 
   // Internal helper functions
   nsIntPoint GetMovementPoint();
   nsIntPoint GetLayerPoint() const;
--- a/dom/serviceworkers/ServiceWorkerManager.cpp
+++ b/dom/serviceworkers/ServiceWorkerManager.cpp
@@ -2429,16 +2429,30 @@ ServiceWorkerManager::Update(nsIPrincipa
   ServiceWorkerUpdaterChild* actor =
     new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable);
 
   mActor->SendPServiceWorkerUpdaterConstructor(actor,
                                                aPrincipal->OriginAttributesRef(),
                                                nsCString(aScope));
 }
 
+namespace {
+
+void
+RejectUpdateWithInvalidStateError(ServiceWorkerUpdateFinishCallback& aCallback)
+{
+  ErrorResult error(NS_ERROR_DOM_INVALID_STATE_ERR);
+  aCallback.UpdateFailed(error);
+
+  // In case the callback does not consume the exception
+  error.SuppressException();
+}
+
+}
+
 void
 ServiceWorkerManager::UpdateInternal(nsIPrincipal* aPrincipal,
                                      const nsACString& aScope,
                                      ServiceWorkerUpdateFinishCallback* aCallback)
 {
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
 
@@ -2454,22 +2468,22 @@ ServiceWorkerManager::UpdateInternal(nsI
     return;
   }
 
   // "Let newestWorker be the result of running Get Newest Worker algorithm
   // passing registration as its argument.
   // If newestWorker is null, return a promise rejected with "InvalidStateError"
   RefPtr<ServiceWorkerInfo> newest = registration->Newest();
   if (!newest) {
-    ErrorResult error(NS_ERROR_DOM_INVALID_STATE_ERR);
-    aCallback->UpdateFailed(error);
-
-    // In case the callback does not consume the exception
-    error.SuppressException();
-
+    RejectUpdateWithInvalidStateError(*aCallback);
+    return;
+  }
+
+  if (newest->State() == ServiceWorkerState::Installing) {
+    RejectUpdateWithInvalidStateError(*aCallback);
     return;
   }
 
   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, aScope);
 
   // "Invoke Update algorithm, or its equivalent, with client, registration as
   // its argument."
   RefPtr<ServiceWorkerUpdateJob> job =
--- a/dom/tests/mochitest/general/test_clipboard_events.html
+++ b/dom/tests/mochitest/general/test_clipboard_events.html
@@ -62,16 +62,28 @@ async function reset() {
   // Reset value of contentInput.
   contentInput.value = "INPUT TEXT";
 }
 
 function getClipboardText() {
   return SpecialPowers.getClipboardData("text/unicode");
 }
 
+function getHTMLEditor() {
+  let editingSession = SpecialPowers.wrap(window).docShell.editingSession;
+  if (!editingSession) {
+    return null;
+  }
+  let editor = editingSession.getEditorForWindow(window);
+  if (!editor) {
+    return null;
+  }
+  return editor.QueryInterface(SpecialPowers.Ci.nsIHTMLEditor);
+}
+
 async function putOnClipboard(expected, operationFn, desc, type) {
   await SimpleTest.promiseClipboardChange(expected, operationFn, type);
   ok(true, desc);
 }
 
 async function wontPutOnClipboard(expected, operationFn, desc, type) {
   await SimpleTest.promiseClipboardChange(null, operationFn, type, 300, true);
   ok(SpecialPowers.getClipboardData(type || "text/unicode")
@@ -795,21 +807,97 @@ add_task(async function test_event_targe
   contenteditableContainer.innerHTML = '<div contenteditable><p id="p1">foo</p><p id="p2">bar</p></div>';
   contenteditableContainer.firstChild.focus();
 
   let p1 = document.getElementById("p1");
   let p2 = document.getElementById("p2");
   selection.setBaseAndExtent(p1.firstChild, 1, p2.firstChild, 1);
 
   let pasteTarget = null;
-  document.addEventListener("paste", (event) => { pasteTarget = event.target; }, {once: true});
-
+  let pasteEventCount = 0;
+  function pasteEventLogger(event) {
+    pasteTarget = event.target;
+    pasteEventCount++;
+  }
+  document.addEventListener("paste", pasteEventLogger);
   synthesizeKey("v", {accelKey: 1});
   is(pasteTarget.getAttribute("id"), "p1",
      "'paste' event's target should be always an element which includes start container of the first Selection range");
+  is(pasteEventCount, 1,
+     "'paste' event should be fired only once when Accel+'v' is pressed");
+  document.removeEventListener("paste", pasteEventLogger);
+  contenteditableContainer.innerHTML = "";
+});
 
+add_task(async function test_paste_event_for_middle_click_without_HTMLEditor() {
+  await SpecialPowers.pushPrefEnv({"set": [["middlemouse.paste", true],
+                                           ["middlemouse.contentLoadURL", false]]});
+
+  await reset();
+
+  contenteditableContainer.innerHTML = '<div id="non-editable-target">non-editable</div>';
+  let noneditableDiv = document.getElementById("non-editable-target");
+
+  ok(!getHTMLEditor(), "There should not be HTMLEditor");
+
+  let selection = document.getSelection();
+  selection.setBaseAndExtent(content.firstChild, 0,
+                             content.firstChild, "CONTENT".length);
+
+  await putOnClipboard("CONTENT", () => {
+    synthesizeKey("c", {accelKey: 1});
+  }, "copy text from non-editable element");
+
+  let auxclickFired = false;
+  function onAuxClick(event) {
+    auxclickFired = true;
+  }
+  document.addEventListener("auxclick", onAuxClick);
+
+  let pasteEventCount = 0;
+  function onPaste(event) {
+    pasteEventCount++;
+    ok(auxclickFired, "'auxclick' event should be fired before 'paste' event");
+    is(event.target, noneditableDiv,
+       "'paste' event should be fired on the clicked element");
+  }
+  document.addEventListener("paste", onPaste);
+
+  synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+  is(pasteEventCount, 1, "'paste' event should be fired just once");
+
+  pasteEventCount = 0;
+  auxclickFired = false;
+  document.addEventListener("mouseup", (event) => { event.preventDefault(); }, {once: true});
+  synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+  is(pasteEventCount, 1,
+     "Even if 'mouseup' event is consumed, 'paste' event should be fired");
+
+  pasteEventCount = 0;
+  auxclickFired = false;
+  document.addEventListener("click", (event) => { event.preventDefault(); }, {once: true, capture: true});
+  synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+  is(pasteEventCount, 0,
+     "If 'click' event is consumed at capturing phase at the document node, 'paste' event should be not be fired");
+
+  pasteEventCount = 0;
+  auxclickFired = false;
+  noneditableDiv.addEventListener("click", (event) => { event.preventDefault(); }, {once: true});
+  synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+  is(pasteEventCount, 1,
+     "Even if 'click' event listener is added to the click event target, 'paste' event should be fired");
+
+  pasteEventCount = 0;
+  auxclickFired = false;
+  document.addEventListener("auxclick", (event) => { event.preventDefault(); }, {once: true});
+  synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+  is(pasteEventCount, 0,
+     "If 'auxclick' event is consumed, 'paste' event should be not be fired");
+
+  document.removeEventListener("auxclick", onAuxClick);
+  document.removeEventListener("paste", onPaste);
   contenteditableContainer.innerHTML = "";
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -1196,17 +1196,17 @@ NS_IMETHODIMP
 EditorBase::CanDelete(bool* aCanDelete)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 EditorBase::Paste(int32_t aClipboardType)
 {
-  nsresult rv = AsTextEditor()->PasteAsAction(aClipboardType);
+  nsresult rv = AsTextEditor()->PasteAsAction(aClipboardType, true);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::PasteTransferable(nsITransferable* aTransferable)
--- a/editor/libeditor/EditorCommands.cpp
+++ b/editor/libeditor/EditorCommands.cpp
@@ -554,17 +554,17 @@ PasteCommand::DoCommand(const char* aCom
                         nsISupports* aCommandRefCon)
 {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
   TextEditor* textEditor = editor->AsTextEditor();
   MOZ_ASSERT(textEditor);
-  return textEditor->PasteAsAction(nsIClipboard::kGlobalClipboard);
+  return textEditor->PasteAsAction(nsIClipboard::kGlobalClipboard, true);
 }
 
 NS_IMETHODIMP
 PasteCommand::DoCommandParams(const char* aCommandName,
                               nsICommandParams* aParams,
                               nsISupports* aCommandRefCon)
 {
   return DoCommand(aCommandName, aCommandRefCon);
@@ -1303,31 +1303,33 @@ PasteQuotationCommand::DoCommand(const c
                                  nsISupports* aCommandRefCon)
 {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
   TextEditor* textEditor = editor->AsTextEditor();
   MOZ_ASSERT(textEditor);
-  return textEditor->PasteAsQuotationAsAction(nsIClipboard::kGlobalClipboard);
+  return textEditor->PasteAsQuotationAsAction(nsIClipboard::kGlobalClipboard,
+                                              true);
 }
 
 NS_IMETHODIMP
 PasteQuotationCommand::DoCommandParams(const char* aCommandName,
                                        nsICommandParams* aParams,
                                        nsISupports* aCommandRefCon)
 {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
   if (!editor) {
     return NS_ERROR_FAILURE;
   }
   TextEditor* textEditor = editor->AsTextEditor();
   MOZ_ASSERT(textEditor);
-  return textEditor->PasteAsQuotationAsAction(nsIClipboard::kGlobalClipboard);
+  return textEditor->PasteAsQuotationAsAction(nsIClipboard::kGlobalClipboard,
+                                              true);
 }
 
 NS_IMETHODIMP
 PasteQuotationCommand::GetCommandStateParams(const char* aCommandName,
                                              nsICommandParams* aParams,
                                              nsISupports* aCommandRefCon)
 {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -6,16 +6,17 @@
 
 #include "EditorEventListener.h"
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc.
 #include "mozilla/AutoRestore.h"
 #include "mozilla/ContentEvents.h"      // for InternalFocusEvent
 #include "mozilla/EditorBase.h"         // for EditorBase, etc.
 #include "mozilla/EventListenerManager.h" // for EventListenerManager
+#include "mozilla/EventStateManager.h"  // for EventStateManager
 #include "mozilla/IMEStateManager.h"    // for IMEStateManager
 #include "mozilla/Preferences.h"        // for Preferences
 #include "mozilla/TextEditor.h"         // for TextEditor
 #include "mozilla/TextEvents.h"         // for WidgetCompositionEvent
 #include "mozilla/dom/Element.h"        // for Element
 #include "mozilla/dom/Event.h"          // for Event
 #include "mozilla/dom/EventTarget.h"    // for EventTarget
 #include "mozilla/dom/MouseEvent.h"     // for MouseEvent
@@ -375,29 +376,36 @@ EditorEventListener::HandleEvent(Event* 
   // NOTE: Each event handler may require specific event interface.  Before
   //       calling it, this queries the specific interface.  If it would fail,
   //       each event handler would just ignore the event.  So, in this method,
   //       you don't need to check if the QI succeeded before each call.
   WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
   switch (internalEvent->mMessage) {
     // dragenter
     case eDragEnter: {
-      return DragEnter(aEvent->AsDragEvent());
+      // aEvent should be grabbed by the caller since this is
+      // nsIDOMEventListener method.  However, our clang plugin cannot check it
+      // if we use Event::As*Event().  So, we need to grab it by ourselves.
+      RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
+      return DragEnter(dragEvent);
     }
     // dragover
     case eDragOver: {
-      return DragOver(aEvent->AsDragEvent());
+      RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
+      return DragOver(dragEvent);
     }
     // dragexit
     case eDragExit: {
-      return DragExit(aEvent->AsDragEvent());
+      RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
+      return DragExit(dragEvent);
     }
     // drop
     case eDrop: {
-      return Drop(aEvent->AsDragEvent());
+      RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
+      return Drop(dragEvent);
     }
 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
     // keydown
     case eKeyDown: {
       return KeyDown(internalEvent->AsKeyboardEvent());
     }
     // keyup
     case eKeyUp:
@@ -415,17 +423,17 @@ EditorEventListener::HandleEvent(Event* 
       // Therefore, even if case #2 or case #3 occurs,
       // mMouseDownOrUpConsumedByIME is true here.  Therefore, we should always
       // overwrite it here.
       mMouseDownOrUpConsumedByIME =
         NotifyIMEOfMouseButtonEvent(internalEvent->AsMouseEvent());
       if (mMouseDownOrUpConsumedByIME) {
         return NS_OK;
       }
-      MouseEvent* mouseEvent = aEvent->AsMouseEvent();
+      RefPtr<MouseEvent> mouseEvent = aEvent->AsMouseEvent();
       return NS_WARN_IF(!mouseEvent) ? NS_OK : MouseDown(mouseEvent);
     }
     // mouseup
     case eMouseUp: {
       // See above comment in the eMouseDown case, first.
       // This code assumes that case #1 is occuring.  However, if case #3 may
       // occurs after case #2 and the mousedown is consumed,
       // mMouseDownOrUpConsumedByIME is true even though EditorEventListener
@@ -436,31 +444,33 @@ EditorEventListener::HandleEvent(Event* 
       // So, before a click event is fired, mMouseDownOrUpConsumedByIME is
       // always initialized in the eMouseDown case if it's referred.
       if (NotifyIMEOfMouseButtonEvent(internalEvent->AsMouseEvent())) {
         mMouseDownOrUpConsumedByIME = true;
       }
       if (mMouseDownOrUpConsumedByIME) {
         return NS_OK;
       }
-      MouseEvent* mouseEvent = aEvent->AsMouseEvent();
+      RefPtr<MouseEvent> mouseEvent = aEvent->AsMouseEvent();
       return NS_WARN_IF(!mouseEvent) ? NS_OK : MouseUp(mouseEvent);
     }
     // click
     case eMouseClick: {
-      MouseEvent* mouseEvent = aEvent->AsMouseEvent();
-      NS_ENSURE_TRUE(mouseEvent, NS_OK);
+      WidgetMouseEvent* widgetMouseEvent = internalEvent->AsMouseEvent();
+      if (NS_WARN_IF(!widgetMouseEvent)) {
+        return NS_OK;
+      }
       // If the preceding mousedown event or mouseup event was consumed,
       // editor shouldn't handle this click event.
       if (mMouseDownOrUpConsumedByIME) {
         mMouseDownOrUpConsumedByIME = false;
-        mouseEvent->PreventDefault();
+        widgetMouseEvent->PreventDefault();
         return NS_OK;
       }
-      return MouseClick(mouseEvent);
+      return MouseClick(widgetMouseEvent);
     }
     // focus
     case eFocus:
       return Focus(internalEvent->AsFocusEvent());
     // blur
     case eBlur:
       return Blur(internalEvent->AsFocusEvent());
     // text
@@ -627,112 +637,89 @@ EditorEventListener::KeyPress(WidgetKeyb
                         nsIWidget::NativeKeyBindingsForRichTextEditor,
                         DoCommandCallback, doc)) {
     aKeyboardEvent->PreventDefault();
   }
   return NS_OK;
 }
 
 nsresult
-EditorEventListener::MouseClick(MouseEvent* aMouseEvent)
+EditorEventListener::MouseClick(WidgetMouseEvent* aMouseClickEvent)
 {
-  if (NS_WARN_IF(!aMouseEvent) || DetachedFromEditor()) {
+  if (NS_WARN_IF(!aMouseClickEvent) || DetachedFromEditor()) {
     return NS_OK;
   }
   // nothing to do if editor isn't editable or clicked on out of the editor.
-  RefPtr<EditorBase> editorBase(mEditorBase);
-  WidgetMouseEvent* clickEvent =
-    aMouseEvent->WidgetEventPtr()->AsMouseEvent();
-  if (editorBase->IsReadonly() || editorBase->IsDisabled() ||
-      !editorBase->IsAcceptableInputEvent(clickEvent)) {
+  RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
+  if (textEditor->IsReadonly() || textEditor->IsDisabled() ||
+      !textEditor->IsAcceptableInputEvent(aMouseClickEvent)) {
     return NS_OK;
   }
 
   // Notifies clicking on editor to IMEStateManager even when the event was
   // consumed.
   if (EditorHasFocus()) {
-    nsPresContext* presContext = GetPresContext();
+    RefPtr<nsPresContext> presContext = GetPresContext();
     if (presContext) {
       IMEStateManager::OnClickInEditor(presContext, GetFocusedRootContent(),
-                                       clickEvent);
+                                       aMouseClickEvent);
       if (DetachedFromEditor()) {
         return NS_OK;
       }
     }
   }
 
-  if (DetachedFromEditorOrDefaultPrevented(clickEvent)) {
+  if (DetachedFromEditorOrDefaultPrevented(aMouseClickEvent)) {
     // We're done if 'preventdefault' is true (see for example bug 70698).
     return NS_OK;
   }
 
   // If we got a mouse down inside the editing area, we should force the
-  // IME to commit before we change the cursor position
+  // IME to commit before we change the cursor position.
   if (!EnsureCommitCompoisition()) {
     return NS_OK;
   }
 
-  if (clickEvent->button == 1) {
-    return HandleMiddleClickPaste(aMouseEvent);
-  }
-  return NS_OK;
-}
+  // XXX The following code is hack for our buggy "click" and "auxclick"
+  //     implementation.  "auxclick" event was added recently, however,
+  //     any non-primary button click event handlers in our UI still keep
+  //     listening to "click" events.  Additionally, "auxclick" event is
+  //     fired after "click" events and even if we do this in the system event
+  //     group, middle click opens new tab before us.  Therefore, we need to
+  //     handle middle click at capturing phase of the default group even
+  //     though this makes web apps cannot prevent middle click paste with
+  //     calling preventDefault() of "click" nor "auxclick".
 
-nsresult
-EditorEventListener::HandleMiddleClickPaste(MouseEvent* aMouseEvent)
-{
-  MOZ_ASSERT(aMouseEvent);
-
-  WidgetMouseEvent* clickEvent =
-    aMouseEvent->WidgetEventPtr()->AsMouseEvent();
-  MOZ_ASSERT(!DetachedFromEditorOrDefaultPrevented(clickEvent));
-
-  if (!Preferences::GetBool("middlemouse.paste", false)) {
-    // Middle click paste isn't enabled.
+  if (aMouseClickEvent->button != WidgetMouseEventBase::eMiddleButton ||
+      !WidgetMouseEvent::IsMiddleClickPasteEnabled()) {
     return NS_OK;
   }
 
-  // Set the selection to the point under the mouse cursor:
-  nsCOMPtr<nsINode> parent = aMouseEvent->GetRangeParent();
-  int32_t offset = aMouseEvent->RangeOffset();
-
-  RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
-  MOZ_ASSERT(textEditor);
-
-  RefPtr<Selection> selection = textEditor->GetSelection();
-  if (selection) {
-    selection->Collapse(parent, offset);
+  nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+  if (NS_WARN_IF(!presShell)) {
+    return NS_OK;
+  }
+  nsPresContext* presContext = GetPresContext();
+  if (NS_WARN_IF(!presContext)) {
+    return NS_OK;
   }
-
-  nsresult rv;
-  int32_t clipboard = nsIClipboard::kGlobalClipboard;
-  nsCOMPtr<nsIClipboard> clipboardService =
-    do_GetService("@mozilla.org/widget/clipboard;1", &rv);
-  if (NS_SUCCEEDED(rv)) {
-    bool selectionSupported;
-    rv = clipboardService->SupportsSelectionClipboard(&selectionSupported);
-    if (NS_SUCCEEDED(rv) && selectionSupported) {
-      clipboard = nsIClipboard::kSelectionClipboard;
-    }
+  MOZ_ASSERT(!aMouseClickEvent->DefaultPrevented());
+  nsEventStatus status = nsEventStatus_eIgnore;
+  RefPtr<EventStateManager> esm = presContext->EventStateManager();
+  DebugOnly<nsresult> rv =
+    esm->HandleMiddleClickPaste(presShell, aMouseClickEvent, &status,
+                                textEditor);
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                       "Failed to paste for the middle button click");
+  if (status == nsEventStatus_eConsumeNoDefault) {
+    // Prevent the event from propagating up to be possibly handled
+    // again by the containing window:
+    aMouseClickEvent->StopImmediatePropagation();
+    aMouseClickEvent->PreventDefault();
   }
-
-  // If the ctrl key is pressed, we'll do paste as quotation.
-  // Would've used the alt key, but the kde wmgr treats alt-middle specially.
-  if (clickEvent->IsControl()) {
-    textEditor->PasteAsQuotationAsAction(clipboard);
-  } else {
-    textEditor->PasteAsAction(clipboard);
-  }
-
-  // Prevent the event from propagating up to be possibly handled
-  // again by the containing window:
-  clickEvent->StopPropagation();
-  clickEvent->PreventDefault();
-
-  // We processed the event, whether drop/paste succeeded or not
   return NS_OK;
 }
 
 bool
 EditorEventListener::NotifyIMEOfMouseButtonEvent(
                        WidgetMouseEvent* aMouseEvent)
 {
   MOZ_ASSERT(aMouseEvent);
--- a/editor/libeditor/EditorEventListener.h
+++ b/editor/libeditor/EditorEventListener.h
@@ -43,17 +43,20 @@ class EditorEventListener : public nsIDO
 public:
   EditorEventListener();
 
   virtual nsresult Connect(EditorBase* aEditorBase);
 
   void Disconnect();
 
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMEVENTLISTENER
+
+  // nsIDOMEventListener
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
+  NS_IMETHOD HandleEvent(dom::Event *aEvent) override;
 
   void SpellCheckIfNeeded();
 
 protected:
   virtual ~EditorEventListener();
 
   nsresult InstallToEditor();
   void UninstallFromEditor();
@@ -61,37 +64,38 @@ protected:
 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
   nsresult KeyDown(const WidgetKeyboardEvent* aKeyboardEvent);
   nsresult KeyUp(const WidgetKeyboardEvent* aKeyboardEvent);
 #endif
   nsresult KeyPress(WidgetKeyboardEvent* aKeyboardEvent);
   nsresult HandleChangeComposition(WidgetCompositionEvent* aCompositionEvent);
   nsresult HandleStartComposition(WidgetCompositionEvent* aCompositionEvent);
   void HandleEndComposition(WidgetCompositionEvent* aCompositionEvent);
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseDown(dom::MouseEvent* aMouseEvent);
   virtual nsresult MouseUp(dom::MouseEvent* aMouseEvent) { return NS_OK; }
-  virtual nsresult MouseClick(dom::MouseEvent* aMouseEvent);
+  MOZ_CAN_RUN_SCRIPT
+  virtual nsresult MouseClick(WidgetMouseEvent* aMouseClickEvent);
   nsresult Focus(InternalFocusEvent* aFocusEvent);
   nsresult Blur(InternalFocusEvent* aBlurEvent);
-  nsresult DragEnter(dom::DragEvent* aDragEvent);
-  nsresult DragOver(dom::DragEvent* aDragEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult DragEnter(dom::DragEvent* aDragEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult DragOver(dom::DragEvent* aDragEvent);
   nsresult DragExit(dom::DragEvent* aDragEvent);
-  nsresult Drop(dom::DragEvent* aDragEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult Drop(dom::DragEvent* aDragEvent);
 
-  bool CanDrop(dom::DragEvent* aEvent);
+  MOZ_CAN_RUN_SCRIPT bool CanDrop(dom::DragEvent* aEvent);
   void CleanupDragDropCaret();
   nsIPresShell* GetPresShell() const;
   nsPresContext* GetPresContext() const;
   nsIContent* GetFocusedRootContent();
   // Returns true if IME consumes the mouse event.
   bool NotifyIMEOfMouseButtonEvent(WidgetMouseEvent* aMouseEvent);
   bool EditorHasFocus();
   bool IsFileControlTextBox();
   bool ShouldHandleNativeKeyBindings(WidgetKeyboardEvent* aKeyboardEvent);
-  nsresult HandleMiddleClickPaste(dom::MouseEvent* aMouseEvent);
 
   /**
    * DetachedFromEditor() returns true if editor was detached.
    * Otherwise, false.
    */
   bool DetachedFromEditor() const;
 
   /**
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -154,18 +154,21 @@ public:
 
   /**
    * PasteAsQuotationAsAction() pastes content in clipboard with newly created
    * blockquote element.  If the editor is in plaintext mode, will paste the
    * content with appending ">" to start of each line.
    *
    * @param aClipboardType      nsIClipboard::kGlobalClipboard or
    *                            nsIClipboard::kSelectionClipboard.
+   * @param aDispatchPasteEvent true if this should dispatch ePaste event
+   *                            before pasting.  Otherwise, false.
    */
-  virtual nsresult PasteAsQuotationAsAction(int32_t aClipboardType) override;
+  virtual nsresult PasteAsQuotationAsAction(int32_t aClipboardType,
+                                            bool aDispatchPasteEvent) override;
 
   /**
    * Can we paste |aTransferable| or, if |aTransferable| is null, will a call
    * to pasteTransferable later possibly succeed if given an instance of
    * nsITransferable then? True if the doc is modifiable, and, if
    * |aTransfeable| is non-null, we have pasteable data in |aTransfeable|.
    */
   virtual bool CanPasteTransferable(nsITransferable* aTransferable) override;
@@ -1305,20 +1308,23 @@ protected: // Shouldn't be used by frien
                                   ErrorResult& aRv,
                                   bool* aIsCellSelected = nullptr) const;
 
   /**
    * PasteInternal() pasts text with replacing selected content.
    * This tries to dispatch ePaste event first.  If its defaultPrevent() is
    * called, this does nothing but returns NS_OK.
    *
-   * @param aClipboardType  nsIClipboard::kGlobalClipboard or
-   *                        nsIClipboard::kSelectionClipboard.
+   * @param aClipboardType      nsIClipboard::kGlobalClipboard or
+   *                            nsIClipboard::kSelectionClipboard.
+   * @param aDispatchPasteEvent true if this should dispatch ePaste event
+   *                            before pasting.  Otherwise, false.
    */
-  nsresult PasteInternal(int32_t aClipboardType);
+  nsresult PasteInternal(int32_t aClipboardType,
+                         bool aDispatchPasteEvent);
 
   /**
    * InsertNodeIntoProperAncestorWithTransaction() attempts to insert aNode
    * into the document, at aPointToInsert.  Checks with strict dtd to see if
    * containment is allowed.  If not allowed, will attempt to find a parent
    * in the parent hierarchy of aPointToInsert.GetContainer() that will accept
    * aNode as a child.  If such a parent is found, will split the document
    * tree from aPointToInsert up to parent, and then insert aNode.
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -1454,19 +1454,20 @@ HTMLEditor::HavePrivateHTMLFlavor(nsICli
                                            &bHavePrivateHTMLFlavor))) {
     return bHavePrivateHTMLFlavor;
   }
 
   return false;
 }
 
 nsresult
-HTMLEditor::PasteInternal(int32_t aClipboardType)
+HTMLEditor::PasteInternal(int32_t aClipboardType,
+                          bool aDispatchPasteEvent)
 {
-  if (!FireClipboardEvent(ePaste, aClipboardType)) {
+  if (aDispatchPasteEvent && !FireClipboardEvent(ePaste, aClipboardType)) {
     return NS_OK;
   }
 
   // Get Clipboard Service
   nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard =
     do_GetService("@mozilla.org/widget/clipboard;1", &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1685,22 +1686,24 @@ HTMLEditor::CanPasteTransferable(nsITran
       return true;
     }
   }
 
   return false;
 }
 
 nsresult
-HTMLEditor::PasteAsQuotationAsAction(int32_t aClipboardType)
+HTMLEditor::PasteAsQuotationAsAction(int32_t aClipboardType,
+                                     bool aDispatchPasteEvent)
 {
   MOZ_ASSERT(aClipboardType == nsIClipboard::kGlobalClipboard ||
              aClipboardType == nsIClipboard::kSelectionClipboard);
 
   if (IsPlaintextEditor()) {
+    // XXX In this case, we don't dispatch ePaste event.  Why?
     return PasteAsPlaintextQuotation(aClipboardType);
   }
 
   // If it's not in plain text edit mode, paste text into new
   // <blockquote type="cite"> element after removing selection.
 
   AutoPlaceholderBatch beginBatching(this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
@@ -1738,17 +1741,21 @@ HTMLEditor::PasteAsQuotationAsAction(int
 
   // Collapse Selection in the new <blockquote> element.
   rv = selection->Collapse(newNode, 0);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // XXX Why don't we call HTMLEditRules::DidDoAction() after Paste()?
-  rv = PasteInternal(aClipboardType);
+  // XXX If ePaste event has not been dispatched yet but selected content
+  //     has already been removed and created a <blockquote> element.
+  //     So, web apps cannot prevent the default of ePaste event which
+  //     will be dispatched by PasteInternal().
+  rv = PasteInternal(aClipboardType, aDispatchPasteEvent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 /**
  * Paste a plaintext quotation.
--- a/editor/libeditor/HTMLEditorEventListener.cpp
+++ b/editor/libeditor/HTMLEditorEventListener.cpp
@@ -178,34 +178,36 @@ HTMLEditorEventListener::MouseDown(Mouse
     int32_t clientY = aMouseEvent->ClientY();
     htmlEditor->OnMouseDown(clientX, clientY, element, aMouseEvent);
   }
 
   return EditorEventListener::MouseDown(aMouseEvent);
 }
 
 nsresult
-HTMLEditorEventListener::MouseClick(MouseEvent* aMouseEvent)
+HTMLEditorEventListener::MouseClick(WidgetMouseEvent* aMouseClickEvent)
 {
   if (NS_WARN_IF(DetachedFromEditor())) {
     return NS_OK;
   }
 
-  RefPtr<EventTarget> target = aMouseEvent->GetTarget();
-  NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
+  RefPtr<EventTarget> target = aMouseClickEvent->GetDOMEventTarget();
+  if (NS_WARN_IF(!target)) {
+    return NS_ERROR_FAILURE;
+  }
   nsCOMPtr<Element> element = do_QueryInterface(target);
   if (NS_WARN_IF(!element)) {
     return NS_ERROR_FAILURE;
   }
 
   RefPtr<HTMLEditor> htmlEditor = mEditorBase->AsHTMLEditor();
   MOZ_ASSERT(htmlEditor);
   htmlEditor->DoInlineTableEditingAction(*element);
   // DoInlineTableEditingAction might cause reframe
   // Editor is destroyed.
   if (htmlEditor->Destroyed()) {
     return NS_OK;
   }
 
-  return EditorEventListener::MouseClick(aMouseEvent);
+  return EditorEventListener::MouseClick(aMouseClickEvent);
 }
 
 } // namespace mozilla
--- a/editor/libeditor/HTMLEditorEventListener.h
+++ b/editor/libeditor/HTMLEditorEventListener.h
@@ -25,16 +25,18 @@ public:
   }
 
   /**
    * Connect() fails if aEditorBase isn't an HTMLEditor instance.
    */
   virtual nsresult Connect(EditorBase* aEditorBase) override;
 
 protected:
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseDown(dom::MouseEvent* aMouseEvent) override;
   virtual nsresult MouseUp(dom::MouseEvent* aMouseEvent) override;
-  virtual nsresult MouseClick(dom::MouseEvent* aMouseEvent) override;
+  MOZ_CAN_RUN_SCRIPT
+  virtual nsresult MouseClick(WidgetMouseEvent* aMouseClickEvent) override;
 };
 
 } // namespace mozilla
 
 #endif // #ifndef HTMLEditorEventListener_h
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -1931,29 +1931,32 @@ TextEditor::ComputeValueInternal(const n
     return NS_ERROR_FAILURE;
   }
 
   // XXX Why don't we call TextEditRules::DidDoAction() here?
   return encoder->EncodeToString(aOutputString);
 }
 
 nsresult
-TextEditor::PasteAsQuotationAsAction(int32_t aClipboardType)
+TextEditor::PasteAsQuotationAsAction(int32_t aClipboardType,
+                                     bool aDispatchPasteEvent)
 {
   MOZ_ASSERT(aClipboardType == nsIClipboard::kGlobalClipboard ||
              aClipboardType == nsIClipboard::kSelectionClipboard);
 
   // Get Clipboard Service
   nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard =
     do_GetService("@mozilla.org/widget/clipboard;1", &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  // XXX Why don't we dispatch ePaste event here?
+
   // Get the nsITransferable interface for getting the data from the clipboard
   nsCOMPtr<nsITransferable> trans;
   rv = PrepareTransferable(getter_AddRefs(trans));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (!trans) {
     return NS_OK;
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -113,18 +113,21 @@ public:
 
   /**
    * PasteAsAction() pastes clipboard content to Selection.  This method
    * may dispatch ePaste event first.  If its defaultPrevent() is called,
    * this does nothing but returns NS_OK.
    *
    * @param aClipboardType      nsIClipboard::kGlobalClipboard or
    *                            nsIClipboard::kSelectionClipboard.
+   * @param aDispatchPasteEvent true if this should dispatch ePaste event
+   *                            before pasting.  Otherwise, false.
    */
-  nsresult PasteAsAction(int32_t aClipboardType);
+  nsresult PasteAsAction(int32_t aClipboardType,
+                         bool aDispatchPasteEvent);
 
   /**
    * InsertTextAsAction() inserts aStringToInsert at selection.
    * Although this method is implementation of nsIPlaintextEditor.insertText(),
    * this treats the input is an edit action.  If you'd like to insert text
    * as part of edit action, you probably should use InsertTextAsSubAction().
    *
    * @param aStringToInsert     The string to insert.
@@ -133,18 +136,21 @@ public:
 
   /**
    * PasteAsQuotationAsAction() pastes content in clipboard as quotation.
    * If the editor is TextEditor or in plaintext mode, will paste the content
    * with appending ">" to start of each line.
    *
    * @param aClipboardType      nsIClipboard::kGlobalClipboard or
    *                            nsIClipboard::kSelectionClipboard.
+   * @param aDispatchPasteEvent true if this should dispatch ePaste event
+   *                            before pasting.  Otherwise, false.
    */
-  virtual nsresult PasteAsQuotationAsAction(int32_t aClipboardType);
+  virtual nsresult PasteAsQuotationAsAction(int32_t aClipboardType,
+                                            bool aDispatchPasteEvent);
 
   /**
    * DeleteSelectionAsAction() removes selection content or content around
    * caret with transactions.  This should be used for handling it as an
    * edit action.  If you'd like to remove selection for preparing to insert
    * something, you probably should use DeleteSelectionAsSubAction().
    *
    * @param aDirection          How much range should be removed.
@@ -208,16 +214,17 @@ public:
    * OnCompositionChange() is called.
    */
   void OnCompositionEnd(WidgetCompositionEvent& aCompositionEndEvent);
 
   /**
    * OnDrop() is called from EditorEventListener::Drop that is handler of drop
    * event.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult OnDrop(dom::DragEvent* aDropEvent);
 
   /**
    * ComputeTextValue() computes plaintext value of this editor.  This may be
    * too expensive if it's in hot path.
    *
    * @param aDocumentEncoderFlags   Flags of nsIDocumentEncoder.
    * @param aCharset                Encoding of the document.
--- a/editor/libeditor/TextEditorDataTransfer.cpp
+++ b/editor/libeditor/TextEditorDataTransfer.cpp
@@ -290,27 +290,29 @@ TextEditor::OnDrop(DragEvent* aDropEvent
   }
 
   ScrollSelectionIntoView(false);
 
   return NS_OK;
 }
 
 nsresult
-TextEditor::PasteAsAction(int32_t aClipboardType)
+TextEditor::PasteAsAction(int32_t aClipboardType,
+                          bool aDispatchPasteEvent)
 {
   if (AsHTMLEditor()) {
-    nsresult rv = AsHTMLEditor()->PasteInternal(aClipboardType);
+    nsresult rv =
+      AsHTMLEditor()->PasteInternal(aClipboardType, aDispatchPasteEvent);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
-  if (!FireClipboardEvent(ePaste, aClipboardType)) {
+  if (aDispatchPasteEvent && !FireClipboardEvent(ePaste, aClipboardType)) {
     return NS_OK;
   }
 
   // Get Clipboard Service
   nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard =
     do_GetService("@mozilla.org/widget/clipboard;1", &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/editor/libeditor/tests/test_middle_click_paste.html
+++ b/editor/libeditor/tests/test_middle_click_paste.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html>
 <head>
-  <title>Test for paste as quotation with middle button click</title>
+  <title>Test for paste with middle button click</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none;">
 
@@ -103,27 +103,129 @@ async function doTextareaTests(aTextarea
 
   await copyPlaintext("abc\ndef\n\n");
   aTextarea.focus();
   synthesizeMouseAtCenter(aTextarea, {button: 1, ctrlKey: true});
   is(aTextarea.value,
      "> abc\n> def\n> \n",
      "If pasted text ends with \"\\n\", only the last line should not started with \">\"");
   aTextarea.value = "";
+
+  let pasteEventCount = 0;
+  function pasteEventLogger(event) {
+    pasteEventCount++;
+  }
+  aTextarea.addEventListener("paste", pasteEventLogger);
+
+  await copyPlaintext("abc");
+  aTextarea.focus();
+  document.body.addEventListener("click", (event) => { event.preventDefault(); }, {capture: true, once: true});
+  synthesizeMouseAtCenter(aTextarea, {button: 1});
+  is(aTextarea.value, "",
+     "If 'click' event is consumed at capturing phase of the <body>, paste should be canceled");
+  is(pasteEventCount, 0,
+     "If 'click' event is consumed at capturing phase of the <body>, 'paste' event should not be fired");
+  aTextarea.value = "";
+
+  await copyPlaintext("abc");
+  aTextarea.focus();
+  aTextarea.addEventListener("mouseup", (event) => { event.preventDefault(); }, {once: true});
+  pasteEventCount = 0;
+  synthesizeMouseAtCenter(aTextarea, {button: 1});
+  is(aTextarea.value, "abc",
+     "Even if 'mouseup' event is consumed, paste should be done");
+  is(pasteEventCount, 1,
+     "Even if 'mouseup' event is consumed, 'paste' event should be fired once");
+  aTextarea.value = "";
+
+  await copyPlaintext("abc");
+  aTextarea.focus();
+  aTextarea.addEventListener("click", (event) => { event.preventDefault(); }, {once: true});
+  pasteEventCount = 0;
+  synthesizeMouseAtCenter(aTextarea, {button: 1});
+  is(aTextarea.value, "abc",
+     "Even if 'click' event handler is added to the <textarea>, paste should not be canceled");
+  is(pasteEventCount, 1,
+     "Even if 'click' event handler is added to the <textarea>, 'paste' event should be fired once");
+  aTextarea.value = "";
+
+  await copyPlaintext("abc");
+  aTextarea.focus();
+  aTextarea.addEventListener("auxclick", (event) => { event.preventDefault(); }, {once: true});
+  pasteEventCount = 0;
+  synthesizeMouseAtCenter(aTextarea, {button: 1});
+  todo_is(aTextarea.value, "",
+          "If 'auxclick' event is consumed, paste should be canceled");
+  todo_is(pasteEventCount, 0,
+          "If 'auxclick' event is consumed, 'paste' event should not be fired once");
+  aTextarea.value = "";
+
+  aTextarea.removeEventListener("paste", pasteEventLogger);
 }
 
 async function doContenteditableTests(aEditableDiv) {
   await copyPlaintext("abc\ndef\nghi");
   aEditableDiv.focus();
   synthesizeMouseAtCenter(aEditableDiv, {button: 1, ctrlKey: true});
   is(aEditableDiv.innerHTML,
      "<blockquote type=\"cite\">abc<br>def<br>ghi</blockquote>",
      "Pasted plaintext should be in <blockquote> element and each linebreaker should be <br> element");
   aEditableDiv.innerHTML = "";
 
+  let pasteEventCount = 0;
+  function pasteEventLogger(event) {
+    pasteEventCount++;
+  }
+  aEditableDiv.addEventListener("paste", pasteEventLogger);
+
+  await copyPlaintext("abc");
+  aEditableDiv.focus();
+  window.addEventListener("click", (event) => { event.preventDefault(); }, {capture: true, once: true});
+  synthesizeMouseAtCenter(aEditableDiv, {button: 1});
+  is(aEditableDiv.innerHTML, "",
+     "If 'click' event is consumed at capturing phase of the window, paste should be canceled");
+  is(pasteEventCount, 0,
+     "If 'click' event is consumed at capturing phase of the window, 'paste' event should be fired once");
+  aEditableDiv.innerHTML = "";
+
+  await copyPlaintext("abc");
+  aEditableDiv.focus();
+  aEditableDiv.addEventListener("mouseup", (event) => { event.preventDefault(); }, {once: true});
+  pasteEventCount = 0;
+  synthesizeMouseAtCenter(aEditableDiv, {button: 1});
+  is(aEditableDiv.innerHTML, "abc",
+     "Even if 'mouseup' event is consumed, paste should be done");
+  is(pasteEventCount, 1,
+     "Even if 'mouseup' event is consumed, 'paste' event should be fired once");
+  aEditableDiv.innerHTML = "";
+
+  await copyPlaintext("abc");
+  aEditableDiv.focus();
+  aEditableDiv.addEventListener("click", (event) => { event.preventDefault(); }, {once: true});
+  pasteEventCount = 0;
+  synthesizeMouseAtCenter(aEditableDiv, {button: 1});
+  is(aEditableDiv.innerHTML, "abc",
+     "Even if 'click' event handler is added to the editing host, paste should not be canceled");
+  is(pasteEventCount, 1,
+     "Even if 'click' event handler is added to the editing host, 'paste' event should be fired");
+  aEditableDiv.innerHTML = "";
+
+  await copyPlaintext("abc");
+  aEditableDiv.focus();
+  aEditableDiv.addEventListener("auxclick", (event) => { event.preventDefault(); }, {once: true});
+  pasteEventCount = 0;
+  synthesizeMouseAtCenter(aEditableDiv, {button: 1});
+  todo_is(aEditableDiv.innerHTML, "",
+          "If 'auxclick' event is consumed, paste should be canceled");
+  todo_is(pasteEventCount, 0,
+          "If 'auxclick' event is consumed, 'paste' event should not be fired");
+  aEditableDiv.innerHTML = "";
+
+  aEditableDiv.removeEventListener("paste", pasteEventLogger);
+
   // Oddly, copyHTMLContent fails randomly only on Linux.  Let's skip this.
   if (navigator.platform.startsWith("Linux")) {
     return;
   }
 
   await copyHTMLContent("<p>abc</p><p>def</p><p>ghi</p>");
   aEditableDiv.focus();
   synthesizeMouseAtCenter(aEditableDiv, {button: 1, ctrlKey: true});
@@ -135,24 +237,100 @@ async function doContenteditableTests(aE
     // Oddly, on Android, we use <br> elements for pasting <p> elements.
     is(aEditableDiv.innerHTML,
        "<blockquote type=\"cite\">abc<br><br>def<br><br>ghi</blockquote>",
        "Pasted HTML content should be set to the <blockquote>");
   }
   aEditableDiv.innerHTML = "";
 }
 
+async function doNestedEditorTests(aEditableDiv) {
+  await copyPlaintext("CLIPBOARD TEXT");
+  aEditableDiv.innerHTML = '<p id="p">foo</p><textarea id="textarea"></textarea>';
+  aEditableDiv.focus();
+  let textarea = document.getElementById("textarea");
+  let pasteTarget = null;
+  function onPaste(aEvent) {
+    pasteTarget = aEvent.target;
+  }
+  document.addEventListener("paste", onPaste);
+
+  synthesizeMouseAtCenter(textarea, {button: 1});
+  is(pasteTarget.getAttribute("id"), "textarea",
+     "Target of 'paste' event should be the clicked <textarea>");
+  is(textarea.value, "CLIPBOARD TEXT",
+     "Clicking in <textarea> in an editable <div> should paste the clipboard text into the <textarea>");
+  is(aEditableDiv.innerHTML, '<p id="p">foo</p><textarea id="textarea"></textarea>',
+     "Pasting in the <textarea> shouldn't be handled by the HTMLEditor");
+
+  textarea.value = "";
+  textarea.readOnly = true;
+  pasteTarget = null;
+  synthesizeMouseAtCenter(textarea, {button: 1});
+  todo_is(pasteTarget, textarea,
+          "Target of 'paste' event should be the clicked <textarea> even if it's read-only");
+  is(textarea.value, "",
+     "Clicking in read-only <textarea> in an editable <div> should not paste the clipboard text into the read-only <textarea>");
+  // HTMLEditor thinks that read-only <textarea> is not modifiable.
+  // Therefore, HTMLEditor does not paste the text.
+  is(aEditableDiv.innerHTML, '<p id="p">foo</p><textarea id="textarea" readonly=""></textarea>',
+     "Clicking in read-only <textarea> shouldn't cause pasting the clipboard text into its parent HTMLEditor");
+
+  textarea.value = "";
+  textarea.readOnly = false;
+  textarea.disabled = true;
+  pasteTarget = null;
+  synthesizeMouseAtCenter(textarea, {button: 1});
+  // Although, this compares with <textarea>, I'm not sure it's proper event
+  // target because of disabled <textarea>.
+  todo_is(pasteTarget, textarea,
+          "Target of 'paste' event should be the clicked <textarea> even if it's disabled");
+  is(textarea.value, "",
+     "Clicking in disabled <textarea> in an editable <div> should not paste the clipboard text into the disabled <textarea>");
+  // HTMLEditor thinks that disabled <textarea> is not modifiable.
+  // Therefore, HTMLEditor does not paste the text.
+  is(aEditableDiv.innerHTML, '<p id="p">foo</p><textarea id="textarea" disabled=""></textarea>',
+     "Clicking in disabled <textarea> shouldn't cause pasting the clipboard text into its parent HTMLEditor");
+
+  document.removeEventListener("paste", onPaste);
+  aEditableDiv.innerHTML = "";
+}
+
+async function doAfterRemoveOfClickedElementTest(aEditableDiv) {
+  await copyPlaintext("CLIPBOARD TEXT");
+  aEditableDiv.innerHTML = '<p id="p">foo<span id="span">bar</span></p>';
+  aEditableDiv.focus();
+  let span = document.getElementById("span");
+  let pasteTarget = null;
+  document.addEventListener("paste", (aEvent) => { pasteTarget = aEvent.target; }, {once: true});
+  document.addEventListener("auxclick", (aEvent) => {
+    is(aEvent.target.getAttribute("id"), "span",
+       "Target of auxclick event should be the <span> element");
+    span.parentElement.removeChild(span);
+  }, {once: true});
+  synthesizeMouseAtCenter(span, {button: 1});
+  todo_is(pasteTarget.getAttribute("id"), "p",
+          "Target of 'paste' event should be the <p> element since <span> has gone");
+  // XXX Currently, pasted to start of the <p> because EventStateManager
+  //     do not recompute event target frame.
+  todo_is(aEditableDiv.innerHTML, '<p id="p">fooCLIPBOARD TEXT</p>',
+          "Clipbpard text should looks like replacing the <span> element");
+  aEditableDiv.innerHTML = "";
+}
+
 async function doTests() {
   await SpecialPowers.pushPrefEnv({"set": [["middlemouse.paste", true],
                                            ["middlemouse.contentLoadURL", false]]});
   let container = document.getElementById("container");
   container.innerHTML = "<textarea id=\"editor\"></textarea>";
   await doTextareaTests(document.getElementById("editor"));
   container.innerHTML = "<div id=\"editor\" contenteditable style=\"min-height: 1em;\"></div>";
   await doContenteditableTests(document.getElementById("editor"));
+  await doNestedEditorTests(document.getElementById("editor"));
+  await doAfterRemoveOfClickedElementTest(document.getElementById("editor"));
   SimpleTest.finish();
 }
 
 SimpleTest.waitForFocus(doTests);
 </script>
 </pre>
 </body>
 </html>
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -7,16 +7,17 @@
 #include "nsLayoutUtils.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventDispatcher.h"
+#include "mozilla/EventStateManager.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/layers/PAPZ.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/ContentChild.h"
@@ -2225,16 +2226,69 @@ nsLayoutUtils::GetPopupFrameForEventCoor
           GetEventCoordinatesRelativeTo(aEvent, popup))) {
       return popup;
     }
   }
 #endif
   return nullptr;
 }
 
+void
+nsLayoutUtils::GetContainerAndOffsetAtEvent(nsIPresShell* aPresShell,
+                                            const WidgetEvent* aEvent,
+                                            nsIContent** aContainer,
+                                            int32_t* aOffset)
+{
+  MOZ_ASSERT(aContainer || aOffset);
+
+  if (aContainer) {
+    *aContainer = nullptr;
+  }
+  if (aOffset) {
+    *aOffset = 0;
+  }
+
+  if (!aPresShell) {
+    return;
+  }
+
+  aPresShell->FlushPendingNotifications(FlushType::Layout);
+
+  RefPtr<nsPresContext> presContext = aPresShell->GetPresContext();
+  if (!presContext) {
+    return;
+  }
+
+  nsIFrame* targetFrame = presContext->EventStateManager()->GetEventTarget();
+  if (!targetFrame) {
+    return;
+  }
+
+  nsPoint point =
+    nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, targetFrame);
+
+  if (aContainer) {
+    // TODO: This result may be useful to change to Selection.  However, this
+    //       may return improper node (e.g., native anonymous node) for the
+    //       Selection.  Perhaps, this should take Selection optionally and
+    //       if it's specified, needs to check if it's proper for the
+    //       Selection.
+    nsCOMPtr<nsIContent> container =
+      targetFrame->GetContentOffsetsFromPoint(point).content;
+    if (container &&
+        (!container->ChromeOnlyAccess() ||
+         nsContentUtils::CanAccessNativeAnon())) {
+      container.forget(aContainer);
+    }
+  }
+  if (aOffset) {
+    *aOffset = targetFrame->GetContentOffsetsFromPoint(point).offset;
+  }
+}
+
 static void ConstrainToCoordValues(float& aStart, float& aSize)
 {
   MOZ_ASSERT(aSize >= 0);
 
   // Here we try to make sure that the resulting nsRect will continue to cover
   // as much of the area that was covered by the original gfx Rect as possible.
 
   // We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -772,16 +772,32 @@ public:
    * @return        Null, if there is no popup frame at the point, otherwise,
    *                returns top-most popup frame at the point.
    */
   static nsIFrame* GetPopupFrameForEventCoordinates(
                      nsPresContext* aPresContext,
                      const mozilla::WidgetEvent* aEvent);
 
   /**
+   * Get container and offset if aEvent collapses Selection.
+   * @param aPresShell      The PresShell handling aEvent.
+   * @param aEvent          The event having coordinates where you want to
+   *                        collapse Selection.
+   * @param aContainer      Returns the container node at the point.
+   *                        Set nullptr if you don't need this.
+   * @param aOffset         Returns offset in the container node at the point.
+   *                        Set nullptr if you don't need this.
+   */
+  MOZ_CAN_RUN_SCRIPT
+  static void GetContainerAndOffsetAtEvent(nsIPresShell* aPresShell,
+                                           const mozilla::WidgetEvent* aEvent,
+                                           nsIContent** aContainer,
+                                           int32_t* aOffset);
+
+  /**
    * Translate from widget coordinates to the view's coordinates
    * @param aPresContext the PresContext for the view
    * @param aWidget the widget
    * @param aPt the point relative to the widget
    * @param aView  view to which returned coordinates are relative
    * @return the point in the view's coordinates
    */
   static nsPoint TranslateWidgetToView(nsPresContext* aPresContext,
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -627,17 +627,17 @@ nsXULPopupManager::InitTriggerEvent(Even
       // get the trigger content from the event
       nsCOMPtr<nsIContent> target = do_QueryInterface(aEvent->GetTarget());
       target.forget(aTriggerContent);
     }
   }
 
   mCachedModifiers = 0;
 
-  UIEvent* uiEvent = aEvent ? aEvent->AsUIEvent() : nullptr;
+  RefPtr<UIEvent> uiEvent = aEvent ? aEvent->AsUIEvent() : nullptr;
   if (uiEvent) {
     mRangeParent = uiEvent->GetRangeParent();
     mRangeOffset = uiEvent->RangeOffset();
 
     // get the event coordinates relative to the root frame of the document
     // containing the popup.
     NS_ASSERTION(aPopup, "Expected a popup node");
     WidgetEvent* event = aEvent->WidgetEventPtr();
--- a/layout/xul/nsXULPopupManager.h
+++ b/layout/xul/nsXULPopupManager.h
@@ -709,16 +709,17 @@ protected:
   nsMenuChainItem* GetTopVisibleMenu();
 
   // Hide all of the visible popups from the given list. This function can
   // cause style changes and frame destruction.
   void HidePopupsInList(const nsTArray<nsMenuPopupFrame *> &aFrames);
 
   // set the event that was used to trigger the popup, or null to clear the
   // event details. aTriggerContent will be set to the target of the event.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void InitTriggerEvent(mozilla::dom::Event* aEvent, nsIContent* aPopup, nsIContent** aTriggerContent);
 
   // callbacks for ShowPopup and HidePopup as events may be done asynchronously
   void ShowPopupCallback(nsIContent* aPopup,
                          nsMenuPopupFrame* aPopupFrame,
                          bool aIsContextMenu,
                          bool aSelectFirstItem);
   void HidePopupCallback(nsIContent* aPopup,
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -62,16 +62,17 @@ public class Tabs implements BundleEvent
     private volatile CopyOnWriteArrayList<Tab> mOrder = new CopyOnWriteArrayList<Tab>();
 
     // A cache that maps a tab ID to an mOrder tab position.  All access should be synchronized.
     private final TabPositionCache tabPositionCache = new TabPositionCache();
 
     // All writes to mSelectedTab must be synchronized on the Tabs instance.
     // In general, it's preferred to always use selectTab()).
     private volatile Tab mSelectedTab;
+    private volatile int mPreviouslySelectedTabId = INVALID_TAB_ID;
 
     // All accesses to mTabs must be synchronized on the Tabs instance.
     private final HashMap<Integer, Tab> mTabs = new HashMap<Integer, Tab>();
 
     private AccountManager mAccountManager;
     private OnAccountsUpdateListener mAccountListener;
 
     public static final int LOADURL_NONE         = 0;
@@ -332,16 +333,17 @@ public class Tabs implements BundleEvent
         }
 
         mSelectedTab = tab;
         mSelectedTab.updatePageAction();
 
         notifyListeners(tab, TabEvents.SELECTED);
 
         if (oldTab != null) {
+            mPreviouslySelectedTabId = oldTab.getId();
             notifyListeners(oldTab, TabEvents.UNSELECTED);
         }
 
         // Pass a message to Gecko to update tab state in BrowserApp.
         final GeckoBundle data = new GeckoBundle(1);
         data.putInt("id", tab.getId());
         mEventDispatcher.dispatch("Tab:Selected", data);
         EventDispatcher.getInstance().dispatch("Tab:Selected", data);
@@ -487,25 +489,22 @@ public class Tabs implements BundleEvent
             Tab lastTab = mOrder.get(mOrder.size() - 1);
             if (!lastTab.isPrivate()) {
                 nextTab = lastTab;
             } else {
                 nextTab = getPreviousTabFrom(lastTab, false);
             }
         }
 
-        Tab parent = getTab(tab.getParentId());
-        if (parent != null) {
-            // If the next tab is a sibling, switch to it. Otherwise go back to the parent.
-            if (nextTab != null && nextTab.getParentId() == tab.getParentId())
-                return nextTab;
-            else
-                return parent;
+        final Tab parentTab = getTab(tab.getParentId());
+        if (tab.getParentId() == mPreviouslySelectedTabId && tab.getParentId() != INVALID_TAB_ID && parentTab != null) {
+            return parentTab;
+        } else {
+            return nextTab;
         }
-        return nextTab;
     }
 
     public Iterable<Tab> getTabsInOrder() {
         return mOrder;
     }
 
     /**
      * @return the current GeckoApp instance, or throws if
--- a/mobile/android/tests/browser/chrome/test_session_parentid.html
+++ b/mobile/android/tests/browser/chrome/test_session_parentid.html
@@ -51,48 +51,70 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   const url = "data:text/html;charset=utf-8,It%20was%20a%20dark%20and%20stormy%20night.";
 
   add_task(async function test_sessionStoreParentId() {
     SimpleTest.registerCleanupFunction(function() {
       cleanupTabs();
     });
 
-    // First, check that passing a parent tab ID works as expected
+    // Initialize parent tab
     tabParent = BrowserApp.addTab(url, { selected: true });
-    await promiseBrowserEvent(tabParent.browser, "DOMContentLoaded");
+    await promiseTabEvent(tabParent.browser, "DOMContentLoaded");
+    is(BrowserApp.selectedTab, tabParent, "tabParent is selected");
 
+    // test case #1
     // Open tabs without passing a parent tab ID
     tabChild1 = BrowserApp.addTab(url, { selected: false });
+    await promiseTabEvent(tabChild1.browser, "DOMContentLoaded");
+    is(BrowserApp.selectedTab, tabParent, "tabParent is selected");
+
     tabChild2 = BrowserApp.addTab(url, { selected: true });
     await promiseTabEvent(BrowserApp.deck, "TabSelect");
     is(BrowserApp.selectedTab, tabChild2, "2nd child tab is selected");
 
     // After closing that tab, its neighbour should be selected
     BrowserApp.closeTab(tabChild2);
     tabChild2 = null;
-    await promiseTabEvent(BrowserApp.deck, "TabSelect");
+    await promiseTabEvent(BrowserApp.deck, "TabClose");
     is(BrowserApp.selectedTab, tabChild1, "1st child tab is selected");
 
-    // Add a new tab and pass a parent tab ID this time
-    tabChild2 = BrowserApp.addTab(url, { selected: true, parentId: tabParent.id });
+    // test case #2
+    // Let's open a new tab, this time with a parent id and let's check if after closing it,
+    // the selected tab will be the neighbour since the parent was not selected
+    tabChild2 = BrowserApp.addTab(url, { selected: false, parentId: tabParent.id });
+    await promiseTabEvent(tabChild2.browser, "DOMContentLoaded");
+    is(BrowserApp.selectedTab, tabChild1, "1st child tab is still selected");
+
+    BrowserApp.selectTab(tabChild2);
     await promiseTabEvent(BrowserApp.deck, "TabSelect");
     is(BrowserApp.selectedTab, tabChild2, "2nd child tab is selected");
 
-    // After closing that tab, its parent should be selected
     BrowserApp.closeTab(tabChild2);
     tabChild2 = null;
-    await promiseTabEvent(BrowserApp.deck, "TabSelect");
-    is(BrowserApp.selectedTab, tabParent, "parent tab is selected");
-
-    // Reset selection and switch to the other child tab
-    BrowserApp.selectTab(tabChild1);
-    await promiseTabEvent(BrowserApp.deck, "TabSelect");
+    await promiseTabEvent(BrowserApp.deck, "TabClose");
     is(BrowserApp.selectedTab, tabChild1, "1st child tab is selected");
 
+    // test case #3
+    // This time we open a new tab with a parent id but this time the parent should be selected
+    // after closing since the parent was the previously selected tab
+    BrowserApp.selectTab(tabParent);
+    await promiseTabEvent(BrowserApp.deck, "TabSelect");
+    is(BrowserApp.selectedTab, tabParent, "tabParent is selected");
+
+    tabChild2 = BrowserApp.addTab(url, { selected: true, parentId: tabParent.id });
+    await promiseTabEvent(tabChild2.browser, "DOMContentLoaded");
+    is(BrowserApp.selectedTab, tabChild2, "2d child tab is selected");
+
+    BrowserApp.closeTab(tabChild2);
+    tabChild2 = null;
+    await promiseTabEvent(BrowserApp.deck, "TabClose");
+    is(BrowserApp.selectedTab, tabParent, "tabParent is selected");
+
+    // test case #4
     // Now check that this works even if the child tab is closed and subsequently restored
     tabChild2 = BrowserApp.addTab(url, { selected: false, parentId: tabParent.id });
     await promiseTabEvent(tabChild2.browser, "SSTabDataUpdated");
     BrowserApp.closeTab(tabChild2);
     await promiseTabEvent(tabChild2.browser, "SSTabCloseProcessed");
 
     // Restore the tab
     let closedTabData = ss.getClosedTabs(chromeWin)[0];
@@ -100,17 +122,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     await promiseTabEvent(BrowserApp.deck, "TabSelect");
     tabChild2 = BrowserApp.getTabForBrowser(browser);
     is(BrowserApp.selectedTab, tabChild2, "restored 2nd child tab is selected");
 
     // After closing that tab, its parent should be selected
     BrowserApp.closeTab(tabChild2);
     tabChild2 = null;
     await promiseTabEvent(BrowserApp.deck, "TabSelect");
-    is(BrowserApp.selectedTab, tabParent, "parent tab is selected after restoring");
+    is(BrowserApp.selectedTab, tabParent, "tabParent is selected after restoring");
 
     cleanupTabs();
   });
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1301160">Mozilla Bug 1301160</a>
--- a/moz.build
+++ b/moz.build
@@ -32,19 +32,16 @@ with Files('mach'):
     BUG_COMPONENT = ('Core', 'mach')
 
 with Files('*moz*'):
     BUG_COMPONENT = ('Firefox Build System', 'General')
 
 with Files('GNUmakefile'):
     BUG_COMPONENT = ('Firefox Build System', 'General')
 
-with Files('Pipfile*'):
-    BUG_COMPONENT = ('Firefox Build System', 'General')
-
 with Files('*gradle*'):
     BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
     SCHEDULES.exclusive = ['android']
 
 with Files('*.json'):
     BUG_COMPONENT = ('Firefox Build System', 'General')
 
 with Files('**/l10n.toml'):
--- a/python/mozbuild/mozbuild/vendor_python.py
+++ b/python/mozbuild/mozbuild/vendor_python.py
@@ -1,15 +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/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import os
+import shutil
 import subprocess
 
 import mozfile
 import mozpack.path as mozpath
 from mozbuild.base import MozbuildObject
 from mozfile import NamedTemporaryFile, TemporaryDirectory
 from mozpack.files import FileFinder
 
@@ -19,46 +20,69 @@ class VendorPython(MozbuildObject):
     def vendor(self, packages=None):
         self.populate_logger()
         self.log_manager.enable_unstructured()
 
         vendor_dir = mozpath.join(
             self.topsrcdir, os.path.join('third_party', 'python'))
 
         packages = packages or []
-        pipenv = self.ensure_pipenv()
 
-        for package in packages:
-            if not all(package.partition('==')):
-                raise Exception('Package {} must be in the format name==version'.format(package))
+        self._activate_virtualenv()
+        pip_compile = os.path.join(self.virtualenv_manager.bin_path, 'pip-compile')
+        if not os.path.exists(pip_compile):
+            path = os.path.normpath(os.path.join(self.topsrcdir, 'third_party', 'python', 'pip-tools'))
+            self.virtualenv_manager.install_pip_package(path, vendored=True)
+        spec = os.path.join(vendor_dir, 'requirements.in')
+        requirements = os.path.join(vendor_dir, 'requirements.txt')
 
-        for package in packages:
-            subprocess.check_call(
-                [pipenv, 'install', package],
-                cwd=self.topsrcdir)
+        with NamedTemporaryFile('w') as tmpspec:
+            shutil.copyfile(spec, tmpspec.name)
+            self._update_packages(tmpspec.name, packages)
 
-        with NamedTemporaryFile('w') as requirements:
-            # determine the dependency graph and generate requirements.txt
-            subprocess.check_call(
-                [pipenv, 'lock', '--requirements'],
-                cwd=self.topsrcdir,
-                stdout=requirements)
+            # resolve the dependencies and update requirements.txt
+            subprocess.check_output([
+                pip_compile,
+                tmpspec.name,
+                '--no-header',
+                '--no-index',
+                '--output-file', requirements,
+                '--generate-hashes'])
 
             with TemporaryDirectory() as tmp:
                 # use requirements.txt to download archived source distributions of all packages
                 self.virtualenv_manager._run_pip([
                     'download',
-                    '-r', requirements.name,
+                    '-r', requirements,
                     '--no-deps',
                     '--dest', tmp,
                     '--no-binary', ':all:',
                     '--disable-pip-version-check'])
                 self._extract(tmp, vendor_dir)
 
-        self.repository.add_remove_files(vendor_dir)
+            shutil.copyfile(tmpspec.name, spec)
+            self.repository.add_remove_files(vendor_dir)
+
+    def _update_packages(self, spec, packages):
+        for package in packages:
+            if not all(package.partition('==')):
+                raise Exception('Package {} must be in the format name==version'.format(package))
+
+        requirements = {}
+        with open(spec, 'r') as f:
+            for line in f.readlines():
+                name, version = line.rstrip().split('==')
+                requirements[name] = version
+        for package in packages:
+            name, version = package.split('==')
+            requirements[name] = version
+
+        with open(spec, 'w') as f:
+            for name, version in sorted(requirements.items()):
+                f.write('{}=={}\n'.format(name, version))
 
     def _extract(self, src, dest):
         """extract source distribution into vendor directory"""
         finder = FileFinder(src)
         for path, _ in finder.find('*'):
             # packages extract into package-version directory name and we strip the version
             tld = mozfile.extract(os.path.join(finder.base, path), dest)[0]
             target = os.path.join(dest, tld.rpartition('-')[0])
--- a/testing/mozbase/mozprofile/setup.py
+++ b/testing/mozbase/mozprofile/setup.py
@@ -5,17 +5,17 @@
 from __future__ import absolute_import
 
 from setuptools import setup
 
 PACKAGE_NAME = 'mozprofile'
 PACKAGE_VERSION = '2.0.0'
 
 deps = [
-    'mozfile==1.*',
+    'mozfile>=1.2',
     'mozlog==3.*',
     'six>=1.10.0,<2',
 ]
 
 setup(name=PACKAGE_NAME,
       version=PACKAGE_VERSION,
       description="Library to create and modify Mozilla application profiles",
       long_description="see https://firefox-source-docs.mozilla.org/mozbase/index.html",
--- a/testing/mozbase/mozrunner/setup.py
+++ b/testing/mozbase/mozrunner/setup.py
@@ -8,17 +8,17 @@ from setuptools import setup, find_packa
 
 PACKAGE_NAME = 'mozrunner'
 PACKAGE_VERSION = '7.1.0'
 
 desc = """Reliable start/stop/configuration of Mozilla Applications (Firefox, Thunderbird, etc.)"""
 
 deps = [
     'mozdevice>=1.*',
-    'mozfile==1.*',
+    'mozfile>=1.2',
     'mozinfo>=0.7,<2',
     'mozlog==3.*',
     'mozprocess>=0.23,<1',
     'mozprofile>=1.1.0,<3',
     'six>=1.10.0,<2',
 ]
 
 EXTRAS_REQUIRE = {'crash': ['mozcrash >= 1.0']}
--- a/testing/mozharness/requirements.txt
+++ b/testing/mozharness/requirements.txt
@@ -1,24 +1,24 @@
 # These packages are needed for mozharness unit tests.
 # Output from 'pip freeze'; we may be able to use other versions of the below packages.
 Cython==0.14.1
 Fabric==1.6.0
 coverage==3.6
 distribute==0.6.35
-dulwich==0.8.7
+dulwich==0.19.6
 hg-git==0.4.0
 logilab-astng==0.24.2
-logilab-common==0.59.0
+logilab-common==1.4.2
 mercurial==4.3.1
 mock==1.0.1
 nose==1.2.1
 ordereddict==1.1
 paramiko==1.10.0
-pycrypto==2.6
+pycrypto==2.6.1
 pyflakes==0.6.1
 pylint==0.27.0
 simplejson==2.1.1
 unittest2==0.5.1
 virtualenv==1.5.1
 wsgiref==0.1.2
 urllib3==1.9.1
 google-api-python-client==1.5.1
--- a/testing/tps/setup.py
+++ b/testing/tps/setup.py
@@ -4,17 +4,17 @@
 
 
 from setuptools import setup, find_packages
 import sys
 
 version = '0.6'
 
 deps = ['httplib2 == 0.9.2',
-        'mozfile == 1.2',
+        'mozfile >= 1.2',
         'mozhttpd == 0.7',
         'mozinfo >= 0.10',
         'mozinstall == 1.16',
         'mozprocess == 0.26',
         'mozprofile == 2.0.0',
         'mozrunner == 7.0.2',
         'mozversion == 1.5',
        ]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/update-during-installation-worker.js
@@ -0,0 +1,23 @@
+'use strict';
+
+const installMayFinish = new Promise(resolve => {
+    self.finishInstall = resolve;
+});
+
+let report = { installEventFired: false };
+
+addEventListener('install', event => {
+    report.installEventFired = true;
+    let attemptUpdate = registration.update().catch(exception => {
+        report.exception = exception.name;
+    });
+    event.waitUntil(Promise.all([installMayFinish, attemptUpdate]));
+});
+
+addEventListener('message', event => {
+    if (event.data === 'finishInstall') {
+        finishInstall();
+    } else {
+        event.source.postMessage(report);
+    }
+});
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/update-not-allowed.https.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script>
+'use strict';
+
+async function spin_up_service_worker(test) {
+    const script = 'resources/update-during-installation-worker.js';
+    const scope = 'resources/blank.html';
+
+    let registration = await service_worker_unregister_and_register(test, script, scope);
+    test.add_cleanup(() => {
+        if (registration.installing) {
+            registration.installing.postMessage('finishInstall');
+        }
+        registration.unregister();
+    });
+
+    return registration;
+}
+
+promise_test(async t => {
+    const registration = await spin_up_service_worker(t);
+    const worker = registration.installing;
+
+    // spin_up_service_worker installs a cleanup hook that ensures the
+    // worker finished its installation by sending it a
+    // 'finishInstall' message, thus making sure that the registration
+    // will be cleanly removed at the end of the test.
+    assert_equals(worker.state, 'installing');
+    promise_rejects(t, 'InvalidStateError', registration.update());
+}, 'ServiceWorkerRegistration.update() from client throws while installing service worker.')
+
+promise_test(async t => {
+    const registration = await spin_up_service_worker(t);
+    const worker = registration.installing;
+    worker.postMessage('finishInstall');
+
+    // By waiting for both states at the same time, the test fails
+    // quickly if the installation fails, avoiding a timeout.
+    await Promise.race([wait_for_state(t, worker, 'activated'),
+                        wait_for_state(t, worker, 'redundant')]);
+    assert_equals(worker.state, 'activated', 'Service worker should be activated.');
+
+    const response = await new Promise(resolve => {
+        navigator.serviceWorker.onmessage = event => { resolve(event.data); };
+        worker.postMessage('PING');
+    });
+
+    // We check that the service worker instance that replied to the
+    // message is the same one that received the 'install' event since
+    // it's possible for them to be two distinct execution
+    // environments.
+    assert_true(response.installEventFired, 'Service worker should have been installed.');
+    assert_equals(response.exception, 'InvalidStateError', 'update() should have thrown.');
+}, 'ServiceWorkerRegistration.update() from installing service worker throws.');
+</script>
--- a/third_party/python/moz.build
+++ b/third_party/python/moz.build
@@ -69,16 +69,19 @@ with Files('pyyaml/**'):
     BUG_COMPONENT = ('Taskcluster', 'General')
 
 with Files('redo/**'):
     BUG_COMPONENT = ('Firefox Build System', 'General')
 
 with Files('requests*/**'):
     BUG_COMPONENT = ('Firefox Build System', 'General')
 
+with Files('requirements.*'):
+    BUG_COMPONENT = ('Firefox Build System', 'General')
+
 with Files('rsa/**'):
     BUG_COMPONENT = ('Core', 'Security: PSM')
 
 with Files('slugid/**'):
     BUG_COMPONENT = ('Taskcluster', 'Platform Libraries')
 
 with Files('virtualenv/**'):
     BUG_COMPONENT = ('Firefox Build System', 'General')
new file mode 100644
--- /dev/null
+++ b/third_party/python/requirements.in
@@ -0,0 +1,12 @@
+attrs==18.1.0
+blessings==1.7
+jsmin==2.1.0
+json-e==2.7.0
+pip-tools==3.0.0
+pipenv==2018.5.18
+pytest==3.6.2
+python-hglib==2.4
+requests==2.9.1
+six==1.10.0
+virtualenv==15.2.0
+voluptuous==0.11.5
new file mode 100644
--- /dev/null
+++ b/third_party/python/requirements.txt
@@ -0,0 +1,68 @@
+atomicwrites==1.1.5 \
+    --hash=sha256:240831ea22da9ab882b551b31d4225591e5e447a68c5e188db5b89ca1d487585 \
+    --hash=sha256:a24da68318b08ac9c9c45029f4a10371ab5b20e4226738e150e6e7c571630ae6 \
+    # via pytest
+attrs==18.1.0 \
+    --hash=sha256:4b90b09eeeb9b88c35bc642cbac057e45a5fd85367b985bd2809c62b7b939265 \
+    --hash=sha256:e0d0eb91441a3b53dab4d9b743eafc1ac44476296a2053b6ca3af0b139faf87b
+blessings==1.7 \
+    --hash=sha256:98e5854d805f50a5b58ac2333411b0482516a8210f23f43308baeb58d77c157d \
+    --hash=sha256:b1fdd7e7a675295630f9ae71527a8ebc10bfefa236b3d6aa4932ee4462c17ba3 \
+    --hash=sha256:caad5211e7ba5afe04367cdd4cfc68fa886e2e08f6f35e76b7387d2109ccea6e
+certifi==2018.4.16 \
+    --hash=sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7 \
+    --hash=sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0 \
+    # via pipenv
+click==7.0 \
+    --hash=sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13 \
+    --hash=sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7 \
+    # via pip-tools
+funcsigs==1.0.2 \
+    --hash=sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca \
+    --hash=sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50 \
+    # via pytest
+jsmin==2.1.0 \
+    --hash=sha256:5d07bf0251a4128e5e8e8eef603849b6b5741c337bff087731a248f9cc774f56
+json-e==2.7.0 \
+    --hash=sha256:d8c1ec3f5bbc7728c3a504ebe58829f283c64eca230871e4eefe974b4cdaae4a
+more-itertools==4.3.0 \
+    --hash=sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092 \
+    --hash=sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e \
+    --hash=sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d \
+    # via pytest
+pip-tools==3.0.0 \
+    --hash=sha256:4a94997602848f77ff02f660c0fcdfeaf316924ebb236c865f9742ce212aa6f9 \
+    --hash=sha256:e45e5198ce3799068642ebb0e7c9be5520bcff944c0186f79c1199a2759c970a
+pipenv==2018.5.18 \
+    --hash=sha256:04b9a8b02a3ff12a5502b335850cfdb192adcfd1d6bbdb7a7c47cae9ab9ddece \
+    --hash=sha256:e96d5bfa6822a17b2200d455aa5f9002c14361c50df1b1e51921479d7c09e741
+pluggy==0.6.0 \
+    --hash=sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff \
+    --hash=sha256:d345c8fe681115900d6da8d048ba67c25df42973bda370783cd58826442dcd7c \
+    --hash=sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5 \
+    # via pytest
+py==1.5.4 \
+    --hash=sha256:3fd59af7435864e1a243790d322d763925431213b6b8529c6ca71081ace3bbf7 \
+    --hash=sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e \
+    # via pytest
+pytest==3.6.2 \
+    --hash=sha256:8ea01fc4fcc8e1b1e305252b4bc80a1528019ab99fd3b88666c9dc38d754406c \
+    --hash=sha256:90898786b3d0b880b47645bae7b51aa9bbf1e9d1e4510c2cfd15dd65c70ea0cd
+python-hglib==2.4 \
+    --hash=sha256:693d6ed92a6566e78802c7a03c256cda33d08c63ad3f00fcfa11379b184b9462
+requests==2.9.1 \
+    --hash=sha256:113fbba5531a9e34945b7d36b33a084e8ba5d0664b703c81a7c572d91919a5b8 \
+    --hash=sha256:c577815dd00f1394203fc44eb979724b098f88264a9ef898ee45b8e5e9cf587f
+six==1.10.0 \
+    --hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \
+    --hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a
+virtualenv-clone==0.3.0 \
+    --hash=sha256:4507071d81013fd03ea9930ec26bc8648b997927a11fa80e8ee81198b57e0ac7 \
+    --hash=sha256:b5cfe535d14dc68dfc1d1bb4ac1209ea28235b91156e2bba8e250d291c3fb4f8 \
+    # via pipenv
+virtualenv==15.2.0 \
+    --hash=sha256:1d7e241b431e7afce47e77f8843a276f652699d1fa4f93b9d8ce0076fd7b0b54 \
+    --hash=sha256:e8e05d4714a1c51a2f5921e62f547fcb0f713ebbe959e0a7f585cc8bef71d11f
+voluptuous==0.11.5 \
+    --hash=sha256:303542b3fc07fb52ec3d7a1c614b329cdbee13a9d681935353d8ea56a7bfa9f1 \
+    --hash=sha256:567a56286ef82a9d7ae0628c5842f65f516abcb496e74f3f59f1d7b28df314ef
--- a/widget/MouseEvents.h
+++ b/widget/MouseEvents.h
@@ -346,16 +346,21 @@ public:
   /**
    * Returns true if the event is a real mouse event.  Otherwise, i.e., it's
    * a synthesized event by scroll or something, returns false.
    */
   bool IsReal() const
   {
     return mReason == eReal;
   }
+
+  /**
+   * Returns true if middle click paste is enabled.
+   */
+  static bool IsMiddleClickPasteEnabled();
 };
 
 /******************************************************************************
  * mozilla::WidgetDragEvent
  ******************************************************************************/
 
 class WidgetDragEvent : public WidgetMouseEvent
 {
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -646,16 +646,26 @@ WidgetInputEvent::AccelModifier()
 #endif
         break;
     }
   }
   return sAccelModifier;
 }
 
 /******************************************************************************
+ * mozilla::WidgetMouseEvent (MouseEvents.h)
+ ******************************************************************************/
+
+/* static */ bool
+WidgetMouseEvent::IsMiddleClickPasteEnabled()
+{
+  return Preferences::GetBool("middlemouse.paste", false);
+}
+
+/******************************************************************************
  * mozilla::WidgetWheelEvent (MouseEvents.h)
  ******************************************************************************/
 
 /* static */ double
 WidgetWheelEvent::ComputeOverriddenDelta(double aDelta, bool aIsForVertical)
 {
   if (!gfxPrefs::MouseWheelHasRootScrollDeltaOverride()) {
     return aDelta;