Bug 1485400 - Remove ')' from merge conflict. a=bustage-fix
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sat, 01 Sep 2018 01:56:12 +0300
changeset 434355 0aa31e702587617919d21d135d757b1004dd6aca
parent 434335 331706d8558f8dadeba4fbf5f56651130d43f092 (current diff)
parent 434354 3686bea81d04bce59d3b601e744babe3d814e3d6 (diff)
child 434356 6c83f735355d19458caa7ff34069b5676c062228
push id107338
push useraciure@mozilla.com
push dateFri, 31 Aug 2018 23:45:51 +0000
treeherdermozilla-inbound@ee4f4e66f861 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbustage-fix
bugs1485400
milestone63.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
Bug 1485400 - Remove ')' from merge conflict. a=bustage-fix
netwerk/protocol/http/nsHttpChannel.cpp
--- a/browser/components/payments/res/containers/address-form.js
+++ b/browser/components/payments/res/containers/address-form.js
@@ -90,16 +90,17 @@ export default class AddressForm extends
         supportedCountries: PaymentDialogUtils.supportedCountries,
       });
 
       // The EditAddress constructor adds `input` event listeners on the same element,
       // which update field validity. By adding our event listeners after this constructor,
       // validity will be updated before our handlers get the event
       this.form.addEventListener("input", this);
       this.form.addEventListener("invalid", this);
+      this.form.addEventListener("change", this);
 
       this.body.appendChild(this.persistCheckbox);
       this.body.appendChild(this.genericErrorText);
 
       this.footer.appendChild(this.cancelButton);
       this.footer.appendChild(this.backButton);
       this.footer.appendChild(this.saveButton);
       // Only call the connected super callback(s) once our markup is fully
