Bug 1276553 - Part 4: Implement frameloader level GroupedSHistory. r=smaug
authorSamael Wang <freesamael@gmail.com>
Fri, 14 Oct 2016 15:31:02 +0800
changeset 319716 97f2b5e98b9c923239278815c0ee0a67389602a9
parent 319715 3bac8b1f4cd434cb08880871bd4498c07e1c4ca8
child 319717 1c508ae660233d0bcb1c231a7243af42f503f79b
push id20748
push userphilringnalda@gmail.com
push dateFri, 28 Oct 2016 03:39:55 +0000
treeherderfx-team@715360440695 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1276553
milestone52.0a1
Bug 1276553 - Part 4: Implement frameloader level GroupedSHistory. r=smaug MozReview-Commit-ID: E5hOVOPW0nl
dom/base/GroupedSHistory.cpp
dom/base/GroupedSHistory.h
dom/base/PartialSHistory.cpp
dom/base/PartialSHistory.h
dom/base/moz.build
dom/base/nsFrameLoader.cpp
dom/base/nsFrameLoader.h
dom/base/nsIFrameLoader.idl
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
new file mode 100644
--- /dev/null
+++ b/dom/base/GroupedSHistory.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GroupedSHistory.h"
+#include "TabParent.h"
+#include "PartialSHistory.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION(GroupedSHistory, mPartialHistories)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(GroupedSHistory)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(GroupedSHistory)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GroupedSHistory)
+  NS_INTERFACE_MAP_ENTRY(nsIGroupedSHistory)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGroupedSHistory)
+NS_INTERFACE_MAP_END
+
+GroupedSHistory::GroupedSHistory()
+  : mCount(0),
+    mIndexOfActivePartialHistory(-1)
+{
+}
+
+NS_IMETHODIMP
+GroupedSHistory::GetCount(uint32_t* aResult)
+{
+  MOZ_ASSERT(aResult);
+  *aResult = mCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GroupedSHistory::AppendPartialSessionHistory(nsIPartialSHistory* aPartialHistory)
+{
+  if (!aPartialHistory) {
+    return NS_ERROR_INVALID_POINTER;
+  }
+
+  nsCOMPtr<nsIPartialSHistory> partialHistory(aPartialHistory);
+  if (!partialHistory || mPartialHistories.Contains(partialHistory)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Remove all items after active one and deactive it, unless it's the first
+  // call and no active partial history has been set yet.
+  if (mIndexOfActivePartialHistory >= 0) {
+    PurgePartialHistories(mIndexOfActivePartialHistory);
+    nsCOMPtr<nsIPartialSHistory> prevPartialHistory =
+      mPartialHistories[mIndexOfActivePartialHistory];
+    if (NS_WARN_IF(!prevPartialHistory)) {
+      // Cycle collected?
+      return NS_ERROR_UNEXPECTED;
+    }
+    prevPartialHistory->OnDeactive();
+  }
+
+  // Attach the partial history.
+  uint32_t offset = mCount;
+  mCount += partialHistory->GetCount();
+  mPartialHistories.AppendElement(partialHistory);
+  partialHistory->OnAttachGroupedSessionHistory(offset);
+  mIndexOfActivePartialHistory = mPartialHistories.Count() - 1;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GroupedSHistory::OnPartialSessionHistoryChange(
+  nsIPartialSHistory* aPartialSessionHistory)
+{
+  if (!aPartialSessionHistory) {
+    return NS_ERROR_INVALID_POINTER;
+  }
+
+  nsCOMPtr<nsIPartialSHistory> partialHistory(aPartialSessionHistory);
+  int32_t index = mPartialHistories.IndexOf(partialHistory);
+  if (NS_WARN_IF(index != mIndexOfActivePartialHistory) ||
+      NS_WARN_IF(index < 0)) {
+    // Non-active or not attached partialHistory
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  PurgePartialHistories(index);
+
+  // Update global count.
+  uint32_t count = partialHistory->GetCount();
+  uint32_t offset = partialHistory->GetGlobalIndexOffset();
+  mCount = count + offset;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GroupedSHistory::GotoIndex(uint32_t aGlobalIndex,
+                           nsIFrameLoader** aTargetLoaderToSwap)
+{
+  nsCOMPtr<nsIPartialSHistory> currentPartialHistory =
+    mPartialHistories[mIndexOfActivePartialHistory];
+  if (!currentPartialHistory) {
+    // Cycle collected?
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  for (uint32_t i = 0; i < mPartialHistories.Length(); i++) {
+    nsCOMPtr<nsIPartialSHistory> partialHistory = mPartialHistories[i];
+    if (NS_WARN_IF(!partialHistory)) {
+      // Cycle collected?
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    // Examine index range.
+    uint32_t offset = partialHistory->GetGlobalIndexOffset();
+    uint32_t count = partialHistory->GetCount();
+    if (offset <= aGlobalIndex && (offset + count) > aGlobalIndex) {
+      uint32_t targetIndex = aGlobalIndex - offset;
+      partialHistory->GetOwnerFrameLoader(aTargetLoaderToSwap);
+      if ((size_t)mIndexOfActivePartialHistory == i) {
+        return NS_OK;
+      }
+      mIndexOfActivePartialHistory = i;
+      if (NS_FAILED(currentPartialHistory->OnDeactive()) ||
+          NS_FAILED(partialHistory->OnActive(mCount, targetIndex))) {
+        return NS_ERROR_FAILURE;
+      }
+      return NS_OK;
+    }
+  }
+
+  // Index not found.
+  NS_WARNING("Out of index request!");
+  return NS_ERROR_FAILURE;
+}
+
+void
+GroupedSHistory::PurgePartialHistories(uint32_t aLastPartialIndexToKeep)
+{
+  uint32_t lastIndex = mPartialHistories.Length() - 1;
+  if (aLastPartialIndexToKeep >= lastIndex) {
+    // Nothing to remove.
+    return;
+  }
+
+  // Close tabs.
+  for (uint32_t i = lastIndex; i > aLastPartialIndexToKeep; i--) {
+    nsCOMPtr<nsIPartialSHistory> partialHistory = mPartialHistories[i];
+    if (!partialHistory) {
+      // Cycle collected?
+      return;
+    }
+
+    nsCOMPtr<nsIFrameLoader> loader;
+    partialHistory->GetOwnerFrameLoader(getter_AddRefs(loader));
+    loader->RequestFrameLoaderClose();
+  }
+
+  // Remove references.
+  mPartialHistories.RemoveElementsAt(aLastPartialIndexToKeep + 1,
+                                     lastIndex - aLastPartialIndexToKeep);
+}
+
+/* static */ bool
+GroupedSHistory::GroupedHistoryEnabled() {
+  return Preferences::GetBool("browser.groupedhistory.enabled", false);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/GroupedSHistory.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GroupedSHistory_h
+#define GroupedSHistory_h
+
+#include "nsIFrameLoader.h"
+#include "nsIGroupedSHistory.h"
+#include "nsIPartialSHistory.h"
+#include "nsTArray.h"
+#include "nsWeakReference.h"
+
+namespace mozilla {
+namespace dom {
+
+
+/**
+ * GroupedSHistory connects session histories across multiple frameloaders.
+ * Each frameloader has a PartialSHistory, and GroupedSHistory has an array
+ * refering to all participating PartialSHistory(s).
+ *
+ * The following figure illustrates the idea. In this case, the GroupedSHistory
+ * is composed of 3 frameloaders, and the active one is frameloader 1.
+ * GroupedSHistory is always attached to the active frameloader.
+ *
+ *            +----------------------------------------------------+
+ *            |                                                    |
+ *            |                                                    v
+ *  +------------------+      +-------------------+       +-----------------+
+ *  |  FrameLoader 1   |      | PartialSHistory 1 |       | GroupedSHistory |
+ *  |     (active)     |----->|     (active)      |<--+---|                 |
+ *  +------------------+      +-------------------+   |   +-----------------+
+ *                                                    |
+ *  +------------------+      +-------------------+   |
+ *  |  FrameLoader 2   |      | PartialSHistory 2 |   |
+ *  |    (inactive)    |----->|    (inactive)     |<--+
+ *  +------------------+      +-------------------+   |
+ *                                                    |
+ *  +------------------+      +-------------------+   |
+ *  |  FrameLoader 3   |      | PartialSHistory 3 |   |
+ *  |    (inactive)    |----->|    (inactive)     |<--+
+ *  +------------------+      +-------------------+
+ *
+ * If a history navigation leads to frameloader 3, it becomes the active one,
+ * and GroupedSHistory is re-attached to frameloader 3.
+ *
+ *  +------------------+      +-------------------+
+ *  |  FrameLoader 1   |      | PartialSHistory 1 |
+ *  |    (inactive)    |----->|    (inactive)     |<--+
+ *  +------------------+      +-------------------+   |
+ *                                                    |
+ *  +------------------+      +-------------------+   |
+ *  |  FrameLoader 2   |      | PartialSHistory 2 |   |
+ *  |    (inactive)    |----->|    (inactive)     |<--+
+ *  +------------------+      +-------------------+   |
+ *                                                    |
+ *  +------------------+      +-------------------+   |   +-----------------+
+ *  |  FrameLoader 3   |      | PartialSHistory 3 |   |   | GroupedSHistory |
+ *  |     (active)     |----->|     (active)      |<--+---|                 |
+ *  +------------------+      +-------------------+       +-----------------+
+ *            |                                                    ^
+ *            |                                                    |
+ *            +----------------------------------------------------+
+ */
+class GroupedSHistory final : public nsIGroupedSHistory
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(GroupedSHistory)
+  NS_DECL_NSIGROUPEDSHISTORY
+  GroupedSHistory();
+
+  /**
+   * Get the value of preference "browser.groupedhistory.enabled" to determine
+   * if grouped session history should be enabled.
+   */
+  static bool GroupedHistoryEnabled();
+
+private:
+  ~GroupedSHistory() {}
+
+  /**
+   * Remove all partial histories and close tabs after the given index (of
+   * mPartialHistories, not the index of session history entries).
+   */
+  void PurgePartialHistories(uint32_t aLastPartialIndexToKeep);
+
+  // The total number of entries in all partial histories.
+  uint32_t mCount;
+
+  // The index of currently active partial history in mPartialHistories.
+  // Use int32_t as we have invalid index and nsCOMArray also uses int32_t.
+  int32_t mIndexOfActivePartialHistory;
+
+  // All participating nsIPartialSHistory objects.
+  nsCOMArray<nsIPartialSHistory> mPartialHistories;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* GroupedSHistory_h */
new file mode 100644
--- /dev/null
+++ b/dom/base/PartialSHistory.cpp
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PartialSHistory.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION(PartialSHistory, mOwnerFrameLoader)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(PartialSHistory)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(PartialSHistory)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PartialSHistory)
+  NS_INTERFACE_MAP_ENTRY(nsIPartialSHistory)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPartialSHistory)
+  NS_INTERFACE_MAP_ENTRY(nsISHistoryListener)
+  NS_INTERFACE_MAP_ENTRY(nsIPartialSHistoryListener)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END
+
+PartialSHistory::PartialSHistory(nsIFrameLoader* aOwnerFrameLoader)
+  : mCount(0),
+    mGlobalIndexOffset(0),
+    mOwnerFrameLoader(aOwnerFrameLoader)
+{
+  MOZ_ASSERT(aOwnerFrameLoader);
+}
+
+already_AddRefed<nsISHistory>
+PartialSHistory::GetSessionHistory()
+{
+  if (!mOwnerFrameLoader) {
+    // Cycle collected?
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIDocShell> docShell;
+  mOwnerFrameLoader->GetDocShell(getter_AddRefs(docShell));
+  if (!docShell) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
+  nsCOMPtr<nsISHistory> shistory;
+  webNav->GetSessionHistory(getter_AddRefs(shistory));
+  return shistory.forget();
+}
+
+already_AddRefed<TabParent>
+PartialSHistory::GetTabParent()
+{
+  if (!mOwnerFrameLoader) {
+    // Cycle collected?
+    return nullptr;
+  }
+
+  nsCOMPtr<nsITabParent> tabParent;
+  mOwnerFrameLoader->GetTabParent(getter_AddRefs(tabParent));
+  return RefPtr<TabParent>(static_cast<TabParent*>(tabParent.get())).forget();
+}
+
+NS_IMETHODIMP
+PartialSHistory::GetCount(uint32_t* aResult)
+{
+  if (!aResult) {
+    return NS_ERROR_INVALID_POINTER;
+  }
+
+  // If we have direct reference to nsISHistory, simply pass through.
+  nsCOMPtr<nsISHistory> shistory(GetSessionHistory());
+  if (shistory) {
+    int32_t count;
+    nsresult rv = shistory->GetCount(&count);
+    if (NS_FAILED(rv) || count < 0) {
+      *aResult = 0;
+      return NS_ERROR_FAILURE;
+    }
+    *aResult = count;
+    return NS_OK;
+  }
+
+  // Otherwise use the cached value.
+  *aResult = mCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PartialSHistory::GetGlobalIndexOffset(uint32_t* aResult)
+{
+  if (!aResult) {
+    return NS_ERROR_INVALID_POINTER;
+  }
+
+  // If we have direct reference to nsISHistory, simply pass through.
+  nsCOMPtr<nsISHistory> shistory(GetSessionHistory());
+  if (shistory) {
+    int32_t offset;
+    nsresult rv = shistory->GetGlobalIndexOffset(&offset);
+    if (NS_FAILED(rv) || offset < 0) {
+      *aResult = 0;
+      return NS_ERROR_FAILURE;
+    }
+    *aResult = offset;
+    return NS_OK;
+  }
+
+  // Otherwise use the cached value.
+  *aResult = mGlobalIndexOffset;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PartialSHistory::GetOwnerFrameLoader(nsIFrameLoader** aResult)
+{
+  nsCOMPtr<nsIFrameLoader> loader(mOwnerFrameLoader);
+  loader.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PartialSHistory::OnAttachGroupedSessionHistory(uint32_t aOffset)
+{
+  mGlobalIndexOffset = aOffset;
+
+  // If we have direct reference to nsISHistory, simply pass through.
+  nsCOMPtr<nsISHistory> shistory(GetSessionHistory());
+  if (shistory) {
+    // nsISHistory uses int32_t
+    if (aOffset > INT32_MAX) {
+      return NS_ERROR_FAILURE;
+    }
+    return shistory->OnAttachGroupedSessionHistory(aOffset);
+  }
+
+  // Otherwise notify through TabParent.
+  RefPtr<TabParent> tabParent(GetTabParent());
+  if (!tabParent) {
+    // We have neither shistory nor tabParent?
+    NS_WARNING("Unable to get shitory nor tabParent!");
+    return NS_ERROR_UNEXPECTED;
+  }
+  Unused << tabParent->SendNotifyAttachGroupedSessionHistory(aOffset);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PartialSHistory::OnSessionHistoryChange(uint32_t aCount)
+{
+  mCount = aCount;
+  return OnLengthChange(aCount);
+}
+
+NS_IMETHODIMP
+PartialSHistory::OnActive(uint32_t aGlobalLength, uint32_t aTargetLocalIndex)
+{
+  // In-process case.
+  nsCOMPtr<nsISHistory> shistory(GetSessionHistory());
+  if (shistory) {
+    // nsISHistory uses int32_t
+    if (aGlobalLength > INT32_MAX || aTargetLocalIndex > INT32_MAX) {
+      return NS_ERROR_FAILURE;
+    }
+    return shistory->OnPartialSessionHistoryActive(aGlobalLength,
+                                                   aTargetLocalIndex);
+  }
+
+  // Cross-process case.
+  RefPtr<TabParent> tabParent(GetTabParent());
+  if (!tabParent) {
+    // We have neither shistory nor tabParent?
+    NS_WARNING("Unable to get shitory nor tabParent!");
+    return NS_ERROR_UNEXPECTED;
+  }
+  Unused << tabParent->SendNotifyPartialSessionHistoryActive(aGlobalLength,
+                                                             aTargetLocalIndex);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PartialSHistory::OnDeactive()
+{
+  // In-process case.
+  nsCOMPtr<nsISHistory> shistory(GetSessionHistory());
+  if (shistory) {
+    if (NS_FAILED(shistory->OnPartialSessionHistoryDeactive())) {
+      return NS_ERROR_FAILURE;
+    }
+    return NS_OK;
+  }
+
+  // Cross-process case.
+  RefPtr<TabParent> tabParent(GetTabParent());
+  if (!tabParent) {
+    // We have neither shistory nor tabParent?
+    NS_WARNING("Unable to get shitory nor tabParent!");
+    return NS_ERROR_UNEXPECTED;
+  }
+  Unused << tabParent->SendNotifyPartialSessionHistoryDeactive();
+  return NS_OK;
+}
+
+/*******************************************************************************
+ * nsIPartialSHistoryListener
+ ******************************************************************************/
+
+NS_IMETHODIMP
+PartialSHistory::OnRequestCrossBrowserNavigation(uint32_t aIndex)
+{
+  if (!mOwnerFrameLoader) {
+    // Cycle collected?
+    return NS_ERROR_UNEXPECTED;
+  }
+  return mOwnerFrameLoader->RequestGroupedHistoryNavigation(aIndex);
+}
+
+/*******************************************************************************
+ * nsISHistoryListener
+ ******************************************************************************/
+
+NS_IMETHODIMP
+PartialSHistory::OnLengthChange(int32_t aCount)
+{
+  if (!mOwnerFrameLoader) {
+    // Cycle collected?
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (aCount < 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIGroupedSHistory> groupedHistory;
+  mOwnerFrameLoader->GetGroupedSessionHistory(getter_AddRefs(groupedHistory));
+  if (!groupedHistory) {
+    // Maybe we're not the active partial history, but in this case we shouldn't
+    // receive any update from session history object either.
+    return NS_ERROR_FAILURE;
+  }
+
+  groupedHistory->OnPartialSessionHistoryChange(this);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PartialSHistory::OnHistoryNewEntry(nsIURI *aNewURI, int32_t aOldIndex)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+PartialSHistory::OnHistoryGoBack(nsIURI *aBackURI, bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+PartialSHistory::OnHistoryGoForward(nsIURI *aForwardURI, bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+PartialSHistory::OnHistoryReload(nsIURI *aReloadURI, uint32_t aReloadFlags, bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+PartialSHistory::OnHistoryGotoIndex(int32_t aIndex, nsIURI *aGotoURI, bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+PartialSHistory::OnHistoryPurge(int32_t aNumEntries, bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+PartialSHistory::OnHistoryReplaceEntry(int32_t aIndex)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/PartialSHistory.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PartialSHistory_h
+#define PartialSHistory_h
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsFrameLoader.h"
+#include "nsIGroupedSHistory.h"
+#include "nsIPartialSHistoryListener.h"
+#include "nsIPartialSHistory.h"
+#include "nsISHistory.h"
+#include "nsISHistoryListener.h"
+#include "TabParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class PartialSHistory final : public nsIPartialSHistory,
+                              public nsISHistoryListener,
+                              public nsIPartialSHistoryListener,
+                              public nsSupportsWeakReference
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(PartialSHistory, nsIPartialSHistory)
+  NS_DECL_NSIPARTIALSHISTORY
+  NS_DECL_NSIPARTIALSHISTORYLISTENER
+  NS_DECL_NSISHISTORYLISTENER
+
+  /**
+   * Note that PartialSHistory must be constructed after frameloader has
+   * created a valid docshell or tabparent.
+   */
+  explicit PartialSHistory(nsIFrameLoader* aOwnerFrameLoader);
+
+private:
+  ~PartialSHistory() {}
+  already_AddRefed<nsISHistory> GetSessionHistory();
+  already_AddRefed<TabParent> GetTabParent();
+
+  // The cache of number of entries in corresponding nsISHistory. It's only
+  // used for remote process case. If nsISHistory is in-process, mCount will not
+  // be used at all.
+  uint32_t mCount;
+
+  // The cache of globalIndexOffset in corresponding nsISHistory. It's only
+  // used for remote process case.
+  uint32_t mGlobalIndexOffset;
+
+  // The frameloader which owns this PartialSHistory.
+  nsCOMPtr<nsIFrameLoader> mOwnerFrameLoader;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* PartialSHistory_h */
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -177,28 +177,30 @@ EXPORTS.mozilla.dom += [
     'ElementInlines.h',
     'EventSource.h',
     'File.h',
     'FileList.h',
     'FileReader.h',
     'FormData.h',
     'FragmentOrElement.h',
     'FromParser.h',
+    'GroupedSHistory.h',
     'ImageEncoder.h',
     'ImageTracker.h',
     'ImportManager.h',
     'Link.h',
     'Location.h',
     'MutableBlobStorage.h',
     'MutableBlobStreamListener.h',
     'NameSpaceConstants.h',
     'Navigator.h',
     'NodeInfo.h',
     'NodeInfoInlines.h',
     'NodeIterator.h',
+    'PartialSHistory.h',
     'ProcessGlobal.h',
     'ResponsiveImageSelector.h',
     'SameProcessMessageQueue.h',
     'ScreenOrientation.h',
     'ScriptSettings.h',
     'ShadowRoot.h',
     'StructuredCloneHolder.h',
     'StructuredCloneTags.h',
@@ -240,16 +242,17 @@ UNIFIED_SOURCES += [
     'DOMStringList.cpp',
     'Element.cpp',
     'EventSource.cpp',
     'File.cpp',
     'FileList.cpp',
     'FileReader.cpp',
     'FormData.cpp',
     'FragmentOrElement.cpp',
+    'GroupedSHistory.cpp',
     'ImageEncoder.cpp',
     'ImageTracker.cpp',
     'ImportManager.cpp',
     'Link.cpp',
     'Location.cpp',
     'MultipartBlobImpl.cpp',
     'MutableBlobStorage.cpp',
     'MutableBlobStreamListener.cpp',
@@ -326,16 +329,17 @@ UNIFIED_SOURCES += [
     'nsTreeSanitizer.cpp',
     'nsViewportInfo.cpp',
     'nsWindowMemoryReporter.cpp',
     'nsWindowRoot.cpp',
     'nsWrapperCache.cpp',
     'nsXHTMLContentSerializer.cpp',
     'nsXMLContentSerializer.cpp',
     'nsXMLNameSpaceMap.cpp',
+    'PartialSHistory.cpp',
     'PostMessageEvent.cpp',
     'ProcessGlobal.cpp',
     'ResponsiveImageSelector.cpp',
     'SameProcessMessageQueue.cpp',
     'ScreenOrientation.cpp',
     'ScriptSettings.cpp',
     'ShadowRoot.cpp',
     'StructuredCloneHolder.cpp',
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -53,16 +53,18 @@
 #include "nsIMozBrowserFrame.h"
 #include "nsISHistory.h"
 #include "nsNullPrincipal.h"
 #include "nsIScriptError.h"
 #include "nsGlobalWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsLayoutUtils.h"
 #include "nsView.h"
+#include "GroupedSHistory.h"
+#include "PartialSHistory.h"
 
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
 
 #include "nsGkAtoms.h"
 #include "nsNameSpaceManager.h"
 
@@ -129,17 +131,22 @@ typedef FrameMetrics::ViewID ViewID;
 // does not count chrome frames when determining depth, nor does it
 // prevent chrome recursion.  Number is fairly arbitrary, but meant to
 // keep number of shells to a reasonable number on accidental recursion with a
 // small (but not 1) branching factor.  With large branching factors the number
 // of shells can rapidly become huge and run us out of memory.  To solve that,
 // we'd need to re-institute a fixed version of bug 98158.
 #define MAX_DEPTH_CONTENT_FRAMES 10
 
-NS_IMPL_CYCLE_COLLECTION(nsFrameLoader, mDocShell, mMessageManager, mChildMessageManager)
+NS_IMPL_CYCLE_COLLECTION(nsFrameLoader,
+                         mDocShell,
+                         mMessageManager,
+                         mChildMessageManager,
+                         mPartialSessionHistory,
+                         mGroupedSessionHistory)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIFrameLoader)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistable)
 NS_INTERFACE_MAP_END
@@ -364,16 +371,127 @@ nsFrameLoader::MakePrerenderedLoaderActi
 
     nsresult rv = mDocShell->SetIsActive(true);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsFrameLoader::GetPartialSessionHistory(nsIPartialSHistory** aResult)
+{
+  if (mRemoteBrowser && !mPartialSessionHistory) {
+    // For remote case we can lazy initialize PartialSHistory since
+    // it doens't need to be registered as a listener to nsISHistory directly.
+    mPartialSessionHistory = new PartialSHistory(this);
+  }
+
+  nsCOMPtr<nsIPartialSHistory> partialHistory(mPartialSessionHistory);
+  partialHistory.forget(aResult);
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsFrameLoader::GetGroupedSessionHistory(nsIGroupedSHistory** aResult)
+{
+  nsCOMPtr<nsIGroupedSHistory> groupedHistory(mGroupedSessionHistory);
+  groupedHistory.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFrameLoader::AppendPartialSessionHistoryAndSwap(nsIFrameLoader* aOther)
+{
+  if (!aOther) {
+    return NS_ERROR_INVALID_POINTER;
+  }
+
+  nsCOMPtr<nsIGroupedSHistory> otherGroupedHistory;
+  aOther->GetGroupedSessionHistory(getter_AddRefs(otherGroupedHistory));
+  MOZ_ASSERT(!otherGroupedHistory,
+             "Cannot append a GroupedSHistory owner to another.");
+  if (otherGroupedHistory) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  // Append ourselves.
+  nsresult rv;
+  if (!mGroupedSessionHistory) {
+    mGroupedSessionHistory = new GroupedSHistory();
+    rv = mGroupedSessionHistory->AppendPartialSessionHistory(mPartialSessionHistory);
+    if (NS_FAILED(rv)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  if (aOther == this) {
+    return NS_OK;
+  }
+
+  // Append the other.
+  RefPtr<nsFrameLoader> otherLoader = static_cast<nsFrameLoader*>(aOther);
+  rv = mGroupedSessionHistory->
+         AppendPartialSessionHistory(otherLoader->mPartialSessionHistory);
+  if (NS_FAILED(rv)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Swap loaders through our owner, so the owner's listeners will be correctly
+  // setup.
+  nsCOMPtr<nsIBrowser> ourBrowser = do_QueryInterface(mOwnerContent);
+  nsCOMPtr<nsIBrowser> otherBrowser = do_QueryInterface(otherLoader->mOwnerContent);
+  if (!ourBrowser || !otherBrowser) {
+    return NS_ERROR_FAILURE;
+  }
+  if (NS_FAILED(ourBrowser->SwapBrowsers(otherBrowser))) {
+    return NS_ERROR_FAILURE;
+  }
+  mGroupedSessionHistory.swap(otherLoader->mGroupedSessionHistory);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFrameLoader::RequestGroupedHistoryNavigation(uint32_t aGlobalIndex)
+{
+  if (!mGroupedSessionHistory) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsCOMPtr<nsIFrameLoader> targetLoader;
+  nsresult rv = mGroupedSessionHistory->
+                  GotoIndex(aGlobalIndex, getter_AddRefs(targetLoader));
+  if (NS_FAILED(rv)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<nsFrameLoader> otherLoader = static_cast<nsFrameLoader*>(targetLoader.get());
+  if (!targetLoader) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (targetLoader == this) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIBrowser> ourBrowser = do_QueryInterface(mOwnerContent);
+  nsCOMPtr<nsIBrowser> otherBrowser = do_QueryInterface(otherLoader->mOwnerContent);
+  if (!ourBrowser || !otherBrowser) {
+    return NS_ERROR_FAILURE;
+  }
+  if (NS_FAILED(ourBrowser->SwapBrowsers(otherBrowser))) {
+    return NS_ERROR_FAILURE;
+  }
+  mGroupedSessionHistory.swap(otherLoader->mGroupedSessionHistory);
+
+  return NS_OK;
+}
+
 nsresult
 nsFrameLoader::ReallyStartLoading()
 {
   nsresult rv = ReallyStartLoadingInternal();
   if (NS_FAILED(rv)) {
     FireErrorEvent();
   }
 
@@ -2027,16 +2145,25 @@ nsFrameLoader::MaybeCreateDocShell()
       !mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory)) {
     nsresult rv;
     nsCOMPtr<nsISHistory> sessionHistory =
       do_CreateInstance(NS_SHISTORY_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
     webNav->SetSessionHistory(sessionHistory);
+
+
+    if (GroupedSHistory::GroupedHistoryEnabled()) {
+      mPartialSessionHistory = new PartialSHistory(this);
+      nsCOMPtr<nsISHistoryListener> listener(do_QueryInterface(mPartialSessionHistory));
+      nsCOMPtr<nsIPartialSHistoryListener> partialListener(do_QueryInterface(mPartialSessionHistory));
+      sessionHistory->AddSHistoryListener(listener);
+      sessionHistory->SetPartialSHistoryListener(partialListener);
+    }
   }
 
   DocShellOriginAttributes attrs;
   if (docShell->ItemType() == mDocShell->ItemType()) {
     attrs = nsDocShell::Cast(docShell)->GetOriginAttributes();
   }
 
   // Inherit origin attributes from parent document if
@@ -3090,16 +3217,28 @@ nsFrameLoader::RequestNotifyAfterRemoteP
   if (mRemoteBrowser) {
     Unused << mRemoteBrowser->SendRequestNotifyAfterRemotePaint();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsFrameLoader::RequestFrameLoaderClose()
+{
+  nsCOMPtr<nsIBrowser> browser = do_QueryInterface(mOwnerContent);
+  if (NS_WARN_IF(!browser)) {
+    // OwnerElement other than nsIBrowser is not supported yet.
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  return browser->CloseBrowser();
+}
+
+NS_IMETHODIMP
 nsFrameLoader::Print(uint64_t aOuterWindowID,
                      nsIPrintSettings* aPrintSettings,
                      nsIWebProgressListener* aProgressListener)
 {
 #if defined(NS_PRINTING)
   if (mRemoteBrowser) {
     RefPtr<embedding::PrintingParent> printingParent =
       mRemoteBrowser->Manager()->AsContentParent()->GetPrintingParent();
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -20,16 +20,17 @@
 #include "nsIURI.h"
 #include "nsFrameMessageManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Attributes.h"
 #include "nsStubMutationObserver.h"
 #include "Units.h"
 #include "nsIWebBrowserPersistable.h"
 #include "nsIFrame.h"
+#include "nsIGroupedSHistory.h"
 
 class nsIURI;
 class nsSubDocumentFrame;
 class nsView;
 class nsIInProcessContentFrameMessageManager;
 class AutoResetInShow;
 class AutoResetInFrameSwap;
 class nsITabParent;
@@ -359,16 +360,19 @@ private:
 
   // See nsIFrameLoader.idl. EVENT_MODE_NORMAL_DISPATCH automatically
   // forwards some input events to out-of-process content.
   uint32_t mEventMode;
 
   // Holds the last known size of the frame.
   mozilla::ScreenIntSize mLazySize;
 
+  nsCOMPtr<nsIPartialSHistory> mPartialSessionHistory;
+  nsCOMPtr<nsIGroupedSHistory> mGroupedSessionHistory;
+
   bool mIsPrerendered : 1;
   bool mDepthTooGreat : 1;
   bool mIsTopLevelContent : 1;
   bool mDestroyCalled : 1;
   bool mNeedsAsyncDestroy : 1;
   bool mInSwap : 1;
   bool mInShow : 1;
   bool mHideCalled : 1;
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -13,16 +13,18 @@ interface nsIFrame;
 interface nsSubDocumentFrame;
 interface nsIMessageSender;
 interface nsIVariant;
 interface nsIDOMElement;
 interface nsITabParent;
 interface nsILoadContext;
 interface nsIPrintSettings;
 interface nsIWebProgressListener;
+interface nsIGroupedSHistory;
+interface nsIPartialSHistory;
 
 [scriptable, builtinclass, uuid(1645af04-1bc7-4363-8f2c-eb9679220ab1)]
 interface nsIFrameLoader : nsISupports
 {
   /**
    * Get the docshell from the frame loader.
    */
   readonly attribute nsIDocShell docShell;
@@ -67,16 +69,27 @@ interface nsIFrameLoader : nsISupports
   void setIsPrerendered();
 
   /**
    * Make the prerendered frameloader being active (and clear isPrerendered flag).
    */
   void makePrerenderedLoaderActive();
 
   /**
+   * Append partial session history from another frame loader.
+   */
+  void appendPartialSessionHistoryAndSwap(in nsIFrameLoader aOther);
+
+  /**
+   * If grouped session history is applied, use this function to navigate to
+   * an entry of session history object of another frameloader.
+   */
+  void requestGroupedHistoryNavigation(in unsigned long aGlobalIndex);
+
+  /**
    * Destroy the frame loader and everything inside it. This will
    * clear the weak owner content reference.
    */
   void destroy();
 
   /**
    * Find out whether the loader's frame is at too great a depth in
    * the frame tree.  This can be used to decide what operations may
@@ -134,16 +147,21 @@ interface nsIFrameLoader : nsISupports
   /**
    * Request that the next time a remote layer transaction has been
    * received by the Compositor, a MozAfterRemoteFrame event be sent
    * to the window.
    */
   void requestNotifyAfterRemotePaint();
 
   /**
+   * Close the window through the ownerElement.
+   */
+  void requestFrameLoaderClose();
+
+  /**
    * Print the current document.
    *
    * @param aOuterWindowID the ID of the outer window to print
    * @param aPrintSettings optional print settings to use; printSilent can be
    *                       set to prevent prompting.
    * @param aProgressListener optional print progress listener.
    */
   void print(in unsigned long long aOuterWindowID,
@@ -223,16 +241,27 @@ interface nsIFrameLoader : nsISupports
 
   /**
    * The last known height of the frame. Reading this property will not trigger
    * a reflow, and therefore may not reflect the current state of things. It
    * should only be used in asynchronous APIs where values are not guaranteed
    * to be up-to-date when received.
    */
   readonly attribute unsigned long lazyHeight;
+
+  /**
+   * The partial session history.
+   */
+  readonly attribute nsIPartialSHistory partialSessionHistory;
+
+  /**
+   * The grouped session history composed of multiple session history objects
+   * across root docshells.
+   */
+  readonly attribute nsIGroupedSHistory groupedSessionHistory;
 };
 
 %{C++
 class nsFrameLoader;
 %}
 
 native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -593,16 +593,33 @@ parent:
 
     // After a compositor reset, it is necessary to reconnect each layers ID to
     // the compositor of the widget that will render those layers. Note that
     // this is sync so we can ensure that messages to the window compositor
     // arrive before the TabChild attempts to use its cross-process compositor
     // bridge.
     sync EnsureLayersConnected();
 
+    /**
+     * Notify parent that one or more entries have been added / removed from
+     * the child session history.
+     *
+     * @param aCount the updated number of entries in child session history
+     */
+    async NotifySessionHistoryChange(uint32_t aCount);
+
+    /**
+     * When the session history is across multiple root docshells, this function
+     * is used to notify parent that it needs to navigate to an entry out of
+     * local index of the child.
+     *
+     * @param aGlobalIndex The global index of history entry to navigate to.
+     */
+    async RequestCrossBrowserNavigation(uint32_t aGlobalIndex);
+
 child:
     /**
      * Notify the remote browser that it has been Show()n on this
      * side, with the given |visibleRect|.  This message is expected
      * to trigger creation of the remote browser's "widget".
      *
      * |Show()| and |Move()| take IntSizes rather than Rects because
      * content processes always render to a virtual <0, 0> top-left
@@ -853,16 +870,42 @@ child:
     /**
      * Update the child with the tab's current top-level native window handle.
      * This is used by a11y objects who must expose their native window.
      *
      * @param aNewHandle The native window handle of the tab's top-level window.
      */
     async UpdateNativeWindowHandle(uintptr_t aNewHandle);
 
+    /**
+     * Called when the session history of this particular PBrowser has been
+     * attached to a grouped session history.
+     *
+     * @param aOffset           The number of entries in the grouped session
+     *                          history before this session history object.
+     */
+    async NotifyAttachGroupedSessionHistory(uint32_t aOffset);
+
+    /**
+     * Notify that the session history associated to this PBrowser has become
+     * the active history in the grouped session history.
+     *
+     * @param aGlobalLength      The up-to-date number of entries in the grouped
+     *                           session history.
+     * @param aTargetLocalIndex  The target local index to navigate to.
+     */
+    async NotifyPartialSessionHistoryActive(uint32_t aGlobalLength,
+                                            uint32_t aTargetLocalIndex);
+
+    /**
+     * Notify that the session history asssociates to this PBrowser has become
+     * an inactive history in the grouped session history.
+     */
+    async NotifyPartialSessionHistoryDeactive();
+
 /*
  * FIXME: write protocol!
 
 state LIVE:
     send LoadURL goto LIVE;
 //etc.
     send Destroy goto DYING;
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -111,16 +111,19 @@
 #include "nsIURILoader.h"
 #include "nsIScriptError.h"
 #include "mozilla/EventForwards.h"
 #include "nsDeviceContext.h"
 #include "nsSandboxFlags.h"
 #include "FrameLayerBuilder.h"
 #include "VRManagerChild.h"
 #include "nsICommandParams.h"
+#include "nsISHistory.h"
+#include "nsQueryObject.h"
+#include "GroupedSHistory.h"
 
 #ifdef NS_PRINTING
 #include "nsIPrintSession.h"
 #include "nsIPrintSettings.h"
 #include "nsIPrintSettingsService.h"
 #include "nsIWebBrowserPrint.h"
 #endif
 
@@ -138,16 +141,20 @@ using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::docshell;
 using namespace mozilla::widget;
 using namespace mozilla::jsipc;
 using mozilla::layers::GeckoContentController;
 
 NS_IMPL_ISUPPORTS(ContentListener, nsIDOMEventListener)
+NS_IMPL_ISUPPORTS(TabChildSHistoryListener,
+                  nsISHistoryListener,
+                  nsIPartialSHistoryListener,
+                  nsISupportsWeakReference)
 
 static const CSSSize kDefaultViewportSize(980, 480);
 
 static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
 
 typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
 static TabChildMap* sTabChildren;
 
@@ -825,16 +832,30 @@ TabChild::Init()
         if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
           static_cast<TabChild*>(tabChild.get())->ContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
         }
       });
   mAPZEventState = new APZEventState(mPuppetWidget, Move(callback));
 
   mIPCOpen = true;
 
+  if (GroupedSHistory::GroupedHistoryEnabled()) {
+    // Set session history listener.
+    nsCOMPtr<nsISHistory> shistory;
+    mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+    if (!shistory) {
+      return NS_ERROR_FAILURE;
+    }
+    mHistoryListener = new TabChildSHistoryListener(this);
+    nsCOMPtr<nsISHistoryListener> listener(do_QueryObject(mHistoryListener));
+    shistory->AddSHistoryListener(listener);
+    nsCOMPtr<nsIPartialSHistoryListener> partialListener(do_QueryObject(mHistoryListener));
+    shistory->SetPartialSHistoryListener(partialListener);
+  }
+
   return NS_OK;
 }
 
 void
 TabChild::NotifyTabContextUpdated(bool aIsPreallocated)
 {
   nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
   MOZ_ASSERT(docShell);
@@ -1286,22 +1307,26 @@ TabChild::ActorDestroy(ActorDestroyReaso
 
   if (GetTabId() != 0) {
     NestedTabChildMap().erase(GetTabId());
   }
 }
 
 TabChild::~TabChild()
 {
-    DestroyWindow();
-
-    nsCOMPtr<nsIWebBrowser> webBrowser = do_QueryInterface(WebNavigation());
-    if (webBrowser) {
-      webBrowser->SetContainerWindow(nullptr);
-    }
+  DestroyWindow();
+
+  nsCOMPtr<nsIWebBrowser> webBrowser = do_QueryInterface(WebNavigation());
+  if (webBrowser) {
+    webBrowser->SetContainerWindow(nullptr);
+  }
+
+  if (mHistoryListener) {
+    mHistoryListener->ClearTabChild();
+  }
 }
 
 void
 TabChild::SetProcessNameToAppName()
 {
   nsCOMPtr<mozIApplication> app = GetOwnApp();
   if (!app) {
     return;
@@ -1836,16 +1861,58 @@ TabChild::RecvStopIMEStateManagement()
 bool
 TabChild::RecvMenuKeyboardListenerInstalled(const bool& aInstalled)
 {
   IMEStateManager::OnInstalledMenuKeyboardListener(aInstalled);
   return true;
 }
 
 bool
+TabChild::RecvNotifyAttachGroupedSessionHistory(const uint32_t& aOffset)
+{
+  // nsISHistory uses int32_t
+  if (NS_WARN_IF(aOffset > INT32_MAX)) {
+    return false;
+  }
+
+  nsCOMPtr<nsISHistory> shistory;
+  mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+  NS_ENSURE_TRUE(shistory, false);
+
+  return NS_SUCCEEDED(shistory->OnAttachGroupedSessionHistory(aOffset));
+}
+
+bool
+TabChild::RecvNotifyPartialSessionHistoryActive(const uint32_t& aGlobalLength,
+                                                const uint32_t& aTargetLocalIndex)
+{
+  // nsISHistory uses int32_t
+  if (NS_WARN_IF(aGlobalLength > INT32_MAX || aTargetLocalIndex > INT32_MAX)) {
+    return false;
+  }
+
+  nsCOMPtr<nsISHistory> shistory;
+  mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+  NS_ENSURE_TRUE(shistory, false);
+
+  return NS_SUCCEEDED(shistory->OnPartialSessionHistoryActive(aGlobalLength,
+                                                              aTargetLocalIndex));
+}
+
+bool
+TabChild::RecvNotifyPartialSessionHistoryDeactive()
+{
+  nsCOMPtr<nsISHistory> shistory;
+  mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+  NS_ENSURE_TRUE(shistory, false);
+
+  return NS_SUCCEEDED(shistory->OnPartialSessionHistoryDeactive());
+}
+
+bool
 TabChild::RecvMouseEvent(const nsString& aType,
                          const float&    aX,
                          const float&    aY,
                          const int32_t&  aButton,
                          const int32_t&  aClickCount,
                          const int32_t&  aModifiers,
                          const bool&     aIgnoreRootScrollFrame)
 {
@@ -3338,16 +3405,90 @@ TabChild::ForcePaint(uint64_t aLayerObse
     // message on the PContent channel.
     return;
   }
 
   nsAutoScriptBlocker scriptBlocker;
   RecvSetDocShellIsActive(true, false, aLayerObserverEpoch);
 }
 
+/*******************************************************************************
+ * nsISHistoryListener
+ ******************************************************************************/
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryNewEntry(nsIURI *aNewURI, int32_t aOldIndex)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryGoBack(nsIURI *aBackURI, bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryGoForward(nsIURI *aForwardURI, bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryReload(nsIURI *aReloadURI, uint32_t aReloadFlags, bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryGotoIndex(int32_t aIndex, nsIURI *aGotoURI, bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryPurge(int32_t aNumEntries, bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryReplaceEntry(int32_t aIndex)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnLengthChange(int32_t aCount)
+{
+  RefPtr<TabChild> tabChild(mTabChild);
+  if (!tabChild) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (aCount < 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return tabChild->SendNotifySessionHistoryChange(aCount) ?
+           NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnRequestCrossBrowserNavigation(uint32_t aIndex)
+{
+  RefPtr<TabChild> tabChild(mTabChild);
+  if (!tabChild) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return tabChild->SendRequestCrossBrowserNavigation(aIndex) ?
+           NS_OK : NS_ERROR_FAILURE;
+}
+
 TabChildGlobal::TabChildGlobal(TabChildBase* aTabChild)
 : mTabChild(aTabChild)
 {
   SetIsNotDOMBinding();
 }
 
 TabChildGlobal::~TabChildGlobal()
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -33,16 +33,18 @@
 #include "mozilla/EventForwards.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "nsIWebBrowserChrome3.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "AudioChannelService.h"
 #include "PuppetWidget.h"
 #include "mozilla/layers/GeckoContentController.h"
+#include "nsISHistoryListener.h"
+#include "nsIPartialSHistoryListener.h"
 
 class nsICachedFileDescriptorListener;
 class nsIDOMWindowUtils;
 
 namespace mozilla {
 namespace layout {
 class RenderFrameChild;
 } // namespace layout
@@ -160,16 +162,37 @@ public:
   explicit ContentListener(TabChild* aTabChild) : mTabChild(aTabChild) {}
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMEVENTLISTENER
 protected:
   ~ContentListener() {}
   TabChild* mTabChild;
 };
 
+/**
+ * Listens on session history change, and sends NotifySessionHistoryChange to
+ * parent process.
+ */
+class TabChildSHistoryListener final : public nsISHistoryListener,
+                                       public nsIPartialSHistoryListener,
+                                       public nsSupportsWeakReference
+{
+public:
+  explicit TabChildSHistoryListener(TabChild* aTabChild) : mTabChild(aTabChild) {}
+  void ClearTabChild() { mTabChild = nullptr; }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISHISTORYLISTENER
+  NS_DECL_NSIPARTIALSHISTORYLISTENER
+
+private:
+  ~TabChildSHistoryListener() {}
+  TabChild* mTabChild;
+};
+
 // This is base clase which helps to share Viewport and touch related
 // functionality between b2g/android FF/embedlite clients implementation.
 // It make sense to place in this class all helper functions, and functionality
 // which could be shared between Cross-process/Cross-thread implmentations.
 class TabChildBase : public nsISupports,
                      public nsMessageManagerScriptExecutor,
                      public ipc::MessageManagerCallback
 {
@@ -676,16 +699,23 @@ protected:
   virtual bool RecvSetKeyboardIndicators(const UIStateChangeType& aShowAccelerators,
                                          const UIStateChangeType& aShowFocusRings) override;
 
   virtual bool RecvStopIMEStateManagement() override;
 
   virtual bool RecvMenuKeyboardListenerInstalled(
                  const bool& aInstalled) override;
 
+  virtual bool RecvNotifyAttachGroupedSessionHistory(const uint32_t& aOffset) override;
+
+  virtual bool RecvNotifyPartialSessionHistoryActive(const uint32_t& aGlobalLength,
+                                                     const uint32_t& aTargetLocalIndex) override;
+
+  virtual bool RecvNotifyPartialSessionHistoryDeactive() override;
+
 private:
   void HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
                        const ScrollableLayerGuid& aGuid);
 
   // Notify others that our TabContext has been updated.
   //
   // You should call this after calling TabContext::SetTabContext().  We also
   // call this during Init().
@@ -728,16 +758,17 @@ private:
   class DelayedDeleteRunnable;
 
   TextureFactoryIdentifier mTextureFactoryIdentifier;
   nsCOMPtr<nsIWebNavigation> mWebNav;
   RefPtr<PuppetWidget> mPuppetWidget;
   nsCOMPtr<nsIURI> mLastURI;
   RenderFrameChild* mRemoteFrame;
   RefPtr<nsIContentChild> mManager;
+  RefPtr<TabChildSHistoryListener> mHistoryListener;
   uint32_t mChromeFlags;
   int32_t mActiveSuppressDisplayport;
   uint64_t mLayersId;
   CSSRect mUnscaledOuterRect;
   // Whether we have already received a FileDescriptor for the app package.
   bool mAppPackageFileDescriptorRecved;
   // At present only 1 of these is really expected.
   AutoTArray<nsAutoPtr<CachedFileDescriptorInfo>, 1>
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -95,16 +95,18 @@
 #include "nsILoginManagerPrompter.h"
 #include "nsPIWindowRoot.h"
 #include "nsIAuthPrompt2.h"
 #include "gfxDrawable.h"
 #include "ImageOps.h"
 #include "UnitTransforms.h"
 #include <algorithm>
 #include "mozilla/WebBrowserPersistDocumentParent.h"
+#include "nsIGroupedSHistory.h"
+#include "PartialSHistory.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::services;
 using namespace mozilla::widget;
 using namespace mozilla::jsipc;
@@ -3446,16 +3448,50 @@ TabParent::RecvLookUpDictionary(const ns
     return true;
   }
 
   widget->LookUpDictionary(aText, aFontRangeArray, aIsVertical,
                            aPoint - GetChildProcessOffset());
   return true;
 }
 
+bool
+TabParent::RecvNotifySessionHistoryChange(const uint32_t& aCount)
+{
+  RefPtr<nsFrameLoader> frameLoader(GetFrameLoader());
+  if (!frameLoader) {
+    // FrameLoader can be nullptr if the it is destroying.
+    // In this case session history change can simply be ignored.
+    return true;
+  }
+
+  nsCOMPtr<nsIPartialSHistory> partialHistory;
+  frameLoader->GetPartialSessionHistory(getter_AddRefs(partialHistory));
+  if (!partialHistory) {
+    // PartialSHistory is not enabled
+    return true;
+  }
+
+  partialHistory->OnSessionHistoryChange(aCount);
+  return true;
+}
+
+bool
+TabParent::RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex)
+{
+  RefPtr<nsFrameLoader> frameLoader(GetFrameLoader());
+  if (!frameLoader) {
+    // FrameLoader can be nullptr if the it is destroying.
+    // In this case we can ignore the request.
+    return true;
+  }
+
+  return NS_SUCCEEDED(frameLoader->RequestGroupedHistoryNavigation(aGlobalIndex));
+}
+
 NS_IMETHODIMP
 FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
 {
   nsAuthInformationHolder* holder =
     static_cast<nsAuthInformationHolder*>(aAuthInfo);
 
   if (!net::gNeckoChild->SendOnAuthAvailable(mCallbackId,
                                              holder->User(),
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -28,16 +28,17 @@
 #include "nsISecureBrowserUI.h"
 #include "nsITabParent.h"
 #include "nsIWebBrowserPersistable.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsRefreshDriver.h"
 #include "nsWeakReference.h"
 #include "Units.h"
 #include "nsIWidget.h"
+#include "nsIPartialSHistory.h"
 
 class nsFrameLoader;
 class nsIFrameLoader;
 class nsIContent;
 class nsIPrincipal;
 class nsIURI;
 class nsILoadContext;
 class nsIDocShell;
@@ -625,16 +626,20 @@ protected:
                                  const int32_t& aX, const int32_t& aY,
                                  const int32_t& aCx, const int32_t& aCy) override;
 
   virtual bool RecvGetTabCount(uint32_t* aValue) override;
 
   virtual bool RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
                                                     const bool& aActive) override;
 
+  virtual bool RecvNotifySessionHistoryChange(const uint32_t& aCount) override;
+
+  virtual bool RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex) override;
+
   ContentCacheInParent mContentCache;
 
   nsIntRect mRect;
   ScreenIntSize mDimensions;
   ScreenOrientationInternal mOrientation;
   float mDPI;
   int32_t mRounding;
   CSSToLayoutDeviceScale mDefaultScale;