Merge m-c to inbound, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 16 Dec 2016 16:52:46 -0800
changeset 450693 da15ea8d6fa5433e275cffe3ec820a74d413c938
parent 450692 98eaebf80768d80401260f12768d5bab4129961a (current diff)
parent 450591 34a1ab064cb5b868fa75cb74d052e978eb34d6c1 (diff)
child 450694 78aafdccfc4413551a35df0881978cc280fe93b1
push id38944
push userbmo:leftysolara@gmail.com
push dateSun, 18 Dec 2016 16:01:15 +0000
reviewersmerge
milestone53.0a1
Merge m-c to inbound, a=merge
--- a/browser/components/preferences/SiteDataManager.jsm
+++ b/browser/components/preferences/SiteDataManager.jsm
@@ -1,14 +1,15 @@
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "OfflineAppCacheHelper",
                                   "resource:///modules/offlineAppCache.jsm");
 
 this.EXPORTED_SYMBOLS = [
   "SiteDataManager"
 ];
 
@@ -172,10 +173,33 @@ this.SiteDataManager = {
     for (let site of this._sites.values()) {
       this._removePermission(site);
       this._removeQuotaUsage(site);
     }
     Services.cache2.clear();
     Services.cookies.removeAll();
     OfflineAppCacheHelper.clear();
     this.updateSites();
+  },
+
+  getSites() {
+    return Promise.all([this._updateQuotaPromise, this._updateDiskCachePromise])
+                  .then(() => {
+                    let list = [];
+                    for (let [origin, site] of this._sites) {
+                      let cache = null;
+                      let usage = site.quotaUsage;
+                      for (cache of site.appCacheList) {
+                        usage += cache.usage;
+                      }
+                      for (cache of site.diskCacheList) {
+                        usage += cache.dataSize;
+                      }
+                      list.push({
+                        usage,
+                        status: site.status,
+                        uri: NetUtil.newURI(origin)
+                      });
+                    }
+                    return list;
+                  });
   }
 };
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -55,16 +55,18 @@ var gAdvancedPane = {
     this.updateActualCacheSize();
     this.updateActualAppCacheSize();
 
     if (Services.prefs.getBoolPref("browser.storageManager.enabled")) {
       Services.obs.addObserver(this, "sitedatamanager:sites-updated", false);
       SiteDataManager.updateSites();
       setEventListener("clearSiteDataButton", "command",
                        gAdvancedPane.clearSiteData);
+      setEventListener("siteDataSettings", "command",
+                       gAdvancedPane.showSiteDataSettings);
     }
 
     setEventListener("layers.acceleration.disabled", "change",
                      gAdvancedPane.updateHardwareAcceleration);
     setEventListener("advancedPrefs", "select",
                      gAdvancedPane.tabSelectionChanged);
     if (AppConstants.MOZ_TELEMETRY_REPORTING) {
       setEventListener("submitHealthReportBox", "command",
@@ -334,16 +336,20 @@ var gAdvancedPane = {
   /**
    * Displays a dialog in which proxy settings may be changed.
    */
   showConnections: function()
   {
     gSubDialog.open("chrome://browser/content/preferences/connection.xul");
   },
 
+  showSiteDataSettings: function() {
+    gSubDialog.open("chrome://browser/content/preferences/siteDataSettings.xul");
+  },
+
   updateTotalSiteDataSize: function() {
     SiteDataManager.getTotalUsage()
       .then(usage => {
         let size = DownloadUtils.convertByteUnits(usage);
         let prefStrBundle = document.getElementById("bundlePreferences");
         let totalSiteDataSizeLabel = document.getElementById("totalSiteDataSize");
         totalSiteDataSizeLabel.textContent = prefStrBundle.getFormattedString("totalSiteDataSize", size);
         let siteDataGroup = document.getElementById("siteDataGroup");
--- a/browser/components/preferences/in-content/advanced.xul
+++ b/browser/components/preferences/in-content/advanced.xul
@@ -333,16 +333,21 @@
       <groupbox id="siteDataGroup" hidden="true">
         <caption><label>&siteData.label;</label></caption>
 
         <hbox align="center">
           <label id="totalSiteDataSize" flex="1"></label>
           <button id="clearSiteDataButton" icon="clear"
                   label="&clearSiteData.label;" accesskey="&clearSiteData.accesskey;"/>
         </hbox>
+        <vbox align="end">
+          <button id="siteDataSettings"
+                  label="&siteDataSettings.label;"
+                  accesskey="&siteDataSettings.accesskey;"/>
+        </vbox>
       </groupbox>
     </tabpanel>
 
     <!-- Update -->
     <tabpanel id="updatePanel" orient="vertical">
 #ifdef MOZ_UPDATER
       <groupbox id="updateApp" align="start">
         <caption><label>&updateApp.label;</label></caption>
--- a/browser/components/preferences/jar.mn
+++ b/browser/components/preferences/jar.mn
@@ -22,10 +22,14 @@ browser.jar:
     content/browser/preferences/permissions.xul
     content/browser/preferences/containers.xul
     content/browser/preferences/containers.js
     content/browser/preferences/permissions.js
     content/browser/preferences/sanitize.xul
     content/browser/preferences/sanitize.js
     content/browser/preferences/selectBookmark.xul
     content/browser/preferences/selectBookmark.js
+    content/browser/preferences/siteDataSettings.xul
+    content/browser/preferences/siteDataSettings.js
+    content/browser/preferences/siteDataSettings.css
+    content/browser/preferences/siteListItem.xml
     content/browser/preferences/translation.xul
     content/browser/preferences/translation.js
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/siteDataSettings.css
@@ -0,0 +1,15 @@
+/* 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/. */
+
+#sitesList {
+  min-height: 20em;
+}
+
+#sitesList > richlistitem {
+  -moz-binding: url("chrome://browser/content/preferences/siteListItem.xml#siteListItem");
+}
+
+.item-box {
+  padding: 5px 8px;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/siteDataSettings.js
@@ -0,0 +1,69 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
+/* 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/. */
+const { interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "SiteDataManager",
+                                  "resource:///modules/SiteDataManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
+                                  "resource://gre/modules/DownloadUtils.jsm");
+
+"use strict";
+
+let gSiteDataSettings = {
+
+  // Array of meatdata of sites. Each array element is object holding:
+  // - uri: uri of site; instance of nsIURI
+  // - status: persistent-storage permission status
+  // - usage: disk usage which site uses
+  _sites: null,
+
+  _list: null,
+
+  init() {
+    this._list = document.getElementById("sitesList");
+    SiteDataManager.getSites().then(sites => {
+      this._sites = sites;
+      this._sortSites(this._sites, "decending");
+      this._buildSitesList(this._sites);
+    });
+  },
+
+  /**
+   * Sort sites by usages
+   *
+   * @param sites {Array}
+   * @param order {String} indicate to sort in the "decending" or "ascending" order
+   */
+  _sortSites(sites, order) {
+    sites.sort((a, b) => {
+      if (order === "ascending") {
+        return a.usage - b.usage;
+      }
+      return b.usage - a.usage;
+    });
+  },
+
+  _buildSitesList(sites) {
+    // Clear old entries.
+    while (this._list.childNodes.length > 1) {
+      this._list.removeChild(this._list.lastChild);
+    }
+
+    let prefStrBundle = document.getElementById("bundlePreferences");
+    for (let data of sites) {
+      let statusStrId = data.status === Ci.nsIPermissionManager.ALLOW_ACTION ? "important" : "default";
+      let size = DownloadUtils.convertByteUnits(data.usage);
+      let item = document.createElement("richlistitem");
+      item.setAttribute("data-origin", data.uri.spec);
+      item.setAttribute("host", data.uri.host);
+      item.setAttribute("status", prefStrBundle.getString(statusStrId));
+      item.setAttribute("usage", prefStrBundle.getFormattedString("siteUsage", size));
+      this._list.appendChild(item);
+    }
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/siteDataSettings.xul
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/preferences/siteDataSettings.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/siteDataSettings.dtd" >
+
+<window id="SiteDataSettingsDialog" windowtype="Browser:SiteDataSettings"
+        class="windowDialog" title="&window.title;"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        style="width: 45em;"
+        onload="gSiteDataSettings.init();"
+        persist="screenX screenY width height">
+
+  <script src="chrome://browser/content/preferences/siteDataSettings.js"/>
+
+  <stringbundle id="bundlePreferences"
+                src="chrome://browser/locale/preferences/preferences.properties"/>
+
+  <vbox flex="1">
+    <description>&settings.description;</description>
+    <separator class="thin"/>
+
+    <richlistbox id="sitesList" orient="vertical" flex="1">
+      <listheader>
+        <treecol flex="4" width="50" label="&hostCol.label;"/>
+        <treecol flex="2" width="50" label="&statusCol.label;"/>
+        <treecol flex="1" width="50" label="&usageCol.label;"/>
+      </listheader>
+    </richlistbox>
+  </vbox>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/siteListItem.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+
+<!-- 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/. -->
+<!-- import-globals-from siteDataSettings.js -->
+
+<!DOCTYPE overlay [
+  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+  <!ENTITY % applicationsDTD SYSTEM "chrome://browser/locale/preferences/siteDataSettings.dtd">
+  %brandDTD;
+  %applicationsDTD;
+]>
+
+<bindings id="siteListItemBindings"
+          xmlns="http://www.mozilla.org/xbl"
+          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+          xmlns:xbl="http://www.mozilla.org/xbl">
+
+  <binding id="siteListItem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+    <content>
+      <xul:hbox flex="1">
+        <xul:hbox flex="4" width="50" class="item-box" align="center" xbl:inherits="tooltiptext=host">
+          <xul:label flex="1" crop="end" xbl:inherits="value=host"/>
+        </xul:hbox>
+        <xul:hbox flex="2" width="50" class="item-box" align="center" xbl:inherits="tooltiptext=status">
+          <xul:label flex="1" crop="end" xbl:inherits="value=status"/>
+        </xul:hbox>
+        <xul:hbox flex="1" width="50" class="item-box" align="center" xbl:inherits="tooltiptext=usage">
+          <xul:label flex="1" crop="end" xbl:inherits="value=usage"/>
+        </xul:hbox>
+      </xul:hbox>
+    </content>
+  </binding>
+
+</bindings>
--- a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
@@ -56,16 +56,18 @@
 <!ENTITY httpCache.label                 "Cached Web Content">
 
 <!ENTITY offlineStorage2.label           "Offline Web Content and User Data">
 
 <!--  Site Data section manages sites using Storage API and is under Network -->
 <!ENTITY siteData.label                  "Site Data">
 <!ENTITY clearSiteData.label             "Clear All Data">
 <!ENTITY clearSiteData.accesskey         "l">
+<!ENTITY siteDataSettings.label          "Settings…">
+<!ENTITY siteDataSettings.accesskey      "i">
 
 <!-- LOCALIZATION NOTE:
   The entities limitCacheSizeBefore.label and limitCacheSizeAfter.label appear on a single
   line in preferences as follows:
 
   &limitCacheSizeBefore.label [textbox for cache size in MB] &limitCacheSizeAfter.label;
 -->
 <!ENTITY limitCacheSizeBefore.label      "Limit cache to">
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -168,16 +168,19 @@ actualAppCacheSize=Your application cach
 #LOCALIZATION NOTE: The next string is for the total usage of site data.
 #   e.g., "The total usage is currently using 200 MB"
 #   %1$S = size
 #   %2$S = unit (MB, KB, etc.)
 totalSiteDataSize=Your stored site data is currently using %1$S %2$S of disk space
 clearSiteDataPromptTitle=Clear all cookies and site data
 clearSiteDataPromptText=Selecting ‘Clear Now’ will clear all cookies and site data stored by Firefox. This may sign you out of websites and remove offline web content.
 clearSiteDataNow=Clear Now
+important=Important
+default=Default
+siteUsage=%1$S %2$S
 
 syncUnlink.title=Do you want to unlink your device?
 syncUnlink.label=This device will no longer be associated with your Sync account. All of your personal data, both on this device and in your Sync account, will remain intact.
 syncUnlinkConfirm.label=Unlink
 
 # LOCALIZATION NOTE (featureEnableRequiresRestart, featureDisableRequiresRestart, restartTitle): %S = brandShortName
 featureEnableRequiresRestart=%S must restart to enable this feature.
 featureDisableRequiresRestart=%S must restart to disable this feature.
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/preferences/siteDataSettings.dtd
@@ -0,0 +1,9 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY     window.title                  "Settings - Site Data">
+<!ENTITY     settings.description          "The following websites asked to store site data in your disk. You can specify which websites are allowed to store site data. Default site data is temporary and could be deleted automatically.">
+<!ENTITY     hostCol.label                 "Site">
+<!ENTITY     statusCol.label               "Status">
+<!ENTITY     usageCol.label                "Storage">
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -77,16 +77,17 @@
     locale/browser/preferences/preferences.properties (%chrome/browser/preferences/preferences.properties)
     locale/browser/preferences/containers.properties  (%chrome/browser/preferences/containers.properties)
     locale/browser/preferences/privacy.dtd            (%chrome/browser/preferences/privacy.dtd)
     locale/browser/preferences/security.dtd           (%chrome/browser/preferences/security.dtd)
     locale/browser/preferences/containers.dtd         (%chrome/browser/preferences/containers.dtd)
     locale/browser/preferences/sync.dtd               (%chrome/browser/preferences/sync.dtd)
     locale/browser/preferences/tabs.dtd               (%chrome/browser/preferences/tabs.dtd)
     locale/browser/preferences/search.dtd             (%chrome/browser/preferences/search.dtd)
+    locale/browser/preferences/siteDataSettings.dtd   (%chrome/browser/preferences/siteDataSettings.dtd)
     locale/browser/preferences/translation.dtd        (%chrome/browser/preferences/translation.dtd)
     locale/browser/syncBrand.dtd                (%chrome/browser/syncBrand.dtd)
     locale/browser/syncSetup.dtd                (%chrome/browser/syncSetup.dtd)
     locale/browser/syncSetup.properties         (%chrome/browser/syncSetup.properties)
     locale/browser/syncGenericChange.properties         (%chrome/browser/syncGenericChange.properties)
     locale/browser/syncKey.dtd                  (%chrome/browser/syncKey.dtd)
     locale/browser/syncQuota.dtd                (%chrome/browser/syncQuota.dtd)
     locale/browser/syncQuota.properties         (%chrome/browser/syncQuota.properties)
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -215,16 +215,20 @@ private:
 
   // Ensure that the backend buffer can hold aLength data. Will update mData.
   // Will enforce that the start of allocated data is always Alignment bytes
   // aligned and that it has sufficient end padding to allow for Alignment bytes
   // block read as required by some data decoders.
   // Returns false if memory couldn't be allocated.
   bool EnsureCapacity(size_t aLength)
   {
+    if (!aLength) {
+      // No need to allocate a buffer yet.
+      return true;
+    }
     const CheckedInt<size_t> sizeNeeded =
       CheckedInt<size_t>(aLength) * sizeof(Type) + AlignmentPaddingSize();
 
     if (!sizeNeeded.isValid() || sizeNeeded.value() >= INT32_MAX) {
       // overflow or over an acceptable size.
       return false;
     }
     if (mData && mCapacity >= sizeNeeded.value()) {
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -95,17 +95,17 @@ TrackBuffersManager::TrackBuffersManager
   , mParser(ContainerParser::CreateForMIMEType(aType))
   , mProcessedInput(0)
   , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
   , mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(aParentDecoder, false /* strict */))
   , mEnded(false)
   , mVideoEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.video",
                                                  100 * 1024 * 1024))
   , mAudioEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.audio",
-                                                 10 * 1024 * 1024))
+                                                 12 * 1024 * 1024))
   , mEvictionState(EvictionState::NO_EVICTION_NEEDED)
   , mMonitor("TrackBuffersManager")
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread");
 }
 
 TrackBuffersManager::~TrackBuffersManager()
 {
@@ -268,20 +268,20 @@ TrackBuffersManager::EvictData(const Tim
     // We're adding more data than we can hold.
     return EvictDataResult::BUFFER_FULL;
   }
   const int64_t toEvict = GetSize() + aSize - EvictionThreshold();
 
   const uint32_t canEvict =
     Evictable(HasVideo() ? TrackInfo::kVideoTrack : TrackInfo::kAudioTrack);
 
-  MSE_DEBUG(
-    "buffered=%lldkB, eviction threshold=%ukB, evict=%lldkB canevict=%ukB",
-    GetSize() / 1024, EvictionThreshold() / 1024, toEvict / 1024,
-    canEvict / 1024);
+  MSE_DEBUG("currentTime=%lld buffered=%lldkB, eviction threshold=%ukB, "
+            "evict=%lldkB canevict=%ukB",
+            aPlaybackTime.ToMicroseconds(), GetSize() / 1024,
+            EvictionThreshold() / 1024, toEvict / 1024, canEvict / 1024);
 
   if (toEvict <= 0) {
     mEvictionState = EvictionState::NO_EVICTION_NEEDED;
     return EvictDataResult::NO_DATA_EVICTED;
   }
 
   EvictDataResult result;
 
@@ -290,17 +290,19 @@ TrackBuffersManager::EvictData(const Tim
     // Our buffer is currently full. We will make another eviction attempt.
     // However, the current appendBuffer will fail as we can't know ahead of
     // time if the eviction will later succeed.
     result = EvictDataResult::BUFFER_FULL;
   } else {
     mEvictionState = EvictionState::EVICTION_NEEDED;
     result = EvictDataResult::NO_DATA_EVICTED;
   }
-  MSE_DEBUG("Reached our size limit, schedule eviction of %lld bytes", toEvict);
+  MSE_DEBUG(
+    "Reached our size limit, schedule eviction of %lld bytes (%s)", toEvict,
+    result == EvictDataResult::BUFFER_FULL ? "buffer full" : "no data evicted");
   QueueTask(new EvictDataTask(aPlaybackTime, toEvict));
 
   return result;
 }
 
 TimeIntervals
 TrackBuffersManager::Buffered() const
 {
@@ -437,17 +439,17 @@ TrackBuffersManager::DoEvictData(const T
     if (frame->mKeyframe) {
       lastKeyFrameIndex = i;
       toEvict -= partialEvict;
       if (toEvict < 0) {
         break;
       }
       partialEvict = 0;
     }
-    if (frame->mTime >= lowerLimit.ToMicroseconds()) {
+    if (frame->GetEndTime() >= lowerLimit.ToMicroseconds()) {
       break;
     }
     partialEvict += frame->ComputedSizeOfIncludingThis();
   }
 
   const int64_t finalSize = mSizeSourceBuffer - aSizeToEvict;
 
   if (lastKeyFrameIndex > 0) {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3168,33 +3168,32 @@ nsLayoutUtils::GetFramesForArea(nsIFrame
     if (rootScrollFrame) {
       builder.SetIgnoreScrollFrame(rootScrollFrame);
     }
   }
   if (aFlags & IGNORE_CROSS_DOC) {
     builder.SetDescendIntoSubdocuments(false);
   }
 
-  builder.SetHitTestShouldStopAtFirstOpaque(aFlags & ONLY_VISIBLE);
-
   builder.EnterPresShell(aFrame);
   aFrame->BuildDisplayListForStackingContext(&builder, aRect, &list);
   builder.LeavePresShell(aFrame, nullptr);
 
 #ifdef MOZ_DUMP_PAINTING
   if (gDumpEventList) {
     fprintf_stderr(stderr, "Event handling --- (%d,%d):\n", aRect.x, aRect.y);
 
     std::stringstream ss;
     nsFrame::PrintDisplayList(&builder, list, ss);
     print_stderr(ss);
   }
 #endif
 
   nsDisplayItem::HitTestState hitTestState;
+  builder.SetHitTestShouldStopAtFirstOpaque(aFlags & ONLY_VISIBLE);
   list.HitTest(&builder, aRect, &hitTestState, &aOutFrames);
   list.DeleteAll();
   return NS_OK;
 }
 
 // aScrollFrameAsScrollable must be non-nullptr and queryable to an nsIFrame
 FrameMetrics
 nsLayoutUtils::CalculateBasicFrameMetrics(nsIScrollableFrame* aScrollFrame) {
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -820,17 +820,17 @@ public:
   static nsIFrame* GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt,
                                     uint32_t aFlags = 0);
 
   /**
    * Given aFrame, the root frame of a stacking context, find all descendant
    * frames under the area of a rectangle that receives a mouse event,
    * or nullptr if there is no such frame.
    * @param aRect the rect, relative to the frame origin
-   * @param aOutFrames an array to append all the frames found
+   * @param aOutFrames an array to add all the frames found
    * @param aFlags some combination of FrameForPointFlags
    */
   static nsresult GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
                                    nsTArray<nsIFrame*> &aOutFrames,
                                    uint32_t aFlags = 0);
 
   /**
    * Transform aRect relative to aFrame up to the coordinate system of
--- a/layout/forms/nsHTMLButtonControlFrame.cpp
+++ b/layout/forms/nsHTMLButtonControlFrame.cpp
@@ -114,17 +114,17 @@ nsHTMLButtonControlFrame::BuildDisplayLi
   nsDisplayList onTop;
   if (IsVisibleForPainting(aBuilder)) {
     mRenderer.DisplayButton(aBuilder, aLists.BorderBackground(), &onTop);
   }
 
   nsDisplayListCollection set;
 
   // Do not allow the child subtree to receive events.
-  if (!isForEventDelivery || aBuilder->HitTestShouldStopAtFirstOpaque()) {
+  if (!isForEventDelivery) {
     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
     if (ShouldClipPaintingToBorderBox()) {
       nsMargin border = StyleBorder()->GetComputedBorder();
       nsRect rect(aBuilder->ToReferenceFrame(this), GetSize());
       rect.Deflate(border);
       nscoord radii[8];
       bool hasRadii = GetPaddingBoxBorderRadii(radii);
--- a/mobile/android/base/resources/layout/search_engine_row.xml
+++ b/mobile/android/base/resources/layout/search_engine_row.xml
@@ -4,27 +4,31 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
     <org.mozilla.gecko.widget.FaviconView android:id="@+id/suggestion_icon"
                                           android:layout_width="@dimen/favicon_bg"
                                           android:layout_height="@dimen/favicon_bg"
                                           android:layout_marginLeft="16dip"
+                                          android:layout_marginStart="16dip"
                                           android:layout_marginRight="16dip"
+                                          android:layout_marginEnd="16dip"
                                           android:layout_centerVertical="true"
                                           android:minWidth="@dimen/favicon_bg"
                                           android:minHeight="@dimen/favicon_bg"/>
 
     <org.mozilla.gecko.widget.FlowLayout android:id="@+id/suggestion_layout"
                                          android:layout_toRightOf="@id/suggestion_icon"
+                                         android:layout_toEndOf="@id/suggestion_icon"
                                          android:layout_centerVertical="true"
                                          android:layout_width="wrap_content"
                                          android:layout_height="wrap_content"
                                          android:layout_marginRight="15dp"
+                                         android:layout_marginEnd="15dp"
                                          android:duplicateParentState="true">
 
         <include layout="@layout/suggestion_item"
                  android:id="@+id/suggestion_user_entered"/>
 
     </org.mozilla.gecko.widget.FlowLayout>
 
 </merge>
--- a/mobile/android/tests/browser/robocop/testReadingListCache.js
+++ b/mobile/android/tests/browser/robocop/testReadingListCache.js
@@ -26,17 +26,17 @@ var TEST_PAGES = [
   },
   {
     url: URL_PREFIX + "not_an_article.html",
     expected: null
   },
   {
     url: URL_PREFIX + "developer.mozilla.org/en/XULRunner/Build_Instructions.html",
     expected: {
-      title: "Building XULRunner",
+      title: "Building XULRunner | MDN",
       byline: null,
       excerpt: "XULRunner is built using basically the same process as Firefox or other applications. Please read and follow the general Build Documentation for instructions on how to get sources and set up build prerequisites.",
     }
   },
 ];
 
 add_task(function* test_article_not_found() {
   let article = yield ReaderMode.getArticleFromCache(TEST_PAGES[0].url);
--- a/toolkit/components/reader/Readability.js
+++ b/toolkit/components/reader/Readability.js
@@ -114,17 +114,17 @@ Readability.prototype = {
   DEFAULT_MAX_PAGES: 5,
 
   // Element tags to score by default.
   DEFAULT_TAGS_TO_SCORE: "section,h2,h3,h4,h5,h6,p,td,pre".toUpperCase().split(","),
 
   // All of the regular expressions in use within readability.
   // Defined up here so we don't instantiate them repeatedly in loops.
   REGEXPS: {
-    unlikelyCandidates: /banner|combx|comment|community|disqus|extra|foot|header|legends|menu|modal|related|remark|rss|shoutbox|sidebar|skyscraper|sponsor|ad-break|agegate|pagination|pager|popup/i,
+    unlikelyCandidates: /banner|combx|comment|community|cover-wrap|disqus|extra|foot|header|legends|menu|modal|related|remark|rss|shoutbox|sidebar|skyscraper|sponsor|ad-break|agegate|pagination|pager|popup|yom-remote/i,
     okMaybeItsACandidate: /and|article|body|column|main|shadow/i,
     positive: /article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story/i,
     negative: /hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|foot|footer|footnote|masthead|media|meta|modal|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget/i,
     extraneous: /print|archive|comment|discuss|e[\-]?mail|share|reply|all|login|sign|single|utility/i,
     byline: /byline|author|dateline|writtenby|p-author/i,
     replaceFonts: /<(\/?)font[^>]*>/gi,
     normalize: /\s{2,}/g,
     videos: /\/\/(www\.)?(dailymotion|youtube|youtube-nocookie|player\.vimeo)\.com/i,
@@ -150,45 +150,58 @@ Readability.prototype = {
   },
 
   /**
    * Iterates over a NodeList, calls `filterFn` for each node and removes node
    * if function returned `true`.
    *
    * If function is not passed, removes all the nodes in node list.
    *
-   * @param NodeList nodeList The no
-   * @param Function filterFn
+   * @param NodeList nodeList The nodes to operate on
+   * @param Function filterFn the function to use as a filter
    * @return void
    */
   _removeNodes: function(nodeList, filterFn) {
     for (var i = nodeList.length - 1; i >= 0; i--) {
       var node = nodeList[i];
       var parentNode = node.parentNode;
       if (parentNode) {
         if (!filterFn || filterFn.call(this, node, i, nodeList)) {
           parentNode.removeChild(node);
         }
       }
     }
   },
 
   /**
+   * Iterates over a NodeList, and calls _setNodeTag for each node.
+   *
+   * @param NodeList nodeList The nodes to operate on
+   * @param String newTagName the new tag name to use
+   * @return void
+   */
+  _replaceNodeTags: function(nodeList, newTagName) {
+    for (var i = nodeList.length - 1; i >= 0; i--) {
+      var node = nodeList[i];
+      this._setNodeTag(node, newTagName);
+    }
+  },
+
+  /**
    * Iterate over a NodeList, which doesn't natively fully implement the Array
    * interface.
    *
    * For convenience, the current object context is applied to the provided
    * iterate function.
    *
    * @param  NodeList nodeList The NodeList.
    * @param  Function fn       The iterate function.
-   * @param  Boolean  backward Whether to use backward iteration.
    * @return void
    */
-  _forEachNode: function(nodeList, fn, backward) {
+  _forEachNode: function(nodeList, fn) {
     Array.prototype.forEach.call(nodeList, fn, this);
   },
 
   /**
    * Iterate over a NodeList, return true if any of the provided iterate
    * function calls returns true, false otherwise.
    *
    * For convenience, the current object context is applied to the
@@ -357,19 +370,17 @@ Readability.prototype = {
 
     // Remove all style tags in head
     this._removeNodes(doc.getElementsByTagName("style"));
 
     if (doc.body) {
       this._replaceBrs(doc.body);
     }
 
-    this._forEachNode(doc.getElementsByTagName("font"), function(fontNode) {
-      this._setNodeTag(fontNode, "SPAN");
-    });
+    this._replaceNodeTags(doc.getElementsByTagName("font"), "SPAN");
   },
 
   /**
    * Finds the next element, starting from the given node, and ignoring
    * whitespace in between. If the given node is an element, the same node is
    * returned.
    */
   _nextElement: function (node) {
@@ -1057,22 +1068,25 @@ Readability.prototype = {
     } else if ("og:description" in values) {
       // Use facebook open graph description.
       metadata.excerpt = values["og:description"];
     } else if ("twitter:description" in values) {
       // Use twitter cards description.
       metadata.excerpt = values["twitter:description"];
     }
 
-    if ("og:title" in values) {
-      // Use facebook open graph title.
-      metadata.title = values["og:title"];
-    } else if ("twitter:title" in values) {
-      // Use twitter cards title.
-      metadata.title = values["twitter:title"];
+    metadata.title = this._getArticleTitle();
+    if (!metadata.title) {
+      if ("og:title" in values) {
+        // Use facebook open graph title.
+        metadata.title = values["og:title"];
+      } else if ("twitter:title" in values) {
+        // Use twitter cards title.
+        metadata.title = values["twitter:title"];
+      }
     }
 
     return metadata;
   },
 
   /**
    * Removes script tags from the document.
    *
@@ -1852,17 +1866,17 @@ Readability.prototype = {
     // this._parsedPages[uri.spec.replace(/\/$/, '')] = true;
 
     // Pull out any possible next page link first.
     // var nextPageLink = this._findNextPageLink(doc.body);
 
     this._prepDocument();
 
     var metadata = this._getArticleMetadata();
-    var articleTitle = metadata.title || this._getArticleTitle();
+    var articleTitle = metadata.title;
 
     var articleContent = this._grabArticle();
     if (!articleContent)
       return null;
 
     this.log("Grabbed: " + articleContent.innerHTML);
 
     this._postProcessContent(articleContent);
--- a/toolkit/components/typeaheadfind/nsITypeAheadFind.idl
+++ b/toolkit/components/typeaheadfind/nsITypeAheadFind.idl
@@ -13,17 +13,17 @@
 /******************************** Declarations *******************************/
 
 interface mozIDOMWindow;
 interface nsIDocShell;
 
 
 /****************************** nsTypeAheadFind ******************************/
 
-[scriptable, uuid(3cfe7906-f189-45a0-8abe-8e4437a23cae)]
+[scriptable, uuid(ae501e28-c57f-4692-ac74-410e1bed98b7)]
 interface nsITypeAheadFind : nsISupports
 {
   /****************************** Initializer ******************************/
 
   /* Necessary initialization that can't happen in the constructor, either
    * because function calls here may fail, or because the docShell is
    * required. */
   void init(in nsIDocShell aDocShell);
@@ -53,17 +53,17 @@ interface nsITypeAheadFind : nsISupports
   void setSelectionModeAndRepaint(in short toggle);
 
   /* Collapse the "found match" selection to its start.  Because not all
    * matches are owned by the same selection controller, this doesn't
    * necessarily happen automatically. */
   void collapseSelection();
 
   /* Check if a range is visible */
-  boolean isRangeVisible(in nsIDOMRange aRange, in boolean aFlushLayout);
+  boolean isRangeVisible(in nsIDOMRange aRange, in boolean aMustBeInViewPort);
 
   /******************************* Attributes ******************************/
 
   readonly attribute AString searchString;
                                         // Most recent search string
   attribute boolean caseSensitive;      // Searches are case sensitive
   attribute boolean entireWord;         // Search for whole words only
   readonly attribute nsIDOMElement foundLink;
--- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
+++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
@@ -47,17 +47,16 @@
 #include "nsNameSpaceManager.h"
 #include "nsIWindowWatcher.h"
 #include "nsIObserverService.h"
 #include "nsFocusManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Link.h"
 #include "nsRange.h"
 #include "nsXBLBinding.h"
-#include "nsLayoutUtils.h"
 
 #include "nsTypeAheadFind.h"
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTypeAheadFind)
   NS_INTERFACE_MAP_ENTRY(nsITypeAheadFind)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITypeAheadFind)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
@@ -66,16 +65,18 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTypeAheadFind)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTypeAheadFind)
 
 NS_IMPL_CYCLE_COLLECTION(nsTypeAheadFind, mFoundLink, mFoundEditable,
                          mCurrentWindow, mStartFindRange, mSearchRange,
                          mStartPointRange, mEndPointRange, mSoundInterface,
                          mFind, mFoundRange)
 
+static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
+
 #define NS_FIND_CONTRACTID "@mozilla.org/embedcomp/rangefind;1"
 
 nsTypeAheadFind::nsTypeAheadFind():
   mStartLinksOnlyPref(false),
   mCaretBrowsingOn(false),
   mDidAddObservers(false),
   mLastFindLength(0),
   mIsSoundInitialized(false),
@@ -416,31 +417,32 @@ nsTypeAheadFind::FindItNow(nsIPresShell 
     return NS_ERROR_FAILURE;
 
   mFind->SetFindBackwards(aFindPrev);
 
   while (true) {    // ----- Outer while loop: go through all docs -----
     while (true) {  // === Inner while loop: go through a single doc ===
       mFind->Find(mTypeAheadBuffer.get(), mSearchRange, mStartPointRange,
                   mEndPointRange, getter_AddRefs(returnRange));
-
+      
       if (!returnRange)
         break;  // Nothing found in this doc, go to outer loop (try next doc)
 
       // ------- Test resulting found range for success conditions ------
       bool isInsideLink = false, isStartingLink = false;
 
       if (aIsLinksOnly) {
         // Don't check if inside link when searching all text
         RangeStartsInsideLink(returnRange, presShell, &isInsideLink,
                               &isStartingLink);
       }
 
       bool usesIndependentSelection;
-      if (!IsRangeVisible(presShell, presContext, returnRange, true,
+      if (!IsRangeVisible(presShell, presContext, returnRange,
+                          aIsFirstVisiblePreferred, false,
                           getter_AddRefs(mStartPointRange), 
                           &usesIndependentSelection) ||
           (aIsLinksOnly && !isInsideLink) ||
           (mStartLinksOnlyPref && aIsLinksOnly && !isStartingLink)) {
         // ------ Failure ------
         // At this point mStartPointRange got updated to the first
         // visible range in the viewport.  We _may_ be able to just
         // start there, if it's not taking us in the wrong direction.
@@ -810,17 +812,18 @@ nsTypeAheadFind::GetSearchContainers(nsI
     if (selection)
       selection->GetRangeAt(0, getter_AddRefs(currentSelectionRange));
   }
 
   if (!currentSelectionRange) {
     // Ensure visible range, move forward if necessary
     // This uses ignores the return value, but usese the side effect of
     // IsRangeVisible. It returns the first visible range after searchRange
-    IsRangeVisible(presShell, presContext, mSearchRange, true,
+    IsRangeVisible(presShell, presContext, mSearchRange, 
+                   aIsFirstVisiblePreferred, true, 
                    getter_AddRefs(mStartPointRange), nullptr);
   }
   else {
     int32_t startOffset;
     nsCOMPtr<nsIDOMNode> startNode;
     if (aFindPrev) {
       currentSelectionRange->GetStartContainer(getter_AddRefs(startNode));
       currentSelectionRange->GetStartOffset(&startOffset);
@@ -1000,28 +1003,28 @@ nsTypeAheadFind::Find(const nsAString& a
     // is typed
     mStartFindRange = nullptr;
     mSelectionController = nullptr;
 
     *aResult = FIND_FOUND;
     return NS_OK;
   }
 
-  bool atEnd = false;
+  bool atEnd = false;    
   if (mTypeAheadBuffer.Length()) {
     const nsAString& oldStr = Substring(mTypeAheadBuffer, 0, mTypeAheadBuffer.Length());
     const nsAString& newStr = Substring(aSearchString, 0, mTypeAheadBuffer.Length());
     if (oldStr.Equals(newStr))
       atEnd = true;
-
+  
     const nsAString& newStr2 = Substring(aSearchString, 0, aSearchString.Length());
     const nsAString& oldStr2 = Substring(mTypeAheadBuffer, 0, aSearchString.Length());
     if (oldStr2.Equals(newStr2))
       atEnd = true;
-
+    
     if (!atEnd)
       mStartFindRange = nullptr;
   }
 
   if (!mIsSoundInitialized && !mNotFoundSoundURL.IsEmpty()) {
     // This makes sure system sound library is loaded so that
     // there's no lag before the first sound is played
     // by waiting for the first keystroke, we still get the startup time benefits.
@@ -1153,122 +1156,160 @@ nsTypeAheadFind::GetFoundRange(nsIDOMRan
   }
 
   mFoundRange->CloneRange(aFoundRange);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsTypeAheadFind::IsRangeVisible(nsIDOMRange *aRange,
-                                bool aFlushLayout,
+                                bool aMustBeInViewPort,
                                 bool *aResult)
 {
   // Jump through hoops to extract the docShell from the range.
   nsCOMPtr<nsIDOMNode> node;
   aRange->GetStartContainer(getter_AddRefs(node));
   nsCOMPtr<nsIDOMDocument> document;
   node->GetOwnerDocument(getter_AddRefs(document));
   nsCOMPtr<mozIDOMWindowProxy> window;
   document->GetDefaultView(getter_AddRefs(window));
   nsCOMPtr<nsIWebNavigation> navNav (do_GetInterface(window));
   nsCOMPtr<nsIDocShell> docShell (do_GetInterface(navNav));
 
   // Set up the arguments needed to check if a range is visible.
   nsCOMPtr<nsIPresShell> presShell (docShell->GetPresShell());
   RefPtr<nsPresContext> presContext = presShell->GetPresContext();
   nsCOMPtr<nsIDOMRange> startPointRange = new nsRange(presShell->GetDocument());
-  *aResult = IsRangeVisible(presShell, presContext, aRange, aFlushLayout,
+  *aResult = IsRangeVisible(presShell, presContext, aRange,
+                            aMustBeInViewPort, false,
                             getter_AddRefs(startPointRange),
                             nullptr);
   return NS_OK;
 }
 
 bool
 nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
                                 nsPresContext *aPresContext,
-                                nsIDOMRange *aRange,
-                                bool aFlushLayout,
+                                nsIDOMRange *aRange, bool aMustBeInViewPort,
+                                bool aGetTopVisibleLeaf,
                                 nsIDOMRange **aFirstVisibleRange,
                                 bool *aUsesIndependentSelection)
 {
   NS_ASSERTION(aPresShell && aPresContext && aRange && aFirstVisibleRange, 
                "params are invalid");
 
-  // We need to know if the range start and end are both visible.
-  // In all cases, return the first visible range in aFirstVisibleRange.
-  aRange->CloneRange(aFirstVisibleRange);
+  // We need to know if the range start is visible.
+  // Otherwise, return the first visible range start 
+  // in aFirstVisibleRange
 
-  if (aFlushLayout) {
-    aPresShell->FlushPendingNotifications(Flush_Layout);
-  }
-
+  aRange->CloneRange(aFirstVisibleRange);
   nsCOMPtr<nsIDOMNode> node;
-  aRange->GetCommonAncestorContainer(getter_AddRefs(node));
+  aRange->GetStartContainer(getter_AddRefs(node));
 
   nsCOMPtr<nsIContent> content(do_QueryInterface(node));
-  if (!content) {
+  if (!content)
     return false;
-  }
 
   nsIFrame *frame = content->GetPrimaryFrame();
-  if (!frame) {
+  if (!frame)    
     return false;  // No frame! Not visible then.
-  }
-
-  // Having a primary frame doesn't mean that the range is visible inside the
-  // viewport. Do a hit-test to determine that quickly and properly.
-  bool foundContent = false;
-  AutoTArray<nsIFrame*,8> frames;
-  nsIFrame *rootFrame = aPresShell->GetRootFrame();
-  RefPtr<nsRange> range = static_cast<nsRange*>(aRange);
-  RefPtr<mozilla::dom::DOMRectList> rects = range->GetClientRects(true, false);
-  for (uint32_t i = 0; i < rects->Length(); ++i) {
-    RefPtr<mozilla::dom::DOMRect> rect = rects->Item(i);
-    nsRect r(nsPresContext::CSSPixelsToAppUnits((float)rect->X()),
-             nsPresContext::CSSPixelsToAppUnits((float)rect->Y()),
-             nsPresContext::CSSPixelsToAppUnits((float)rect->Width()),
-             nsPresContext::CSSPixelsToAppUnits((float)rect->Height()));
-    // Append visible frames to frames array.
-    nsLayoutUtils::GetFramesForArea(rootFrame, r, frames,
-      nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
-      nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME |
-      nsLayoutUtils::ONLY_VISIBLE);
 
-    // See if any of the frames contain the content. If they do, then the range
-    // is visible. We search for the content rather than the original frame,
-    // because nsTextContinuation frames might be returned instead of the
-    // original frame.
-    for (const auto &f: frames) {
-      if (f->GetContent() == content) {
-        foundContent = true;
-        break;
-      }
-    }
-
-    if (foundContent) {
-      break;
-    }
-
-    frames.ClearAndRetainStorage();
-  }
-
-  // Test that content appears in the list of framesForArea. If it does, then
-  // that means that content is at least partly visible.
-  if (!foundContent) {
+  if (!frame->StyleVisibility()->IsVisible())
     return false;
-  }
 
   // Detect if we are _inside_ a text control, or something else with its own
   // selection controller.
   if (aUsesIndependentSelection) {
     *aUsesIndependentSelection = 
       (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
   }
 
-  return true;
+  // ---- We have a frame ----
+  if (!aMustBeInViewPort)   
+    return true; //  Don't need it to be on screen, just in rendering tree
+
+  // Get the next in flow frame that contains the range start
+  int32_t startRangeOffset, startFrameOffset, endFrameOffset;
+  aRange->GetStartOffset(&startRangeOffset);
+  while (true) {
+    frame->GetOffsets(startFrameOffset, endFrameOffset);
+    if (startRangeOffset < endFrameOffset)
+      break;
+
+    nsIFrame *nextContinuationFrame = frame->GetNextContinuation();
+    if (nextContinuationFrame)
+      frame = nextContinuationFrame;
+    else
+      break;
+  }
+
+  // Set up the variables we need, return true if we can't get at them all
+  const uint16_t kMinPixels  = 12;
+  nscoord minDistance = nsPresContext::CSSPixelsToAppUnits(kMinPixels);
+
+  // Get the bounds of the current frame, relative to the current view.
+  // We don't use the more accurate AccGetBounds, because that is
+  // more expensive and the STATE_OFFSCREEN flag that this is used
+  // for only needs to be a rough indicator
+  nsRectVisibility rectVisibility = nsRectVisibility_kAboveViewport;
+
+  if (!aGetTopVisibleLeaf && !frame->GetRect().IsEmpty()) {
+    rectVisibility =
+      aPresShell->GetRectVisibility(frame,
+                                    nsRect(nsPoint(0,0), frame->GetSize()),
+                                    minDistance);
+
+    if (rectVisibility != nsRectVisibility_kAboveViewport) {
+      return true;
+    }
+  }
+
+  // We know that the target range isn't usable because it's not in the
+  // view port. Move range forward to first visible point,
+  // this speeds us up a lot in long documents
+  nsCOMPtr<nsIFrameEnumerator> frameTraversal;
+  nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID));
+  if (trav)
+    trav->NewFrameTraversal(getter_AddRefs(frameTraversal),
+                            aPresContext, frame,
+                            eLeaf,
+                            false, // aVisual
+                            false, // aLockInScrollView
+                            false, // aFollowOOFs
+                            false  // aSkipPopupChecks
+                            );
+
+  if (!frameTraversal)
+    return false;
+
+  while (rectVisibility == nsRectVisibility_kAboveViewport) {
+    frameTraversal->Next();
+    frame = frameTraversal->CurrentItem();
+    if (!frame)
+      return false;
+
+    if (!frame->GetRect().IsEmpty()) {
+      rectVisibility =
+        aPresShell->GetRectVisibility(frame,
+                                      nsRect(nsPoint(0,0), frame->GetSize()),
+                                      minDistance);
+    }
+  }
+
+  if (frame) {
+    nsCOMPtr<nsIDOMNode> firstVisibleNode = do_QueryInterface(frame->GetContent());
+
+    if (firstVisibleNode) {
+      frame->GetOffsets(startFrameOffset, endFrameOffset);
+      (*aFirstVisibleRange)->SetStart(firstVisibleNode, startFrameOffset);
+      (*aFirstVisibleRange)->SetEnd(firstVisibleNode, endFrameOffset);
+    }
+  }
+
+  return false;
 }
 
 already_AddRefed<nsIPresShell>
 nsTypeAheadFind::GetPresShell()
 {
   if (!mPresShell)
     return nullptr;
 
--- a/toolkit/components/typeaheadfind/nsTypeAheadFind.h
+++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.h
@@ -51,18 +51,18 @@ protected:
   void RangeStartsInsideLink(nsIDOMRange *aRange, nsIPresShell *aPresShell, 
                              bool *aIsInsideLink, bool *aIsStartingLink);
 
   void GetSelection(nsIPresShell *aPresShell, nsISelectionController **aSelCon, 
                     nsISelection **aDomSel);
   // *aNewRange may not be collapsed.  If you want to collapse it in a
   // particular way, you need to do it yourself.
   bool IsRangeVisible(nsIPresShell *aPresShell, nsPresContext *aPresContext,
-                        nsIDOMRange *aRange, bool aFlushLayout,
-                        nsIDOMRange **aNewRange,
+                        nsIDOMRange *aRange, bool aMustBeVisible, 
+                        bool aGetTopVisibleLeaf, nsIDOMRange **aNewRange,
                         bool *aUsesIndependentSelection);
   nsresult FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly,
                      bool aIsFirstVisiblePreferred, bool aFindPrev,
                      uint16_t* aResult);
   nsresult GetSearchContainers(nsISupports *aContainer,
                                nsISelectionController *aSelectionController,
                                bool aIsFirstVisiblePreferred,
                                bool aFindPrev, nsIPresShell **aPresShell,
@@ -90,17 +90,17 @@ protected:
   // mLastFindLength is the character length of the last find string.  It is used for
   // disabling the "not found" sound when using backspace or delete
   uint32_t mLastFindLength;
 
   // Sound is played asynchronously on some platforms.
   // If we destroy mSoundInterface before sound has played, it won't play
   nsCOMPtr<nsISound> mSoundInterface;
   bool mIsSoundInitialized;
-
+  
   // where selection was when user started the find
   nsCOMPtr<nsIDOMRange> mStartFindRange;
   nsCOMPtr<nsIDOMRange> mSearchRange;
   nsCOMPtr<nsIDOMRange> mStartPointRange;
   nsCOMPtr<nsIDOMRange> mEndPointRange;
 
   // Cached useful interfaces
   nsCOMPtr<nsIFind> mFind;
--- a/toolkit/modules/FinderHighlighter.jsm
+++ b/toolkit/modules/FinderHighlighter.jsm
@@ -1132,19 +1132,16 @@ FinderHighlighter.prototype = {
     this._updateRangeOutline(dict);
 
     let allRects = [];
     if (paintContent || dict.modalHighlightAllMask) {
       this._updateDynamicRangesRects(dict);
 
       let DOMRect = window.DOMRect;
       for (let [range, rectsAndTexts] of dict.modalHighlightRectsMap) {
-        if (!this.finder._fastFind.isRangeVisible(range, false))
-          continue;
-
         if (dict.updateAllRanges)
           rectsAndTexts = this._updateRangeRects(range);
 
         // If a geometry change was detected, we bail out right away here, because
         // the current set of ranges has been invalidated.
         if (dict.detectedGeometryChange)
           return;
 
--- a/toolkit/modules/FinderIterator.jsm
+++ b/toolkit/modules/FinderIterator.jsm
@@ -562,17 +562,23 @@ this.FinderIterator = {
       return frames;
 
     // Casting `window.frames` to an Iterator doesn't work, so we're stuck with
     // a plain, old for-loop.
     for (let i = 0, l = window.frames.length; i < l; ++i) {
       let frame = window.frames[i];
       // Don't count matches in hidden frames.
       let frameEl = frame && frame.frameElement;
-      if (!frameEl || !frameEl.getClientRects().length)
+      if (!frameEl)
+        continue;
+      // Construct a range around the frame element to check its visiblity.
+      let range = window.document.createRange();
+      range.setStart(frameEl, 0);
+      range.setEnd(frameEl, 0);
+      if (!finder._fastFind.isRangeVisible(range, this._getDocShell(range), true))
         continue;
       // All conditions pass, so push the current frame and its children on the
       // stack.
       frames.push(frame, ...this._collectFrames(frame, finder));
     }
 
     return frames;
   },