@@ -205,16 +206,20 @@ export default class AddressForm extends
       }
     }
 
     this.updateSaveButtonState();
   }
 
   handleEvent(event) {
     switch (event.type) {
+      case "change": {
+        this.updateSaveButtonState();
+        break;
+      }
       case "click": {
         this.onClick(event);
         break;
       }
       case "input": {
         this.onInput(event);
         break;
       }
--- a/browser/components/payments/test/mochitest/test_address_form.html
+++ b/browser/components/payments/test/mochitest/test_address_form.html
@@ -136,16 +136,21 @@ add_task(async function test_saveButton(
   let fieldsVisiblyInvalid = form.querySelectorAll(":-moz-ui-invalid");
   is(fieldsVisiblyInvalid.length, 1, "Check 1 field visibly invalid after blanking and blur");
   is(fieldsVisiblyInvalid[0].id, "street-address", "Check #street-address is visibly invalid");
 
   fillField(form.form.querySelector("#street-address"), "404 Internet Super Highway");
   is(form.querySelectorAll(":-moz-ui-invalid").length, 0, "Check no fields visibly invalid");
   ok(!form.saveButton.disabled, "Save button is enabled after re-filling street-address");
 
+  fillField(form.form.querySelector("#country"), "CA");
+  ok(form.saveButton.disabled, "Save button is disabled after changing the country to Canada");
+  fillField(form.form.querySelector("#country"), "US");
+  ok(!form.saveButton.disabled, "Save button is enabled after changing the country to US");
+
   let messagePromise = promiseContentToChromeMessage("updateAutofillRecord");
   is(form.saveButton.textContent, "Add", "Check label");
   form.saveButton.scrollIntoView();
   synthesizeMouseAtCenter(form.saveButton, {});
 
   let details = await messagePromise;
   ok(typeof(details.messageID) == "number" && details.messageID > 0, "Check messageID type");
   delete details.messageID;
--- a/browser/components/payments/test/mochitest/test_basic_card_form.html
+++ b/browser/components/payments/test/mochitest/test_basic_card_form.html
@@ -111,16 +111,27 @@ add_task(async function test_saveButton(
   fillField(form.form.querySelector("#cc-exp-month"), "11");
   let year = (new Date()).getFullYear().toString();
   fillField(form.form.querySelector("#cc-exp-year"), year);
   fillField(form.form.querySelector("#cc-type"), "visa");
   form.saveButton.focus();
   ok(!form.saveButton.disabled,
      "Save button should be enabled since the required fields are filled");
 
+  fillField(form.form.querySelector("#cc-exp-month"), "");
+  fillField(form.form.querySelector("#cc-exp-year"), "");
+  form.saveButton.focus();
+  ok(form.saveButton.disabled,
+     "Save button should be disabled since the required fields are empty");
+  fillField(form.form.querySelector("#cc-exp-month"), "11");
+  fillField(form.form.querySelector("#cc-exp-year"), year);
+  form.saveButton.focus();
+  ok(!form.saveButton.disabled,
+     "Save button should be enabled since the required fields are filled again");
+
   info("blanking the cc-number field");
   fillField(form.form.querySelector("#cc-number"), "");
   ok(form.saveButton.disabled, "Save button is disabled after blanking cc-number");
   form.form.querySelector("#cc-number").blur();
   let fieldsVisiblyInvalid = form.querySelectorAll(":-moz-ui-invalid");
   is(fieldsVisiblyInvalid.length, 1, "Check 1 field visibly invalid after blanking and blur");
   is(fieldsVisiblyInvalid[0].id, "cc-number", "Check #cc-number is visibly invalid");
 
@@ -376,27 +387,34 @@ add_task(async function test_field_valid
   let form = new BasicCardForm();
   form.dataset.updateButtonLabel = "Add";
   await form.promiseReady;
   display.appendChild(form);
   await asyncElementRendered();
 
   let ccNumber = form.form.querySelector("#cc-number");
   let nameInput = form.form.querySelector("#cc-name");
+  let monthInput = form.form.querySelector("#cc-exp-month");
+  let yearInput = form.form.querySelector("#cc-exp-year");
 
   info("test with valid cc-number but missing cc-name");
   fillField(ccNumber, "4111111111111111");
   ok(ccNumber.checkValidity(), "cc-number field is valid with good input");
   ok(!nameInput.checkValidity(), "cc-name field is invalid when empty");
   ok(form.saveButton.disabled, "Save button should be disabled with incomplete input");
 
-  info("correct by adding cc-name value");
+  info("correct by adding cc-name and expiration values");
   fillField(nameInput, "First");
+  fillField(monthInput, "11");
+  let year = (new Date()).getFullYear().toString();
+  fillField(yearInput, year);
   ok(ccNumber.checkValidity(), "cc-number field is valid with good input");
   ok(nameInput.checkValidity(), "cc-name field is valid with a value");
+  ok(monthInput.checkValidity(), "cc-exp-month field is valid with a value");
+  ok(yearInput.checkValidity(), "cc-exp-year field is valid with a value");
   ok(!form.saveButton.disabled, "Save button should not be disabled with good input");
 
   info("edit to make the cc-number invalid");
   ccNumber.focus();
   sendString("aa");
   nameInput.focus();
   sendString("Surname");
 
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1548,24 +1548,33 @@ var gMainPane = {
       this._list.removeChild(this._list.lastChild);
 
     var visibleTypes = this._visibleTypes;
 
     // If the user is filtering the list, then only show matching types.
     if (this._filter.value)
       visibleTypes = visibleTypes.filter(this._matchesFilter, this);
 
-    for (let visibleType of visibleTypes) {
-      let item = new HandlerListItem(visibleType);
-      item.connectAndAppendToList(this._list);
-
-      if (visibleType.type === lastSelectedType) {
-        this._list.selectedItem = item.node;
+    let items = visibleTypes.map(visibleType => new HandlerListItem(visibleType));
+    let itemsFragment = document.createDocumentFragment();
+    let lastSelectedItem;
+    for (let item of items) {
+      item.createNode(itemsFragment);
+      if (item.handlerInfoWrapper.type == lastSelectedType) {
+        lastSelectedItem = item;
       }
     }
+    this._list.appendChild(itemsFragment);
+    for (let item of items) {
+      item.setupNode();
+    }
+
+    if (lastSelectedItem) {
+      this._list.selectedItem = lastSelectedItem.node;
+    }
   },
 
   _matchesFilter(aType) {
     var filterValue = this._filter.value.toLowerCase();
     return aType.typeDescription.toLowerCase().includes(filterValue) ||
            aType.actionDescription.toLowerCase().includes(filterValue);
   },
 
@@ -2491,21 +2500,23 @@ class HandlerListItem {
       if (value) {
         node.setAttribute(name, value);
       } else {
         node.removeAttribute(name);
       }
     }
   }
 
-  connectAndAppendToList(list) {
+  createNode(list) {
     list.appendChild(document.importNode(gHandlerListItemFragment, true));
     this.node = list.lastChild;
     gNodeToObjectMap.set(this.node, this);
-
+  }
+
+  setupNode() {
     this.node.querySelector(".actionsMenu").addEventListener("command",
       event => gMainPane.onSelectAction(event.originalTarget));
 
     let typeDescription = this.handlerInfoWrapper.typeDescription;
     this.setOrRemoveAttributes([
       [null, "type", this.handlerInfoWrapper.type],
       [".typeContainer", "tooltiptext", typeDescription],
       [".typeDescription", "value", typeDescription],
--- a/browser/extensions/formautofill/content/editCreditCard.xhtml
+++ b/browser/extensions/formautofill/content/editCreditCard.xhtml
@@ -23,17 +23,17 @@
         order to get proper label styling with :focus and :moz-ui-invalid.
       -->
     <label id="cc-number-container" class="container">
       <span id="invalidCardNumberString" hidden="hidden" data-localization="invalidCardNumber"></span>
       <input id="cc-number" type="text" required="required" minlength="9" pattern="[- 0-9]+"/>
       <span data-localization="cardNumber" class="label-text"/>
     </label>
     <label id="cc-exp-month-container" class="container">
-      <select id="cc-exp-month">
+      <select id="cc-exp-month" required="required">
         <option/>
         <option value="1">01</option>
         <option value="2">02</option>
         <option value="3">03</option>
         <option value="4">04</option>
         <option value="5">05</option>
         <option value="6">06</option>
         <option value="7">07</option>
@@ -41,17 +41,17 @@
         <option value="9">09</option>
         <option value="10">10</option>
         <option value="11">11</option>
         <option value="12">12</option>
       </select>
       <span data-localization="cardExpiresMonth" class="label-text"/>
     </label>
     <label id="cc-exp-year-container" class="container">
-      <select id="cc-exp-year">
+      <select id="cc-exp-year" required="required">
         <option/>
       </select>
       <span data-localization="cardExpiresYear" class="label-text"/>
     </label>
     <label id="cc-name-container" class="container">
       <input id="cc-name" type="text" required="required"/>
       <span data-localization="nameOnCard" class="label-text"/>
     </label>
--- a/browser/locales/en-US/browser/preferences/connection.ftl
+++ b/browser/locales/en-US/browser/preferences/connection.ftl
@@ -79,13 +79,13 @@ connection-proxy-autologin =
     .tooltip = This option silently authenticates you to proxies when you have saved credentials for them. You will be prompted if authentication fails.
 
 connection-proxy-socks-remote-dns =
     .label = Proxy DNS when using SOCKS v5
     .accesskey = D
 
 connection-dns-over-https =
     .label = Enable DNS over HTTPS
-    .accesskey = H
+    .accesskey = b
 
 connection-dns-over-https-url = URL
     .accesskey = U
     .tooltiptext = URL for resolving DNS over HTTPS
--- a/build/macosx/cross-mozconfig.common
+++ b/build/macosx/cross-mozconfig.common
@@ -27,17 +27,17 @@ FLAGS="-target x86_64-apple-darwin11 -B 
 export CC="$topsrcdir/clang/bin/clang $FLAGS"
 export CXX="$topsrcdir/clang/bin/clang++ $FLAGS"
 export CPP="$topsrcdir/clang/bin/clang $FLAGS -E"
 export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
 export LDFLAGS="-Wl,-syslibroot,$CROSS_SYSROOT -Wl,-dead_strip"
 export BINDGEN_CFLAGS="$FLAGS"
 export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_PATH/bin/x86_64-apple-darwin11-
 export DSYMUTIL=$topsrcdir/build/macosx/llvm-dsymutil
-mk_add_options "export REAL_DSYMUTIL=$topsrcdir/llvm-dsymutil/bin/llvm-dsymutil"
+mk_add_options "export REAL_DSYMUTIL=$topsrcdir/llvm-dsymutil/bin/dsymutil"
 export MKFSHFS=$topsrcdir/hfsplus-tools/newfs_hfs
 export DMG_TOOL=$topsrcdir/dmg/dmg
 export HFS_TOOL=$topsrcdir/dmg/hfsplus
 
 export HOST_CC="$topsrcdir/clang/bin/clang"
 export HOST_CXX="$topsrcdir/clang/bin/clang++"
 export HOST_CPP="$topsrcdir/clang/bin/clang -E"
 export HOST_CFLAGS="-g"
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -3550,24 +3550,26 @@ public:
   }
 
   void DecrementIgnoreOpensDuringUnloadCounter()
   {
     MOZ_ASSERT(mIgnoreOpensDuringUnloadCounter);
     --mIgnoreOpensDuringUnloadCounter;
   }
 
-  void IncrementTrackerCount(bool aIsTrackerBlocked)
+  void IncrementTrackerCount()
   {
     MOZ_ASSERT(!GetSameTypeParentDocument());
-
     ++mNumTrackersFound;
-    if (aIsTrackerBlocked) {
-      ++mNumTrackersBlocked;
-    }
+  }
+
+  void IncrementTrackerBlockedCount()
+  {
+    MOZ_ASSERT(!GetSameTypeParentDocument());
+    ++mNumTrackersBlocked;
   }
 
   uint32_t NumTrackersFound()
   {
     MOZ_ASSERT(!GetSameTypeParentDocument() || mNumTrackersFound == 0);
 
     return mNumTrackersFound;
   }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
@@ -358,22 +358,17 @@ public class GeckoView extends FrameLayo
     public void onAttachedToWindow() {
         if (mSession == null) {
             setSession(new GeckoSession(), GeckoRuntime.getDefault(getContext()));
         }
 
         if (!mSession.isOpen()) {
             mSession.open(mRuntime);
         }
-        // Temporary solution until we find out why mRuntime can end up as null here. It means we
-        // might miss an orientation change if we were background OOM-killed, but it's better than
-        // crashing. See bug 1484001.
-        if (mRuntime != null) {
-            mRuntime.orientationChanged();
-        }
+        mRuntime.orientationChanged();
 
         super.onAttachedToWindow();
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
 
@@ -423,21 +418,46 @@ public class GeckoView extends FrameLayo
         if (!(state instanceof SavedState)) {
             super.onRestoreInstanceState(state);
             return;
         }
 
         final SavedState ss = (SavedState) state;
         super.onRestoreInstanceState(ss.getSuperState());
 
-        if (mSession == null && ss.session != null) {
-            setSession(ss.session, ss.session.getRuntime());
-        } else if (ss.session != null) {
-            mSession.transferFrom(ss.session);
-            mRuntime = mSession.getRuntime();
+        restoreSession(ss.session);
+    }
+
+    private void restoreSession(final @Nullable GeckoSession savedSession) {
+        if (savedSession == null) {
+            return;
+        }
+
+        GeckoRuntime runtimeToRestore = savedSession.getRuntime();
+        // Note: setSession sets either both mSession and mRuntime, or none of them. So if we don't
+        // have an mRuntime here, we won't have an mSession, either.
+        if (mRuntime == null) {
+            if (runtimeToRestore == null) {
+                // If the saved session is closed, we fall back to using the default runtime, same
+                // as we do when we don't even have an mSession in onAttachedToWindow().
+                runtimeToRestore = GeckoRuntime.getDefault(getContext());
+            }
+            setSession(savedSession, runtimeToRestore);
+        // We already have a session. We only want to transfer the saved session if its close/open
+        // state is the same or better as our current session.
+        } else if (savedSession.isOpen() || !mSession.isOpen()) {
+            if (mSession.isOpen()) {
+                mSession.close();
+            }
+            mSession.transferFrom(savedSession);
+            if (runtimeToRestore != null) {
+                // If the saved session was open, we transfer its runtime as well. Otherwise we just
+                // keep the runtime we already had in mRuntime.
+                mRuntime = runtimeToRestore;
+            }
         }
     }
 
     @Override
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
 
         if (mSession != null) {
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -906,18 +906,16 @@ nsChannelClassifier::SetBlockedContent(n
   auto* pwin = nsPIDOMWindowOuter::From(win);
   nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
   if (!docShell) {
     return NS_OK;
   }
   nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
   NS_ENSURE_TRUE(doc, NS_OK);
 
-  doc->IncrementTrackerCount(true);
-
   unsigned state;
   if (aErrorCode == NS_ERROR_TRACKING_URI) {
     state = nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
   } else {
     state = nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
   }
   pwin->NotifyContentBlockingState(state, channel);
 
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -326,16 +326,20 @@ HttpBaseChannel::SetIsTrackingResource(b
 
   if (aIsThirdParty) {
     MOZ_ASSERT(!mIsFirstPartyTrackingResource);
     mIsThirdPartyTrackingResource = true;
   } else {
     MOZ_ASSERT(!mIsThirdPartyTrackingResource);
     mIsFirstPartyTrackingResource = true;
   }
+
+  if (mLoadInfo) {
+    MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetIsTracker(true));
+  }
 }
 
 nsresult
 HttpBaseChannel::Init(nsIURI *aURI,
                       uint32_t aCaps,
                       nsProxyInfo *aProxyInfo,
                       uint32_t aProxyResolveFlags,
                       nsIURI *aProxyURI,
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -742,29 +742,31 @@ HttpChannelChild::DoOnStartRequest(nsIRe
   }
 
   if (mSynthesizedResponsePump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
     mSynthesizedResponsePump->PeekStream(CallTypeSniffers,
                                          static_cast<nsIChannel*>(this));
   }
 
   bool isTracker;
-  if (NS_SUCCEEDED(mLoadInfo->GetIsTracker(&isTracker)) && isTracker) {
+  MOZ_ALWAYS_SUCCEEDS(mLoadInfo->GetIsTracker(&isTracker));
+  if (isTracker) {
     bool isTrackerBlocked;
-    Unused << mLoadInfo->GetIsTrackerBlocked(&isTrackerBlocked);
-
+    MOZ_ALWAYS_SUCCEEDS(mLoadInfo->GetIsTrackerBlocked(&isTrackerBlocked));
     LOG(("HttpChannelChild::DoOnStartRequest FastBlock %d [this=%p]\n",
          isTrackerBlocked,
          this));
 
     nsCOMPtr<nsIDocument> doc;
     if (!NS_WARN_IF(NS_FAILED(GetTopDocument(this,
                                              getter_AddRefs(doc))))) {
-
-      doc->IncrementTrackerCount(isTrackerBlocked);
+      doc->IncrementTrackerCount();
+      if (isTrackerBlocked) {
+        doc->IncrementTrackerBlockedCount();
+      }
     }
   }
 
   nsresult rv = mListener->OnStartRequest(aRequest, aContext);
   if (NS_FAILED(rv)) {
     Cancel(rv);
     return;
   }
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -689,47 +689,51 @@ IsContentPolicyTypeWhitelistedForFastBlo
     return false;
   }
 }
 
 bool
 nsHttpChannel::CheckFastBlocked()
 {
     LOG(("nsHttpChannel::CheckFastBlocked [this=%p]\n", this));
+    MOZ_ASSERT(mIsThirdPartyTrackingResource);
 
     static bool sFastBlockInited = false;
     static bool sIsFastBlockEnabled = false;
     static uint32_t sFastBlockTimeout = 0;
 
     if (!sFastBlockInited) {
         sFastBlockInited = true;
         Preferences::AddBoolVarCache(&sIsFastBlockEnabled, "browser.fastblock.enabled");
         Preferences::AddUintVarCache(&sFastBlockTimeout, "browser.fastblock.timeout");
     }
 
     TimeStamp timestamp;
-    if (NS_FAILED(GetNavigationStartTimeStamp(&timestamp))) {
+    if (NS_FAILED(GetNavigationStartTimeStamp(&timestamp)) || !timestamp) {
+        LOG(("FastBlock passed (no timestamp) [this=%p]\n", this));
+
         return false;
     }
 
     if (!StaticPrefs::browser_contentblocking_enabled() ||
         !sIsFastBlockEnabled ||
         IsContentPolicyTypeWhitelistedForFastBlock(mLoadInfo) ||
-        !timestamp ||
         // If the user has interacted with the document, we disable fastblock.
         (mLoadInfo && mLoadInfo->GetDocumentHasUserInteracted())) {
+
+        LOG(("FastBlock passed (invalid) [this=%p]\n", this));
+        
         return false;
     }
 
     TimeDuration duration = TimeStamp::NowLoRes() - timestamp;
     bool isFastBlocking = duration.ToMilliseconds() >= sFastBlockTimeout;
 
-    if (mLoadInfo) {
-        MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetIsTracker(true));
-        MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetIsTrackerBlocked(isFastBlocking));
+    if (isFastBlocking && mLoadInfo) {
+        MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetIsTrackerBlocked(true));
     }
 
     LOG(("FastBlock %s (%lf) [this=%p]\n",
          isFastBlocking ? "timeout" : "passed",
          duration.ToMilliseconds(),
          this));
     return isFastBlocking;
 }
@@ -6104,16 +6108,20 @@ nsHttpChannel::ContinueCancelledByTracki
 
 nsresult
 nsHttpChannel::CancelInternal(nsresult status)
 {
     bool trackingProtectionCancellationPending =
       !!mTrackingProtectionCancellationPending;
     if (status == NS_ERROR_TRACKING_URI) {
       mTrackingProtectionCancellationPending = 0;
+      if (mLoadInfo) {
+        MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetIsTracker(true));
+        MOZ_ALWAYS_SUCCEEDS(mLoadInfo->SetIsTrackerBlocked(true));
+      }
     }
 
     mCanceled = true;
     mStatus = status;
     if (mProxyRequest)
         mProxyRequest->Cancel(status);
     CancelNetworkRequest(status);
     mCacheInputStream.CloseAndRelease();
--- a/python/mozbuild/mozbuild/android_version_code.py
+++ b/python/mozbuild/mozbuild/android_version_code.py
@@ -44,36 +44,34 @@ def android_version_code_v1(buildid, cpu
 
     The 17 bits labelled 't' represent the number of hours since midnight on
     September 1, 2015.  (2015090100 in YYYYMMMDDHH format.)  This yields a
     little under 15 years worth of hourly build identifiers, since 2**17 / (366
     * 24) =~ 14.92.
 
     The bits labelled 'x', 'p', and 'g' are feature flags.
 
-    The bit labelled 'x' is 1 if the build is for an x86 or ARM64 architecture,
-    and 0 otherwise, which means the build is for a (32-bit) ARM architecture.
+    The bit labelled 'x' is 1 if the build is for an x86 or x86-64 architecture,
+    and 0 otherwise, which means the build is for an ARM or ARM64 architecture.
     (Fennec no longer supports ARMv6, so ARM is equivalent to ARMv7.
      ARM64 is also known as AArch64; it is logically ARMv8.)
 
-    For the same release, x86 and ARM64 builds have higher version codes and
+    For the same release, x86 and x86_64 builds have higher version codes and
     take precedence over ARM builds, so that they are preferred over ARM on
     devices that have ARM emulation.
 
-    The bit labelled 'p' is a placeholder that is always 0 (for now).
-
-    Firefox no longer supports API 14 or earlier.
+    The bit labelled 'p' is 1 if the build is for a 64-bit architecture (x86-64
+    or ARM64), and 0 otherwise, which means the build is for a 32-bit
+    architecture (x86 or ARM). 64-bit builds have higher version codes so
+    they take precedence over 32-bit builds on devices that support 64-bit.
 
-    This version code computation allows for a split on API levels that allowed
-    us to ship builds specifically for Gingerbread (API 9-10); we preserve
-    that functionality for sanity's sake, and to allow us to reintroduce a
-    split in the future.
-
-    At present, the bit labelled 'g' is 1 if the build is an ARM build
-    targeting API 16+, which will always be the case.
+    The bit labelled 'g' is 1 if the build targets a recent API level, which
+    is currently always the case, because Firefox no longer ships releases that
+    are split by API levels. However, we may reintroduce a split in the future,
+    in which case the release that targets an older API level will
 
     We throw an explanatory exception when we are within one calendar year of
     running out of build events.  This gives lots of time to update the version
     scheme.  The responsible individual should then bump the range (to allow
     builds to continue) and use the time remaining to update the version scheme
     via the reserved high order bits.
 
     N.B.: the reserved 0 bit to the left of the highest order 't' bit can,
@@ -108,34 +106,37 @@ def android_version_code_v1(buildid, cpu
                          "android:versionCode from build ID %s: "
                          "; YOU HAVE ONE YEAR TO UPDATE THE VERSION SCHEME." % buildid)
 
     version = 0b1111000001000000000000000000000
     # We reserve 1 "middle" high order bit for the future, and 3 low order bits
     # for architecture and APK splits.
     version |= base << 3
 
-    # None is interpreted as arm.
-    if not cpu_arch or cpu_arch == 'armeabi-v7a':
-        # 0 is interpreted as SDK 9.
-        if not min_sdk or min_sdk == 9:
-            pass
-        # This used to compare to 11. The 16+ APK directly supersedes 11+, so
-        # we reuse this check.
-        elif min_sdk == 16:
-            version |= 1 << 0
-        else:
-            raise ValueError("Don't know how to compute android:versionCode "
-                             "for CPU arch %s and min SDK %s" % (cpu_arch, min_sdk))
-    elif cpu_arch in ['x86', 'arm64-v8a']:
+    # 'x' bit is 1 for x86/x86-64 architectures (`None` is interpreted as ARM).
+    if cpu_arch in ['x86', 'x86_64']:
         version |= 1 << 2
+    elif not cpu_arch or cpu_arch in ['armeabi-v7a', 'arm64-v8a']:
+        pass
     else:
         raise ValueError("Don't know how to compute android:versionCode "
                          "for CPU arch %s" % cpu_arch)
 
+    # 'p' bit is 1 for 64-bit architectures.
+    if cpu_arch in ['arm64-v8a', 'x86_64']:
+        version |= 1 << 1
+    elif cpu_arch in ['armeabi-v7a', 'x86']:
+        pass
+    else:
+        raise ValueError("Don't know how to compute android:versionCode "
+                         "for CPU arch %s" % cpu_arch)
+
+    # 'g' bit is currently always 1, but may depend on `min_sdk` in the future.
+    version |= 1 << 0
+
     return version
 
 def android_version_code(buildid, *args, **kwargs):
     base = int(str(buildid))
     if base < V1_CUTOFF:
         return android_version_code_v0(buildid, *args, **kwargs)
     else:
         return android_version_code_v1(buildid, *args, **kwargs)
--- a/python/mozlint/mozlint/roller.py
+++ b/python/mozlint/mozlint/roller.py
@@ -173,18 +173,17 @@ class LintRoller(object):
 
     def roll(self, paths=None, outgoing=None, workdir=None, num_procs=None):
         """Run all of the registered linters against the specified file paths.
 
         :param paths: An iterable of files and/or directories to lint.
         :param outgoing: Lint files touched by commits that are not on the remote repository.
         :param workdir: Lint all files touched in the working directory.
         :param num_procs: The number of processes to use. Default: cpu count
-        :return: A dictionary with file names as the key, and a list of
-                 :class:`~result.Issue`s as the value.
+        :return: A :class:`~result.ResultSummary` instance.
         """
         if not self.linters:
             raise LintersNotConfigured
 
         self.result.reset()
 
         # Need to use a set in case vcs operations specify the same file
         # more than once.
@@ -210,17 +209,17 @@ class LintRoller(object):
                     print("warning: could not find default push, specify a remote for --outgoing")
         except CalledProcessError as e:
             print("error running: {}".format(' '.join(e.cmd)))
             if e.output:
                 print(e.output)
 
         if not (paths or vcs_paths) and (workdir or outgoing):
             print("warning: no files linted")
-            return {}
+            return self.result
 
         # Make sure all paths are absolute. Join `paths` to cwd and `vcs_paths` to root.
         paths = set(map(os.path.abspath, paths))
         vcs_paths = set([os.path.join(self.root, p) if not os.path.isabs(p) else p
                          for p in vcs_paths])
 
         num_procs = num_procs or cpu_count()
         jobs = list(self._generate_jobs(paths, vcs_paths, num_procs))
--- a/python/mozlint/test/test_roller.py
+++ b/python/mozlint/test/test_roller.py
@@ -95,16 +95,28 @@ def test_roll_with_excluded_path(lint, l
 
     lint.read(linters)
     result = lint.roll(files)
 
     assert len(result.issues) == 0
     assert result.failed == set([])
 
 
+def test_roll_with_no_files_to_lint(lint, linters, capfd):
+    lint.read(linters)
+    lint.mock_vcs([])
+    result = lint.roll([], workdir=True)
+    assert isinstance(result, ResultSummary)
+    assert len(result.issues) == 0
+    assert len(result.failed) == 0
+
+    out, err = capfd.readouterr()
+    assert 'warning: no files linted' in out
+
+
 def test_roll_with_invalid_extension(lint, lintdir, filedir):
     lint.read(os.path.join(lintdir, 'external.yml'))
     result = lint.roll(os.path.join(filedir, 'foobar.py'))
     assert len(result.issues) == 0
     assert result.failed == set([])
 
 
 def test_roll_with_failure_code(lint, lintdir, files):
--- a/taskcluster/scripts/misc/build-llvm-dsymutil.sh
+++ b/taskcluster/scripts/misc/build-llvm-dsymutil.sh
@@ -9,60 +9,28 @@ UPLOAD_DIR=$HOME/artifacts
 
 cd $HOME_DIR/src
 
 . taskcluster/scripts/misc/tooltool-download.sh
 
 git clone -n https://github.com/llvm-mirror/llvm
 
 cd llvm
-git checkout 4727bc748a48e46824eae55a81ae890cd25c3a34
-
-patch -p1 <<'EOF'
-diff --git a/lib/DebugInfo/DWARF/DWARFDie.cpp b/lib/DebugInfo/DWARF/DWARFDie.cpp
-index 17559d2..b08a8d9 100644
---- a/lib/DebugInfo/DWARF/DWARFDie.cpp
-+++ b/lib/DebugInfo/DWARF/DWARFDie.cpp
-@@ -304,20 +304,24 @@ DWARFDie::find(ArrayRef<dwarf::Attribute> Attrs) const {
- 
- Optional<DWARFFormValue>
- DWARFDie::findRecursively(ArrayRef<dwarf::Attribute> Attrs) const {
-   if (!isValid())
-     return None;
-   if (auto Value = find(Attrs))
-     return Value;
-   if (auto Die = getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) {
-+    if (Die.getOffset() == getOffset())
-+      return None;
-     if (auto Value = Die.findRecursively(Attrs))
-       return Value;
-   }
-   if (auto Die = getAttributeValueAsReferencedDie(DW_AT_specification)) {
-+    if (Die.getOffset() == getOffset())
-+      return None;
-     if (auto Value = Die.findRecursively(Attrs))
-       return Value;
-   }
-   return None;
- }
- 
- DWARFDie
- DWARFDie::getAttributeValueAsReferencedDie(dwarf::Attribute Attr) const {
-EOF
+git checkout 9df0977d9409b093156ebbd8e1ac99bc32b9eb39
 
 mkdir build
 cd build
 
 cmake \
   -GNinja \
   -DCMAKE_BUILD_TYPE=Release \
   -DLLVM_TARGETS_TO_BUILD=X86 \
   -DCMAKE_C_COMPILER=$HOME_DIR/src/gcc/bin/gcc \
   ..
 
 export LD_LIBRARY_PATH=$HOME_DIR/src/gcc/lib64
 
-ninja llvm-dsymutil llvm-symbolizer
+ninja dsymutil llvm-symbolizer
 
-tar --xform='s,^,llvm-dsymutil/,' -Jcf llvm-dsymutil.tar.xz bin/llvm-dsymutil bin/llvm-symbolizer
+tar --xform='s,^,llvm-dsymutil/,' -Jcf llvm-dsymutil.tar.xz bin/dsymutil bin/llvm-symbolizer
 
 mkdir -p $UPLOAD_DIR
 cp llvm-dsymutil.tar.xz $UPLOAD_DIR
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -3138,17 +3138,17 @@ GeckoDriver.prototype.fullscreenWindow =
  * no modal is displayed.
  */
 GeckoDriver.prototype.dismissDialog = async function() {
   let win = assert.open(this.getCurrentWindow());
   this._checkIfAlertIsPresent();
 
   await new Promise(resolve => {
     win.addEventListener("DOMModalDialogClosed", whenIdle(win, () => {
-      this.dialog = modal.findModalDialogs(this.curBrowser);
+      this.dialog = null;
       resolve();
     }), {once: true});
 
     let {button0, button1} = this.dialog.ui;
     (button1 ? button1 : button0).click();
   });
 };
 
@@ -3157,17 +3157,17 @@ GeckoDriver.prototype.dismissDialog = as
  * no modal is displayed.
  */
 GeckoDriver.prototype.acceptDialog = async function() {
   let win = assert.open(this.getCurrentWindow());
   this._checkIfAlertIsPresent();
 
   await new Promise(resolve => {
     win.addEventListener("DOMModalDialogClosed", whenIdle(win, () => {
-      this.dialog = modal.findModalDialogs(this.curBrowser);
+      this.dialog = null;
       resolve();
     }), {once: true});
 
     let {button0} = this.dialog.ui;
     button0.click();
   });
 };
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py
@@ -1,193 +1,230 @@
+# 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
 
 from marionette_driver.by import By
 from marionette_driver.expected import element_present
 from marionette_driver import errors
 from marionette_driver.marionette import Alert
 from marionette_driver.wait import Wait
 
-from marionette_harness import MarionetteTestCase, parameterized, WindowManagerMixin
+from marionette_harness import MarionetteTestCase, WindowManagerMixin
 
 
 class BaseAlertTestCase(WindowManagerMixin, MarionetteTestCase):
 
     @property
     def alert_present(self):
         try:
             Alert(self.marionette).text
             return True
         except errors.NoAlertPresentException:
             return False
 
     def wait_for_alert(self, timeout=None):
         Wait(self.marionette, timeout=timeout).until(
             lambda _: self.alert_present)
 
+    def wait_for_alert_closed(self, timeout=None):
+        Wait(self.marionette, timeout=timeout).until(
+            lambda _: not self.alert_present)
+
 
 class TestTabModalAlerts(BaseAlertTestCase):
 
     def setUp(self):
         super(TestTabModalAlerts, self).setUp()
         self.assertTrue(self.marionette.get_pref("prompts.tab_modal.enabled",
                         "Tab modal alerts should be enabled by default."))
 
         self.test_page = self.marionette.absolute_url("test_tab_modal_dialogs.html")
         self.marionette.navigate(self.test_page)
 
     def tearDown(self):
-        # Ensure to close all possible remaining tab modal dialogs
+        # Ensure to close a possible remaining tab modal dialog
         try:
-            while True:
-                alert = self.marionette.switch_to_alert()
-                alert.dismiss()
-        except errors.NoAlertPresentException:
+            alert = self.marionette.switch_to_alert()
+            alert.dismiss()
+
+            self.wait_for_alert_closed()
+        except:
             pass
 
         super(TestTabModalAlerts, self).tearDown()
 
     def test_no_alert_raises(self):
         with self.assertRaises(errors.NoAlertPresentException):
             Alert(self.marionette).accept()
         with self.assertRaises(errors.NoAlertPresentException):
             Alert(self.marionette).dismiss()
 
+    def test_alert_accept(self):
+        self.marionette.find_element(By.ID, "tab-modal-alert").click()
+        self.wait_for_alert()
+        alert = self.marionette.switch_to_alert()
+        alert.accept()
+
+    def test_alert_dismiss(self):
+        self.marionette.find_element(By.ID, "tab-modal-alert").click()
+        self.wait_for_alert()
+        alert = self.marionette.switch_to_alert()
+        alert.dismiss()
+
+    def test_confirm_accept(self):
+        self.marionette.find_element(By.ID, "tab-modal-confirm").click()
+        self.wait_for_alert()
+        alert = self.marionette.switch_to_alert()
+        alert.accept()
+        self.wait_for_condition(
+            lambda mn: mn.find_element(By.ID, "confirm-result").text == "true")
+
+    def test_confirm_dismiss(self):
+        self.marionette.find_element(By.ID, "tab-modal-confirm").click()
+        self.wait_for_alert()
+        alert = self.marionette.switch_to_alert()
+        alert.dismiss()
+        self.wait_for_condition(
+            lambda mn: mn.find_element(By.ID, "confirm-result").text == "false")
+
+    def test_prompt_accept(self):
+        self.marionette.find_element(By.ID, "tab-modal-prompt").click()
+        self.wait_for_alert()
+        alert = self.marionette.switch_to_alert()
+        alert.accept()
+        self.wait_for_condition(
+            lambda mn: mn.find_element(By.ID, "prompt-result").text == "")
+
+    def test_prompt_dismiss(self):
+        self.marionette.find_element(By.ID, "tab-modal-prompt").click()
+        self.wait_for_alert()
+        alert = self.marionette.switch_to_alert()
+        alert.dismiss()
+        self.wait_for_condition(
+            lambda mn: mn.find_element(By.ID, "prompt-result").text == "null")
+
     def test_alert_opened_before_session_starts(self):
         self.marionette.find_element(By.ID, "tab-modal-alert").click()
         self.wait_for_alert()
 
         # Restart the session to ensure we still find the formerly left-open dialog.
         self.marionette.delete_session()
         self.marionette.start_session()
 
         alert = self.marionette.switch_to_alert()
         alert.dismiss()
 
-    @parameterized("alert", "alert", "undefined")
-    @parameterized("confirm", "confirm", "true")
-    @parameterized("prompt", "prompt", "")
-    def test_accept(self, value, result):
-        self.marionette.find_element(By.ID, "tab-modal-{}".format(value)).click()
+    def test_alert_text(self):
+        with self.assertRaises(errors.NoAlertPresentException):
+            alert = self.marionette.switch_to_alert()
+            alert.text
+        self.marionette.find_element(By.ID, "tab-modal-alert").click()
         self.wait_for_alert()
         alert = self.marionette.switch_to_alert()
+        self.assertEqual(alert.text, "Marionette alert")
         alert.accept()
-        self.assertEqual(self.marionette.find_element(By.ID, "text").text, result)
 
-    @parameterized("alert", "alert", "undefined")
-    @parameterized("confirm", "confirm", "false")
-    @parameterized("prompt", "prompt", "null")
-    def test_dismiss(self, value, result):
-        self.marionette.find_element(By.ID, "tab-modal-{}".format(value)).click()
-        self.wait_for_alert()
-        alert = self.marionette.switch_to_alert()
-        alert.dismiss()
-        self.assertEqual(self.marionette.find_element(By.ID, "text").text, result)
-
-    @parameterized("alert", "alert", "Marionette alert")
-    @parameterized("confirm", "confirm", "Marionette confirm")
-    @parameterized("prompt", "prompt", "Marionette prompt")
-    def test_text(self, value, text):
+    def test_prompt_text(self):
         with self.assertRaises(errors.NoAlertPresentException):
             alert = self.marionette.switch_to_alert()
             alert.text
-        self.marionette.find_element(By.ID, "tab-modal-{}".format(value)).click()
+        self.marionette.find_element(By.ID, "tab-modal-prompt").click()
         self.wait_for_alert()
         alert = self.marionette.switch_to_alert()
-        self.assertEqual(alert.text, text)
+        self.assertEqual(alert.text, "Marionette prompt")
         alert.accept()
 
-    @parameterized("alert", "alert")
-    @parameterized("confirm", "confirm")
-    def test_set_text_throws(self, value):
+    def test_confirm_text(self):
+        with self.assertRaises(errors.NoAlertPresentException):
+            alert = self.marionette.switch_to_alert()
+            alert.text
+        self.marionette.find_element(By.ID, "tab-modal-confirm").click()
+        self.wait_for_alert()
+        alert = self.marionette.switch_to_alert()
+        self.assertEqual(alert.text, "Marionette confirm")
+        alert.accept()
+
+    def test_set_text_throws(self):
         with self.assertRaises(errors.NoAlertPresentException):
             Alert(self.marionette).send_keys("Foo")
-        self.marionette.find_element(By.ID, "tab-modal-{}".format(value)).click()
+        self.marionette.find_element(By.ID, "tab-modal-alert").click()
         self.wait_for_alert()
         alert = self.marionette.switch_to_alert()
         with self.assertRaises(errors.ElementNotInteractableException):
             alert.send_keys("Foo")
         alert.accept()
 
     def test_set_text_accept(self):
         self.marionette.find_element(By.ID, "tab-modal-prompt").click()
         self.wait_for_alert()
         alert = self.marionette.switch_to_alert()
-        alert.send_keys("Foo bar")
+        alert.send_keys("Some text!")
         alert.accept()
-        self.assertEqual(self.marionette.find_element(By.ID, "text").text, "Foo bar")
+        self.wait_for_condition(
+            lambda mn: mn.find_element(By.ID, "prompt-result").text == "Some text!")
 
     def test_set_text_dismiss(self):
         self.marionette.find_element(By.ID, "tab-modal-prompt").click()
         self.wait_for_alert()
         alert = self.marionette.switch_to_alert()
         alert.send_keys("Some text!")
         alert.dismiss()
-        self.assertEqual(self.marionette.find_element(By.ID, "text").text, "null")
+        self.wait_for_condition(
+            lambda mn: mn.find_element(By.ID, "prompt-result").text == "null")
 
     def test_unrelated_command_when_alert_present(self):
         self.marionette.find_element(By.ID, "tab-modal-alert").click()
         self.wait_for_alert()
         with self.assertRaises(errors.UnexpectedAlertOpen):
-            self.marionette.find_element(By.ID, "text")
+            self.marionette.find_element(By.ID, "click-result")
 
     def test_modal_is_dismissed_after_unexpected_alert(self):
         self.marionette.find_element(By.ID, "tab-modal-alert").click()
         self.wait_for_alert()
         with self.assertRaises(errors.UnexpectedAlertOpen):
-            self.marionette.find_element(By.ID, "text")
+            self.marionette.find_element(By.ID, "click-result")
 
         assert not self.alert_present
 
-    def test_handle_two_modal_dialogs(self):
-        self.marionette.find_element(By.ID, "open-two-dialogs").click()
-
-        self.wait_for_alert()
-        alert1 = self.marionette.switch_to_alert()
-        alert1.send_keys("foo")
-        alert1.accept()
-
-        alert2 = self.marionette.switch_to_alert()
-        alert2.send_keys("bar")
-        alert2.accept()
-
-        self.assertEqual(self.marionette.find_element(By.ID, "text1").text, "foo")
-        self.assertEqual(self.marionette.find_element(By.ID, "text2").text, "bar")
-
 
 class TestModalAlerts(BaseAlertTestCase):
 
     def setUp(self):
         super(TestModalAlerts, self).setUp()
-        self.marionette.set_pref(
-            "network.auth.non-web-content-triggered-resources-http-auth-allow",
-            True)
+        self.marionette.set_pref("network.auth.non-web-content-triggered-resources-http-auth-allow",
+                                 True)
 
     def tearDown(self):
         # Ensure to close a possible remaining modal dialog
         self.close_all_windows()
-        self.marionette.clear_pref(
-            "network.auth.non-web-content-triggered-resources-http-auth-allow")
+        self.marionette.clear_pref("network.auth.non-web-content-triggered-resources-http-auth-allow")
 
         super(TestModalAlerts, self).tearDown()
 
     def test_http_auth_dismiss(self):
         self.marionette.navigate(self.marionette.absolute_url("http_auth"))
         self.wait_for_alert(timeout=self.marionette.timeout.page_load)
 
         alert = self.marionette.switch_to_alert()
         alert.dismiss()
 
+        self.wait_for_alert_closed()
+
         status = Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             element_present(By.ID, "status")
         )
         self.assertEqual(status.text, "restricted")
 
     def test_alert_opened_before_session_starts(self):
         self.marionette.navigate(self.marionette.absolute_url("http_auth"))
         self.wait_for_alert(timeout=self.marionette.timeout.page_load)
 
         # Restart the session to ensure we still find the formerly left-open dialog.
         self.marionette.delete_session()
         self.marionette.start_session()
 
         alert = self.marionette.switch_to_alert()
         alert.dismiss()
+
+        self.wait_for_alert_closed()
--- a/testing/marionette/harness/marionette_harness/www/test_tab_modal_dialogs.html
+++ b/testing/marionette/harness/marionette_harness/www/test_tab_modal_dialogs.html
@@ -2,42 +2,33 @@
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <!DOCTYPE html>
 <html>
 <head>
   <title>Dialog Test</title>
   <script type="text/javascript">
-    function setInnerText(id, value) {
-      document.getElementById(id).innerHTML = "<p>" + value + "</p>";
-    }
-
     function handleAlert () {
-      setInnerText("text", alert("Marionette alert"));
+      window.alert('Marionette alert');
     }
 
     function handleConfirm () {
-      setInnerText("text", confirm("Marionette confirm"));
+      var alertAccepted = window.confirm('Marionette confirm');
+      document.getElementById('confirm-result').innerHTML = alertAccepted;
     }
 
     function handlePrompt () {
-      setInnerText("text", prompt("Marionette prompt"));
-    }
-
-    function handleTwoDialogs() {
-      setInnerText("text1", prompt("First"));
-      setInnerText("text2", prompt("Second"));
+      var promptText = window.prompt('Marionette prompt');
+      document.getElementById('prompt-result').innerHTML = promptText === null ? 'null' : promptText;
     }
   </script>
 </head>
 <body>
-  <a href="#" id="tab-modal-alert" onclick="handleAlert()">Open an alert dialog.</a>
-  <a href="#" id="tab-modal-confirm" onclick="handleConfirm()">Open a confirm dialog.</a>
-  <a href="#" id="tab-modal-prompt" onclick="handlePrompt()">Open a prompt dialog.</a>
-  <a href="#" id="open-two-dialogs" onclick="handleTwoDialogs()">Open two prompts.</a>
-  <a href="#" id="click-handler" onclick="document.getElementById('text').innerHTML='result';">Make text appear.</a>
-
-  <div id="text"></div>
-  <div id="text1"></div>
-  <div id="text2"></div>
+   <a href="#" id="tab-modal-alert" onclick="handleAlert()">Open an alert dialog.</a>
+   <a href="#" id="tab-modal-confirm" onclick="handleConfirm()">Open a confirm dialog.</a>
+   <a href="#" id="tab-modal-prompt" onclick="handlePrompt()">Open a prompt dialog.</a>
+   <a href="#" id="click-handler" onclick="document.getElementById('click-result').innerHTML='result';">Make text appear.</a>
+   <div id="confirm-result"></div>
+   <div id="prompt-result"></div>
+   <div id="click-result"></div>
 </body>
 </html>
--- a/toolkit/components/reputationservice/ApplicationReputation.cpp
+++ b/toolkit/components/reputationservice/ApplicationReputation.cpp
@@ -551,16 +551,17 @@ static const char* const kBinaryFileExte
     ".mst", // Windows installer
     ".ndif", // Mac disk image
     //".ntfs", // 7z
     ".ocx", // ActiveX
     ".ops", // MS Office
     ".osas", // AppleScript
     ".osax", // AppleScript
     //".out", // Linux binary
+    ".oxt", // OpenOffice extension, can execute arbitrary code
     //".paf", // PortableApps package
     //".paq8f",
     //".paq8jd",
     //".paq8l",
     //".paq8o",
     ".partial", // Downloads
     ".pax", // Mac archive
     ".pcd", // Microsoft Visual Test
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -1773,25 +1773,36 @@ Engine.prototype = {
   _parseURL: function SRCH_ENG_parseURL(aElement) {
     var type     = aElement.getAttribute("type");
     // According to the spec, method is optional, defaulting to "GET" if not
     // specified
     var method   = aElement.getAttribute("method") || "GET";
     var template = aElement.getAttribute("template");
     var resultDomain = aElement.getAttribute("resultdomain");
 
+    let rels = [];
+    if (aElement.hasAttribute("rel")) {
+      rels = aElement.getAttribute("rel").toLowerCase().split(/\s+/);
+    }
+
+    // Support an alternate suggestion type, see bug 1425827 for details.
+    if (type == "application/json" && rels.includes("suggestions")) {
+      type = URLTYPE_SUGGEST_JSON;
+    }
+
     try {
       var url = new EngineURL(type, method, template, resultDomain);
     } catch (ex) {
       FAIL("_parseURL: failed to add " + template + " as a URL",
            Cr.NS_ERROR_FAILURE);
     }
 
-    if (aElement.hasAttribute("rel"))
-      url.rels = aElement.getAttribute("rel").toLowerCase().split(/\s+/);
+    if (rels.length) {
+      url.rels = rels;
+    }
 
     for (var i = 0; i < aElement.children.length; ++i) {
       var param = aElement.children[i];
       if (param.localName == "Param") {
         try {
           url.addParam(param.getAttribute("name"), param.getAttribute("value"));
         } catch (ex) {
           // Ignore failure
--- a/toolkit/components/search/tests/xpcshell/data/engineMaker.sjs
+++ b/toolkit/components/search/tests/xpcshell/data/engineMaker.sjs
@@ -28,25 +28,29 @@ function handleRequest(request, response
  */
 function createOpenSearchEngine(response, engineData) {
   let params = "", queryString = "";
   if (engineData.method == "POST") {
     params = "<Param name='q' value='{searchTerms}'/>";
   } else {
     queryString = "?q={searchTerms}";
   }
+  let type = "type='application/x-suggestions+json'";
+  if (engineData.alternativeJSONType) {
+    type = "type='application/json' rel='suggestions'";
+  }
 
-  let result = "<?xml version='1.0' encoding='utf-8'?>\
-<OpenSearchDescription xmlns='http://a9.com/-/spec/opensearch/1.1/'>\
-  <ShortName>" + engineData.name + "</ShortName>\
-  <Description>" + engineData.description + "</Description>\
-  <InputEncoding>UTF-8</InputEncoding>\
-  <LongName>" + engineData.name + "</LongName>\
-  <Url type='application/x-suggestions+json' method='" + engineData.method + "'\
-       template='" + engineData.baseURL + "searchSuggestions.sjs" + queryString + "'>\
-    " + params + "\
-  </Url>\
-  <Url type='text/html' method='" + engineData.method + "'\
-       template='" + engineData.baseURL + queryString + "'/>\
-</OpenSearchDescription>\
-";
+  let result = `<?xml version='1.0' encoding='utf-8'?>
+<OpenSearchDescription xmlns='http://a9.com/-/spec/opensearch/1.1/'>
+  <ShortName>${engineData.name}</ShortName>
+  <Description>${engineData.description}</Description>
+  <InputEncoding>UTF-8</InputEncoding>
+  <LongName>${engineData.name}</LongName>
+  <Url ${type} method='${engineData.method}'
+       template='${engineData.baseURL}searchSuggestions.sjs${queryString}'>
+    ${params}
+  </Url>
+  <Url type='text/html' method='${engineData.method}'
+       template='${engineData.baseURL}${queryString}'/>
+</OpenSearchDescription>
+`;
   response.write(result);
 }
--- a/toolkit/components/search/tests/xpcshell/test_searchSuggest.js
+++ b/toolkit/components/search/tests/xpcshell/test_searchSuggest.js
@@ -16,17 +16,17 @@ ChromeUtils.import("resource://gre/modul
 // We must make sure the FormHistoryStartup component is
 // initialized in order for it to respond to FormHistory
 // requests from nsFormAutoComplete.js.
 var formHistoryStartup = Cc["@mozilla.org/satchel/form-history-startup;1"].
                          getService(Ci.nsIObserver);
 formHistoryStartup.observe(null, "profile-after-change", null);
 
 var httpServer = new HttpServer();
-var getEngine, postEngine, unresolvableEngine;
+var getEngine, postEngine, unresolvableEngine, alternateJSONEngine;
 
 function run_test() {
   Services.prefs.setBoolPref("browser.search.suggest.enabled", true);
 
   let server = useHttpServer();
   server.registerContentType("sjs", "sjs");
 
   registerCleanupFunction(() => (async function cleanup() {
@@ -52,29 +52,40 @@ add_task(async function add_test_engines
   };
 
   let unresolvableEngineData = {
     baseURL: "http://example.invalid/",
     name: "Offline suggestion engine",
     method: "GET",
   };
 
-  [getEngine, postEngine, unresolvableEngine] = await addTestEngines([
+  let alternateJSONSuggestEngineData = {
+    baseURL: gDataUrl,
+    name: "Alternative JSON suggestion type",
+    method: "GET",
+    alternativeJSONType: true,
+  };
+
+  [getEngine, postEngine, unresolvableEngine, alternateJSONEngine] = await addTestEngines([
     {
       name: getEngineData.name,
       xmlFileName: "engineMaker.sjs?" + JSON.stringify(getEngineData),
     },
     {
       name: postEngineData.name,
       xmlFileName: "engineMaker.sjs?" + JSON.stringify(postEngineData),
     },
     {
       name: unresolvableEngineData.name,
       xmlFileName: "engineMaker.sjs?" + JSON.stringify(unresolvableEngineData),
     },
+    {
+      name: alternateJSONSuggestEngineData.name,
+      xmlFileName: "engineMaker.sjs?" + JSON.stringify(alternateJSONSuggestEngineData),
+    },
   ]);
 });
 
 
 // Begin tests
 
 add_task(async function simple_no_result_callback() {
   await new Promise(resolve => {
@@ -121,16 +132,27 @@ add_task(async function simple_remote_no
   Assert.equal(result.term, "mo");
   Assert.equal(result.local.length, 0);
   Assert.equal(result.remote.length, 3);
   Assert.equal(result.remote[0], "Mozilla");
   Assert.equal(result.remote[1], "modern");
   Assert.equal(result.remote[2], "mom");
 });
 
+add_task(async function simple_remote_no_local_result_alternative_type() {
+  let controller = new SearchSuggestionController();
+  let result = await controller.fetch("mo", false, alternateJSONEngine);
+  Assert.equal(result.term, "mo");
+  Assert.equal(result.local.length, 0);
+  Assert.equal(result.remote.length, 3);
+  Assert.equal(result.remote[0], "Mozilla");
+  Assert.equal(result.remote[1], "modern");
+  Assert.equal(result.remote[2], "mom");
+});
+
 add_task(async function remote_term_case_mismatch() {
   let controller = new SearchSuggestionController();
   let result = await controller.fetch("Query Case Mismatch", false, getEngine);
   Assert.equal(result.term, "Query Case Mismatch");
   Assert.equal(result.remote.length, 1);
   Assert.equal(result.remote[0], "Query Case Mismatch");
 });