forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1947 lines
		
	
	
	
		
			65 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1947 lines
		
	
	
	
		
			65 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- 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 "js/Array.h"  // JS::GetArrayLength, JS::IsArrayObject
 | 
						|
#include "js/JSON.h"
 | 
						|
#include "js/Object.h"
 | 
						|
#include "js/PropertyAndElement.h"  // JS_GetElement
 | 
						|
#include "js/TypeDecls.h"
 | 
						|
#include "jsapi.h"
 | 
						|
#include "mozilla/BasePrincipal.h"
 | 
						|
#include "mozilla/ErrorResult.h"
 | 
						|
#include "mozilla/PresShell.h"
 | 
						|
#include "mozilla/dom/AutocompleteInfoBinding.h"
 | 
						|
#include "mozilla/dom/CanonicalBrowsingContext.h"
 | 
						|
#include "mozilla/dom/CustomElementTypes.h"
 | 
						|
#include "mozilla/dom/CustomElementRegistry.h"
 | 
						|
#include "mozilla/dom/Document.h"
 | 
						|
#include "mozilla/dom/DocumentInlines.h"
 | 
						|
#include "mozilla/dom/FormData.h"
 | 
						|
#include "mozilla/dom/FragmentOrElement.h"
 | 
						|
#include "mozilla/dom/HTMLElement.h"
 | 
						|
#include "mozilla/dom/HTMLInputElement.h"
 | 
						|
#include "mozilla/dom/HTMLSelectElement.h"
 | 
						|
#include "mozilla/dom/HTMLTextAreaElement.h"
 | 
						|
#include "mozilla/dom/RootedDictionary.h"
 | 
						|
#include "mozilla/dom/SessionStorageManager.h"
 | 
						|
#include "mozilla/dom/PBackgroundSessionStorageCache.h"
 | 
						|
#include "mozilla/dom/SessionStoreChangeListener.h"
 | 
						|
#include "mozilla/dom/SessionStoreChild.h"
 | 
						|
#include "mozilla/dom/SessionStoreUtils.h"
 | 
						|
#include "mozilla/dom/SessionStoreUtilsBinding.h"
 | 
						|
#include "mozilla/dom/ToJSValue.h"
 | 
						|
#include "mozilla/dom/UnionTypes.h"
 | 
						|
#include "mozilla/dom/sessionstore/SessionStoreTypes.h"
 | 
						|
#include "mozilla/dom/txIXPathContext.h"
 | 
						|
#include "mozilla/dom/WindowGlobalParent.h"
 | 
						|
#include "mozilla/dom/WindowProxyHolder.h"
 | 
						|
#include "mozilla/dom/XPathResult.h"
 | 
						|
#include "mozilla/dom/XPathEvaluator.h"
 | 
						|
#include "mozilla/dom/XPathExpression.h"
 | 
						|
#include "mozilla/dom/PBackgroundSessionStorageCache.h"
 | 
						|
#include "mozilla/ipc/BackgroundUtils.h"
 | 
						|
#include "mozilla/ReverseIterator.h"
 | 
						|
#include "mozilla/UniquePtr.h"
 | 
						|
#include "nsCharSeparatedTokenizer.h"
 | 
						|
#include "nsContentList.h"
 | 
						|
#include "nsContentUtils.h"
 | 
						|
#include "nsFocusManager.h"
 | 
						|
#include "nsGlobalWindowInner.h"
 | 
						|
#include "nsIContentInlines.h"
 | 
						|
#include "nsIDocShell.h"
 | 
						|
#include "nsIFormControl.h"
 | 
						|
#include "nsIScrollableFrame.h"
 | 
						|
#include "nsISHistory.h"
 | 
						|
#include "nsIXULRuntime.h"
 | 
						|
#include "nsPresContext.h"
 | 
						|
#include "nsPrintfCString.h"
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
using namespace mozilla::dom;
 | 
						|
using namespace mozilla::dom::sessionstore;
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
class DynamicFrameEventFilter final : public nsIDOMEventListener {
 | 
						|
 public:
 | 
						|
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 | 
						|
  NS_DECL_CYCLE_COLLECTION_CLASS(DynamicFrameEventFilter)
 | 
						|
 | 
						|
  explicit DynamicFrameEventFilter(EventListener* aListener)
 | 
						|
      : mListener(aListener) {}
 | 
						|
 | 
						|
  NS_IMETHODIMP HandleEvent(Event* aEvent) override {
 | 
						|
    if (mListener && TargetInNonDynamicDocShell(aEvent)) {
 | 
						|
      mListener->HandleEvent(*aEvent);
 | 
						|
    }
 | 
						|
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  ~DynamicFrameEventFilter() = default;
 | 
						|
 | 
						|
  bool TargetInNonDynamicDocShell(Event* aEvent) {
 | 
						|
    EventTarget* target = aEvent->GetTarget();
 | 
						|
    if (!target) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    nsPIDOMWindowOuter* outer = target->GetOwnerGlobalForBindingsInternal();
 | 
						|
    if (!outer || !outer->GetDocShell()) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    RefPtr<BrowsingContext> context = outer->GetBrowsingContext();
 | 
						|
    return context && !context->CreatedDynamically();
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<EventListener> mListener;
 | 
						|
};
 | 
						|
 | 
						|
NS_IMPL_CYCLE_COLLECTION(DynamicFrameEventFilter, mListener)
 | 
						|
 | 
						|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DynamicFrameEventFilter)
 | 
						|
  NS_INTERFACE_MAP_ENTRY(nsISupports)
 | 
						|
  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
 | 
						|
NS_INTERFACE_MAP_END
 | 
						|
 | 
						|
NS_IMPL_CYCLE_COLLECTING_ADDREF(DynamicFrameEventFilter)
 | 
						|
NS_IMPL_CYCLE_COLLECTING_RELEASE(DynamicFrameEventFilter)
 | 
						|
 | 
						|
}  // anonymous namespace
 | 
						|
 | 
						|
/* static */
 | 
						|
void SessionStoreUtils::ForEachNonDynamicChildFrame(
 | 
						|
    const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
 | 
						|
    SessionStoreUtilsFrameCallback& aCallback, ErrorResult& aRv) {
 | 
						|
  if (!aWindow.get()) {
 | 
						|
    aRv.Throw(NS_ERROR_INVALID_ARG);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIDocShell> docShell = aWindow.get()->GetDocShell();
 | 
						|
  if (!docShell) {
 | 
						|
    aRv.Throw(NS_ERROR_FAILURE);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  int32_t length;
 | 
						|
  aRv = docShell->GetInProcessChildCount(&length);
 | 
						|
  if (aRv.Failed()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  for (int32_t i = 0; i < length; ++i) {
 | 
						|
    nsCOMPtr<nsIDocShellTreeItem> item;
 | 
						|
    docShell->GetInProcessChildAt(i, getter_AddRefs(item));
 | 
						|
    if (!item) {
 | 
						|
      aRv.Throw(NS_ERROR_FAILURE);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    RefPtr<BrowsingContext> context = item->GetBrowsingContext();
 | 
						|
    if (!context) {
 | 
						|
      aRv.Throw(NS_ERROR_FAILURE);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!context->CreatedDynamically()) {
 | 
						|
      int32_t childOffset = context->ChildOffset();
 | 
						|
      aCallback.Call(WindowProxyHolder(context.forget()), childOffset);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
already_AddRefed<nsISupports>
 | 
						|
SessionStoreUtils::AddDynamicFrameFilteredListener(
 | 
						|
    const GlobalObject& aGlobal, EventTarget& aTarget, const nsAString& aType,
 | 
						|
    JS::Handle<JS::Value> aListener, bool aUseCapture, bool aMozSystemGroup,
 | 
						|
    ErrorResult& aRv) {
 | 
						|
  if (NS_WARN_IF(!aListener.isObject())) {
 | 
						|
    aRv.Throw(NS_ERROR_INVALID_ARG);
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  JSContext* cx = aGlobal.Context();
 | 
						|
  JS::Rooted<JSObject*> obj(cx, &aListener.toObject());
 | 
						|
  JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
 | 
						|
  RefPtr<EventListener> listener =
 | 
						|
      new EventListener(cx, obj, global, GetIncumbentGlobal());
 | 
						|
 | 
						|
  nsCOMPtr<nsIDOMEventListener> filter(new DynamicFrameEventFilter(listener));
 | 
						|
  if (aMozSystemGroup) {
 | 
						|
    aRv = aTarget.AddSystemEventListener(aType, filter, aUseCapture);
 | 
						|
  } else {
 | 
						|
    aRv = aTarget.AddEventListener(aType, filter, aUseCapture);
 | 
						|
  }
 | 
						|
  if (aRv.Failed()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  return filter.forget();
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
void SessionStoreUtils::RemoveDynamicFrameFilteredListener(
 | 
						|
    const GlobalObject& global, EventTarget& aTarget, const nsAString& aType,
 | 
						|
    nsISupports* aListener, bool aUseCapture, bool aMozSystemGroup,
 | 
						|
    ErrorResult& aRv) {
 | 
						|
  nsCOMPtr<nsIDOMEventListener> listener = do_QueryInterface(aListener);
 | 
						|
  if (!listener) {
 | 
						|
    aRv.Throw(NS_ERROR_NO_INTERFACE);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aMozSystemGroup) {
 | 
						|
    aTarget.RemoveSystemEventListener(aType, listener, aUseCapture);
 | 
						|
  } else {
 | 
						|
    aTarget.RemoveEventListener(aType, listener, aUseCapture);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
void SessionStoreUtils::CollectDocShellCapabilities(const GlobalObject& aGlobal,
 | 
						|
                                                    nsIDocShell* aDocShell,
 | 
						|
                                                    nsCString& aRetVal) {
 | 
						|
  bool allow;
 | 
						|
 | 
						|
#define TRY_ALLOWPROP(y)          \
 | 
						|
  PR_BEGIN_MACRO                  \
 | 
						|
  aDocShell->GetAllow##y(&allow); \
 | 
						|
  if (!allow) {                   \
 | 
						|
    if (!aRetVal.IsEmpty()) {     \
 | 
						|
      aRetVal.Append(',');        \
 | 
						|
    }                             \
 | 
						|
    aRetVal.Append(#y);           \
 | 
						|
  }                               \
 | 
						|
  PR_END_MACRO
 | 
						|
 | 
						|
  TRY_ALLOWPROP(Plugins);
 | 
						|
  // Bug 1328013 : Don't collect "AllowJavascript" property
 | 
						|
  // TRY_ALLOWPROP(Javascript);
 | 
						|
  TRY_ALLOWPROP(MetaRedirects);
 | 
						|
  TRY_ALLOWPROP(Subframes);
 | 
						|
  TRY_ALLOWPROP(Images);
 | 
						|
  TRY_ALLOWPROP(Media);
 | 
						|
  TRY_ALLOWPROP(DNSPrefetch);
 | 
						|
  TRY_ALLOWPROP(WindowControl);
 | 
						|
  TRY_ALLOWPROP(Auth);
 | 
						|
  TRY_ALLOWPROP(ContentRetargeting);
 | 
						|
  TRY_ALLOWPROP(ContentRetargetingOnChildren);
 | 
						|
#undef TRY_ALLOWPROP
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
void SessionStoreUtils::RestoreDocShellCapabilities(
 | 
						|
    nsIDocShell* aDocShell, const nsCString& aDisallowCapabilities) {
 | 
						|
  aDocShell->SetAllowPlugins(true);
 | 
						|
  aDocShell->SetAllowMetaRedirects(true);
 | 
						|
  aDocShell->SetAllowSubframes(true);
 | 
						|
  aDocShell->SetAllowImages(true);
 | 
						|
  aDocShell->SetAllowMedia(true);
 | 
						|
  aDocShell->SetAllowDNSPrefetch(true);
 | 
						|
  aDocShell->SetAllowWindowControl(true);
 | 
						|
  aDocShell->SetAllowContentRetargeting(true);
 | 
						|
  aDocShell->SetAllowContentRetargetingOnChildren(true);
 | 
						|
 | 
						|
  bool allowJavascript = true;
 | 
						|
  for (const nsACString& token :
 | 
						|
       nsCCharSeparatedTokenizer(aDisallowCapabilities, ',').ToRange()) {
 | 
						|
    if (token.EqualsLiteral("Plugins")) {
 | 
						|
      aDocShell->SetAllowPlugins(false);
 | 
						|
    } else if (token.EqualsLiteral("Javascript")) {
 | 
						|
      allowJavascript = false;
 | 
						|
    } else if (token.EqualsLiteral("MetaRedirects")) {
 | 
						|
      aDocShell->SetAllowMetaRedirects(false);
 | 
						|
    } else if (token.EqualsLiteral("Subframes")) {
 | 
						|
      aDocShell->SetAllowSubframes(false);
 | 
						|
    } else if (token.EqualsLiteral("Images")) {
 | 
						|
      aDocShell->SetAllowImages(false);
 | 
						|
    } else if (token.EqualsLiteral("Media")) {
 | 
						|
      aDocShell->SetAllowMedia(false);
 | 
						|
    } else if (token.EqualsLiteral("DNSPrefetch")) {
 | 
						|
      aDocShell->SetAllowDNSPrefetch(false);
 | 
						|
    } else if (token.EqualsLiteral("WindowControl")) {
 | 
						|
      aDocShell->SetAllowWindowControl(false);
 | 
						|
    } else if (token.EqualsLiteral("ContentRetargeting")) {
 | 
						|
      bool allow;
 | 
						|
      aDocShell->GetAllowContentRetargetingOnChildren(&allow);
 | 
						|
      aDocShell->SetAllowContentRetargeting(
 | 
						|
          false);  // will also set AllowContentRetargetingOnChildren
 | 
						|
      aDocShell->SetAllowContentRetargetingOnChildren(
 | 
						|
          allow);  // restore the allowProp to original
 | 
						|
    } else if (token.EqualsLiteral("ContentRetargetingOnChildren")) {
 | 
						|
      aDocShell->SetAllowContentRetargetingOnChildren(false);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!mozilla::SessionHistoryInParent()) {
 | 
						|
    // With SessionHistoryInParent, this is set from the parent process.
 | 
						|
    BrowsingContext* bc = aDocShell->GetBrowsingContext();
 | 
						|
    Unused << bc->SetAllowJavascript(allowJavascript);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void CollectCurrentScrollPosition(JSContext* aCx, Document& aDocument,
 | 
						|
                                         Nullable<CollectedData>& aRetVal) {
 | 
						|
  PresShell* presShell = aDocument.GetPresShell();
 | 
						|
  if (!presShell) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  nsPoint scrollPos = presShell->GetVisualViewportOffset();
 | 
						|
  int scrollX = nsPresContext::AppUnitsToIntCSSPixels(scrollPos.x);
 | 
						|
  int scrollY = nsPresContext::AppUnitsToIntCSSPixels(scrollPos.y);
 | 
						|
 | 
						|
  if ((scrollX != 0) || (scrollY != 0)) {
 | 
						|
    aRetVal.SetValue().mScroll.Construct() =
 | 
						|
        nsPrintfCString("%d,%d", scrollX, scrollY);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
void SessionStoreUtils::RestoreScrollPosition(const GlobalObject& aGlobal,
 | 
						|
                                              nsGlobalWindowInner& aWindow,
 | 
						|
                                              const CollectedData& aData) {
 | 
						|
  if (aData.mScroll.WasPassed()) {
 | 
						|
    RestoreScrollPosition(aWindow, aData.mScroll.Value());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
void SessionStoreUtils::RestoreScrollPosition(
 | 
						|
    nsGlobalWindowInner& aWindow, const nsCString& aScrollPosition) {
 | 
						|
  using Change = mozilla::dom::SessionStoreChangeListener::Change;
 | 
						|
  SessionStoreChangeListener::CollectSessionStoreData(
 | 
						|
      aWindow.GetWindowContext(), EnumSet<Change>(Change::Scroll));
 | 
						|
 | 
						|
  nsCCharSeparatedTokenizer tokenizer(aScrollPosition, ',');
 | 
						|
  nsAutoCString token(tokenizer.nextToken());
 | 
						|
  int pos_X = atoi(token.get());
 | 
						|
  token = tokenizer.nextToken();
 | 
						|
  int pos_Y = atoi(token.get());
 | 
						|
 | 
						|
  aWindow.ScrollTo(pos_X, pos_Y);
 | 
						|
 | 
						|
  if (nsCOMPtr<Document> doc = aWindow.GetExtantDoc()) {
 | 
						|
    if (nsPresContext* presContext = doc->GetPresContext()) {
 | 
						|
      if (presContext->IsRootContentDocumentCrossProcess()) {
 | 
						|
        // Use eMainThread so this takes precedence over session history
 | 
						|
        // (ScrollFrameHelper::ScrollToRestoredPosition()).
 | 
						|
        presContext->PresShell()->ScrollToVisual(
 | 
						|
            CSSPoint::ToAppUnits(CSSPoint(pos_X, pos_Y)),
 | 
						|
            layers::FrameMetrics::eMainThread, ScrollMode::Instant);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// Implements the Luhn checksum algorithm as described at
 | 
						|
// http://wikipedia.org/wiki/Luhn_algorithm
 | 
						|
// Number digit lengths vary with network, but should fall within 12-19 range.
 | 
						|
// [2] More details at https://en.wikipedia.org/wiki/Payment_card_number
 | 
						|
static bool IsValidCCNumber(nsAString& aValue) {
 | 
						|
  uint32_t total = 0;
 | 
						|
  uint32_t numLength = 0;
 | 
						|
  uint32_t strLen = aValue.Length();
 | 
						|
  for (uint32_t i = 0; i < strLen; ++i) {
 | 
						|
    uint32_t idx = strLen - i - 1;
 | 
						|
    // ignore whitespace and dashes)
 | 
						|
    char16_t chr = aValue[idx];
 | 
						|
    if (IsSpaceCharacter(chr) || chr == '-') {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    // If our number is too long, note that fact
 | 
						|
    ++numLength;
 | 
						|
    if (numLength > 19) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    // Try to parse the character as a base-10 integer.
 | 
						|
    nsresult rv = NS_OK;
 | 
						|
    uint32_t val = Substring(aValue, idx, 1).ToInteger(&rv, 10);
 | 
						|
    if (NS_FAILED(rv)) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    if (i % 2 == 1) {
 | 
						|
      val *= 2;
 | 
						|
      if (val > 9) {
 | 
						|
        val -= 9;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    total += val;
 | 
						|
  }
 | 
						|
 | 
						|
  return numLength >= 12 && total % 10 == 0;
 | 
						|
}
 | 
						|
 | 
						|
// Limit the number of XPath expressions for performance reasons. See bug
 | 
						|
// 477564.
 | 
						|
static const uint16_t kMaxTraversedXPaths = 100;
 | 
						|
 | 
						|
// A helper function to append a element into mId or mXpath of CollectedData
 | 
						|
static Record<nsString, OwningStringOrBooleanOrObject>::EntryType*
 | 
						|
AppendEntryToCollectedData(nsINode* aNode, const nsAString& aId,
 | 
						|
                           uint16_t& aGeneratedCount,
 | 
						|
                           Nullable<CollectedData>& aRetVal) {
 | 
						|
  Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry;
 | 
						|
  if (!aId.IsEmpty()) {
 | 
						|
    if (!aRetVal.SetValue().mId.WasPassed()) {
 | 
						|
      aRetVal.SetValue().mId.Construct();
 | 
						|
    }
 | 
						|
    auto& recordEntries = aRetVal.SetValue().mId.Value().Entries();
 | 
						|
    entry = recordEntries.AppendElement();
 | 
						|
    entry->mKey = aId;
 | 
						|
  } else {
 | 
						|
    if (!aRetVal.SetValue().mXpath.WasPassed()) {
 | 
						|
      aRetVal.SetValue().mXpath.Construct();
 | 
						|
    }
 | 
						|
    auto& recordEntries = aRetVal.SetValue().mXpath.Value().Entries();
 | 
						|
    entry = recordEntries.AppendElement();
 | 
						|
    nsAutoString xpath;
 | 
						|
    aNode->GenerateXPath(xpath);
 | 
						|
    aGeneratedCount++;
 | 
						|
    entry->mKey = xpath;
 | 
						|
  }
 | 
						|
  return entry;
 | 
						|
}
 | 
						|
 | 
						|
/* for bool value */
 | 
						|
static void AppendValueToCollectedData(nsINode* aNode, const nsAString& aId,
 | 
						|
                                       const bool& aValue,
 | 
						|
                                       uint16_t& aGeneratedCount,
 | 
						|
                                       JSContext* aCx,
 | 
						|
                                       Nullable<CollectedData>& aRetVal) {
 | 
						|
  Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
 | 
						|
      AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
 | 
						|
  entry->mValue.SetAsBoolean() = aValue;
 | 
						|
}
 | 
						|
 | 
						|
/* for nsString value */
 | 
						|
static void AppendValueToCollectedData(nsINode* aNode, const nsAString& aId,
 | 
						|
                                       const nsString& aValue,
 | 
						|
                                       uint16_t& aGeneratedCount,
 | 
						|
                                       Nullable<CollectedData>& aRetVal) {
 | 
						|
  Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
 | 
						|
      AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
 | 
						|
  entry->mValue.SetAsString() = aValue;
 | 
						|
}
 | 
						|
 | 
						|
/* for single select value */
 | 
						|
static void AppendValueToCollectedData(
 | 
						|
    nsINode* aNode, const nsAString& aId,
 | 
						|
    const CollectedNonMultipleSelectValue& aValue, uint16_t& aGeneratedCount,
 | 
						|
    JSContext* aCx, Nullable<CollectedData>& aRetVal) {
 | 
						|
  JS::Rooted<JS::Value> jsval(aCx);
 | 
						|
  if (!ToJSValue(aCx, aValue, &jsval)) {
 | 
						|
    JS_ClearPendingException(aCx);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
 | 
						|
      AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
 | 
						|
  entry->mValue.SetAsObject() = &jsval.toObject();
 | 
						|
}
 | 
						|
 | 
						|
/* special handing for input element with string type */
 | 
						|
static void AppendValueToCollectedData(Document& aDocument, nsINode* aNode,
 | 
						|
                                       const nsAString& aId,
 | 
						|
                                       const nsString& aValue,
 | 
						|
                                       uint16_t& aGeneratedCount,
 | 
						|
                                       JSContext* aCx,
 | 
						|
                                       Nullable<CollectedData>& aRetVal) {
 | 
						|
  if (!aId.IsEmpty()) {
 | 
						|
    // We want to avoid saving data for about:sessionrestore as a string.
 | 
						|
    // Since it's stored in the form as stringified JSON, stringifying
 | 
						|
    // further causes an explosion of escape characters. cf. bug 467409
 | 
						|
    if (aId.EqualsLiteral("sessionData")) {
 | 
						|
      nsAutoCString url;
 | 
						|
      Unused << aDocument.GetDocumentURI()->GetSpecIgnoringRef(url);
 | 
						|
      if (url.EqualsLiteral("about:sessionrestore") ||
 | 
						|
          url.EqualsLiteral("about:welcomeback")) {
 | 
						|
        JS::Rooted<JS::Value> jsval(aCx);
 | 
						|
        if (JS_ParseJSON(aCx, aValue.get(), aValue.Length(), &jsval) &&
 | 
						|
            jsval.isObject()) {
 | 
						|
          Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
 | 
						|
              AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
 | 
						|
          entry->mValue.SetAsObject() = &jsval.toObject();
 | 
						|
        } else {
 | 
						|
          JS_ClearPendingException(aCx);
 | 
						|
        }
 | 
						|
        return;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  AppendValueToCollectedData(aNode, aId, aValue, aGeneratedCount, aRetVal);
 | 
						|
}
 | 
						|
 | 
						|
/* for nsTArray<nsString>: file and multipleSelect */
 | 
						|
static void AppendValueToCollectedData(nsINode* aNode, const nsAString& aId,
 | 
						|
                                       const nsAString& aValueType,
 | 
						|
                                       nsTArray<nsString>& aValue,
 | 
						|
                                       uint16_t& aGeneratedCount,
 | 
						|
                                       JSContext* aCx,
 | 
						|
                                       Nullable<CollectedData>& aRetVal) {
 | 
						|
  JS::Rooted<JS::Value> jsval(aCx);
 | 
						|
  if (aValueType.EqualsLiteral("file")) {
 | 
						|
    CollectedFileListValue val;
 | 
						|
    val.mType = aValueType;
 | 
						|
    val.mFileList = std::move(aValue);
 | 
						|
    if (!ToJSValue(aCx, val, &jsval)) {
 | 
						|
      JS_ClearPendingException(aCx);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    if (!ToJSValue(aCx, aValue, &jsval)) {
 | 
						|
      JS_ClearPendingException(aCx);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
 | 
						|
      AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
 | 
						|
  entry->mValue.SetAsObject() = &jsval.toObject();
 | 
						|
}
 | 
						|
 | 
						|
/* For form-associated custom element state */
 | 
						|
static void AppendValueToCollectedData(
 | 
						|
    nsINode* aNode, const nsAString& aId,
 | 
						|
    const Nullable<OwningFileOrUSVStringOrFormData>& aValue,
 | 
						|
    const Nullable<OwningFileOrUSVStringOrFormData>& aState,
 | 
						|
    uint16_t& aGeneratedCount, JSContext* aCx,
 | 
						|
    Nullable<CollectedData>& aRetVal) {
 | 
						|
  Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
 | 
						|
      AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
 | 
						|
 | 
						|
  CollectedCustomElementValue val;
 | 
						|
  val.mValue = aValue;
 | 
						|
  val.mState = aState;
 | 
						|
  JS::Rooted<JS::Value> jsval(aCx);
 | 
						|
  if (!ToJSValue(aCx, val, &jsval)) {
 | 
						|
    JS_ClearPendingException(aCx);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  entry->mValue.SetAsObject() = &jsval.toObject();
 | 
						|
}
 | 
						|
 | 
						|
// This isn't size as in binary size, just a heuristic to not store too large
 | 
						|
// fields in session store. See StaticPrefs::browser_sessionstore_dom_form_limit
 | 
						|
static uint32_t SizeOfFormEntry(const FormEntryValue& aValue) {
 | 
						|
  uint32_t size = 0;
 | 
						|
  switch (aValue.type()) {
 | 
						|
    case FormEntryValue::TCheckbox:
 | 
						|
      size = aValue.get_Checkbox().value() ? 4 : 5;
 | 
						|
      break;
 | 
						|
    case FormEntryValue::TTextField:
 | 
						|
      size = aValue.get_TextField().value().Length();
 | 
						|
      break;
 | 
						|
    case FormEntryValue::TFileList: {
 | 
						|
      for (const auto& value : aValue.get_FileList().valueList()) {
 | 
						|
        size += value.Length();
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case FormEntryValue::TSingleSelect:
 | 
						|
      size = aValue.get_SingleSelect().value().Length();
 | 
						|
      break;
 | 
						|
    case FormEntryValue::TMultipleSelect: {
 | 
						|
      for (const auto& value : aValue.get_MultipleSelect().valueList()) {
 | 
						|
        size += value.Length();
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case FormEntryValue::TCustomElementTuple: {
 | 
						|
      auto customElementTupleSize =
 | 
						|
          [](const CustomElementFormValue& value) -> uint32_t {
 | 
						|
        switch (value.type()) {
 | 
						|
          case CustomElementFormValue::TBlobImpl:
 | 
						|
            return value.get_BlobImpl()->GetAllocationSize();
 | 
						|
          case CustomElementFormValue::TnsString:
 | 
						|
            return value.get_nsString().Length();
 | 
						|
          case CustomElementFormValue::TArrayOfFormDataTuple: {
 | 
						|
            uint32_t formDataSize = 0;
 | 
						|
            for (const auto& entry : value.get_ArrayOfFormDataTuple()) {
 | 
						|
              formDataSize += entry.name().Length();
 | 
						|
              const auto& entryValue = entry.value();
 | 
						|
              switch (entryValue.type()) {
 | 
						|
                case FormDataValue::TBlobImpl:
 | 
						|
                  formDataSize +=
 | 
						|
                      entryValue.get_BlobImpl()->GetAllocationSize();
 | 
						|
                  break;
 | 
						|
                case FormDataValue::TnsString:
 | 
						|
                  formDataSize += entryValue.get_nsString().Length();
 | 
						|
                  break;
 | 
						|
                default:
 | 
						|
                  break;
 | 
						|
              }
 | 
						|
            }
 | 
						|
            return formDataSize;
 | 
						|
          }
 | 
						|
          default:
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
      };
 | 
						|
 | 
						|
      auto ceTuple = aValue.get_CustomElementTuple();
 | 
						|
      size += customElementTupleSize(ceTuple.value());
 | 
						|
      size += customElementTupleSize(ceTuple.state());
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t AppendEntry(nsINode* aNode, const nsString& aId,
 | 
						|
                            const FormEntryValue& aValue,
 | 
						|
                            sessionstore::FormData& aFormData) {
 | 
						|
  uint32_t size = SizeOfFormEntry(aValue);
 | 
						|
  if (size > StaticPrefs::browser_sessionstore_dom_form_limit()) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aId.IsEmpty()) {
 | 
						|
    FormEntry* entry = aFormData.xpath().AppendElement();
 | 
						|
    entry->value() = aValue;
 | 
						|
    aNode->GenerateXPath(entry->id());
 | 
						|
    size += entry->id().Length();
 | 
						|
  } else {
 | 
						|
    aFormData.id().AppendElement(FormEntry{aId, aValue});
 | 
						|
    size += aId.Length();
 | 
						|
  }
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t CollectTextAreaElement(Document* aDocument,
 | 
						|
                                       sessionstore::FormData& aFormData) {
 | 
						|
  uint32_t size = 0;
 | 
						|
  RefPtr<nsContentList> textlist =
 | 
						|
      NS_GetContentList(aDocument, kNameSpaceID_XHTML, u"textarea"_ns);
 | 
						|
  uint32_t length = textlist->Length();
 | 
						|
  for (uint32_t i = 0; i < length; ++i) {
 | 
						|
    MOZ_ASSERT(textlist->Item(i), "null item in node list!");
 | 
						|
 | 
						|
    auto* textArea = HTMLTextAreaElement::FromNodeOrNull(textlist->Item(i));
 | 
						|
    if (!textArea) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    DOMString autocomplete;
 | 
						|
    textArea->GetAutocomplete(autocomplete);
 | 
						|
    if (autocomplete.AsAString().EqualsLiteral("off")) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    nsAutoString id;
 | 
						|
    textArea->GetId(id);
 | 
						|
    if (id.IsEmpty() && (aFormData.xpath().Length() > kMaxTraversedXPaths)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    nsString value;
 | 
						|
    textArea->GetValue(value);
 | 
						|
    // In order to reduce XPath generation (which is slow), we only save data
 | 
						|
    // for form fields that have been changed. (cf. bug 537289)
 | 
						|
    if (textArea->AttrValueIs(kNameSpaceID_None, nsGkAtoms::value, value,
 | 
						|
                              eCaseMatters)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    size += AppendEntry(textArea, id, TextField{value}, aFormData);
 | 
						|
  }
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t CollectInputElement(Document* aDocument,
 | 
						|
                                    sessionstore::FormData& aFormData) {
 | 
						|
  uint32_t size = 0;
 | 
						|
  RefPtr<nsContentList> inputlist =
 | 
						|
      NS_GetContentList(aDocument, kNameSpaceID_XHTML, u"input"_ns);
 | 
						|
  uint32_t length = inputlist->Length();
 | 
						|
  for (uint32_t i = 0; i < length; ++i) {
 | 
						|
    MOZ_ASSERT(inputlist->Item(i), "null item in node list!");
 | 
						|
    nsCOMPtr<nsIFormControl> formControl =
 | 
						|
        do_QueryInterface(inputlist->Item(i));
 | 
						|
    if (formControl) {
 | 
						|
      auto controlType = formControl->ControlType();
 | 
						|
      if (controlType == FormControlType::InputPassword ||
 | 
						|
          controlType == FormControlType::InputHidden ||
 | 
						|
          controlType == FormControlType::InputButton ||
 | 
						|
          controlType == FormControlType::InputImage ||
 | 
						|
          controlType == FormControlType::InputSubmit ||
 | 
						|
          controlType == FormControlType::InputReset) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    RefPtr<HTMLInputElement> input =
 | 
						|
        HTMLInputElement::FromNodeOrNull(inputlist->Item(i));
 | 
						|
    if (!input || !nsContentUtils::IsAutocompleteEnabled(input)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    nsAutoString id;
 | 
						|
    input->GetId(id);
 | 
						|
    if (id.IsEmpty() && (aFormData.xpath().Length() > kMaxTraversedXPaths)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    Nullable<AutocompleteInfo> aInfo;
 | 
						|
    input->GetAutocompleteInfo(aInfo);
 | 
						|
    if (!aInfo.IsNull() && !aInfo.Value().mCanAutomaticallyPersist) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    FormEntryValue value;
 | 
						|
    if (input->ControlType() == FormControlType::InputCheckbox ||
 | 
						|
        input->ControlType() == FormControlType::InputRadio) {
 | 
						|
      bool checked = input->Checked();
 | 
						|
      if (checked == input->DefaultChecked()) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      size += AppendEntry(input, id, Checkbox{checked}, aFormData);
 | 
						|
    } else if (input->ControlType() == FormControlType::InputFile) {
 | 
						|
      IgnoredErrorResult rv;
 | 
						|
      sessionstore::FileList file;
 | 
						|
      input->MozGetFileNameArray(file.valueList(), rv);
 | 
						|
      if (rv.Failed() || file.valueList().IsEmpty()) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      size += AppendEntry(input, id, file, aFormData);
 | 
						|
    } else {
 | 
						|
      TextField field;
 | 
						|
      input->GetValue(field.value(), CallerType::System);
 | 
						|
      auto& value = field.value();
 | 
						|
      // In order to reduce XPath generation (which is slow), we only save data
 | 
						|
      // for form fields that have been changed. (cf. bug 537289)
 | 
						|
      // Also, don't want to collect credit card number.
 | 
						|
      if (value.IsEmpty() || IsValidCCNumber(value) ||
 | 
						|
          input->HasBeenTypePassword() ||
 | 
						|
          input->AttrValueIs(kNameSpaceID_None, nsGkAtoms::value, value,
 | 
						|
                             eCaseMatters)) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      size += AppendEntry(input, id, field, aFormData);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t CollectSelectElement(Document* aDocument,
 | 
						|
                                     sessionstore::FormData& aFormData) {
 | 
						|
  uint32_t size = 0;
 | 
						|
  RefPtr<nsContentList> selectlist =
 | 
						|
      NS_GetContentList(aDocument, kNameSpaceID_XHTML, u"select"_ns);
 | 
						|
  uint32_t length = selectlist->Length();
 | 
						|
  for (uint32_t i = 0; i < length; ++i) {
 | 
						|
    MOZ_ASSERT(selectlist->Item(i), "null item in node list!");
 | 
						|
    RefPtr<HTMLSelectElement> select =
 | 
						|
        HTMLSelectElement::FromNodeOrNull(selectlist->Item(i));
 | 
						|
    if (!select) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    nsAutoString id;
 | 
						|
    select->GetId(id);
 | 
						|
    if (id.IsEmpty() && (aFormData.xpath().Length() > kMaxTraversedXPaths)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    AutocompleteInfo aInfo;
 | 
						|
    select->GetAutocompleteInfo(aInfo);
 | 
						|
    if (!aInfo.mCanAutomaticallyPersist) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!select->Multiple()) {
 | 
						|
      HTMLOptionsCollection* options = select->GetOptions();
 | 
						|
      if (!options) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      uint32_t numOptions = options->Length();
 | 
						|
      int32_t defaultIndex = 0;
 | 
						|
      for (uint32_t idx = 0; idx < numOptions; idx++) {
 | 
						|
        HTMLOptionElement* option = options->ItemAsOption(idx);
 | 
						|
        if (option->DefaultSelected()) {
 | 
						|
          defaultIndex = option->Index();
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      int32_t selectedIndex = select->SelectedIndex();
 | 
						|
      if (selectedIndex == defaultIndex || selectedIndex < 0) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      DOMString selectVal;
 | 
						|
      select->GetValue(selectVal);
 | 
						|
      size += AppendEntry(select, id,
 | 
						|
                          SingleSelect{static_cast<uint32_t>(selectedIndex),
 | 
						|
                                       selectVal.AsAString()},
 | 
						|
                          aFormData);
 | 
						|
    } else {
 | 
						|
      HTMLOptionsCollection* options = select->GetOptions();
 | 
						|
      if (!options) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      bool hasDefaultValue = true;
 | 
						|
      nsTArray<nsString> selectslist;
 | 
						|
      uint32_t numOptions = options->Length();
 | 
						|
      for (uint32_t idx = 0; idx < numOptions; idx++) {
 | 
						|
        HTMLOptionElement* option = options->ItemAsOption(idx);
 | 
						|
        bool selected = option->Selected();
 | 
						|
 | 
						|
        hasDefaultValue =
 | 
						|
            hasDefaultValue && (selected == option->DefaultSelected());
 | 
						|
 | 
						|
        if (!selected) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
        option->GetValue(*selectslist.AppendElement());
 | 
						|
      }
 | 
						|
      // In order to reduce XPath generation (which is slow), we only save data
 | 
						|
      // for form fields that have been changed. (cf. bug 537289)
 | 
						|
      if (hasDefaultValue) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      size += AppendEntry(select, id, MultipleSelect{selectslist}, aFormData);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
static already_AddRefed<nsContentList> GetFormAssociatedCustomElements(
 | 
						|
    nsINode* aRootNode) {
 | 
						|
  MOZ_ASSERT(aRootNode, "Content list has to have a root");
 | 
						|
 | 
						|
  auto matchFunc = [](Element* aElement, int32_t aNamespace, nsAtom* aAtom,
 | 
						|
                      void* aData) -> bool {
 | 
						|
    return aElement->HasCustomElementData() &&
 | 
						|
           aElement->GetCustomElementData()->IsFormAssociated();
 | 
						|
  };
 | 
						|
  RefPtr<nsContentList> list =
 | 
						|
      new nsContentList(aRootNode, matchFunc, nullptr, nullptr);
 | 
						|
  return list.forget();
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t CollectFormAssociatedCustomElement(
 | 
						|
    Document* aDocument, sessionstore::FormData& aFormData) {
 | 
						|
  uint32_t size = 0;
 | 
						|
  RefPtr<nsContentList> faceList = GetFormAssociatedCustomElements(aDocument);
 | 
						|
  uint32_t length = faceList->Length();
 | 
						|
  for (uint32_t i = 0; i < length; ++i) {
 | 
						|
    MOZ_ASSERT(faceList->Item(i), "null item in node list!");
 | 
						|
    RefPtr<Element> element = Element::FromNode(faceList->Item(i));
 | 
						|
 | 
						|
    nsAutoString id;
 | 
						|
    element->GetId(id);
 | 
						|
    if (id.IsEmpty() && (aFormData.xpath().Length() > kMaxTraversedXPaths)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    const auto* internals =
 | 
						|
        element->GetCustomElementData()->GetElementInternals();
 | 
						|
    auto formState = internals->GetFormState();
 | 
						|
    auto formValue = internals->GetFormSubmissionValue();
 | 
						|
    if (formState.IsNull() && formValue.IsNull()) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    CustomElementTuple entry;
 | 
						|
    entry.value() = nsContentUtils::ConvertToCustomElementFormValue(formValue);
 | 
						|
    entry.state() = nsContentUtils::ConvertToCustomElementFormValue(formState);
 | 
						|
    size += AppendEntry(element, id, entry, aFormData);
 | 
						|
  }
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
uint32_t SessionStoreUtils::CollectFormData(Document* aDocument,
 | 
						|
                                            sessionstore::FormData& aFormData) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aDocument);
 | 
						|
  uint32_t size = 0;
 | 
						|
  size += CollectTextAreaElement(aDocument, aFormData);
 | 
						|
  size += CollectInputElement(aDocument, aFormData);
 | 
						|
  size += CollectSelectElement(aDocument, aFormData);
 | 
						|
  size += CollectFormAssociatedCustomElement(aDocument, aFormData);
 | 
						|
 | 
						|
  aFormData.hasData() =
 | 
						|
      !aFormData.id().IsEmpty() || !aFormData.xpath().IsEmpty();
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
template <typename... ArgsT>
 | 
						|
void SessionStoreUtils::CollectFromTextAreaElement(Document& aDocument,
 | 
						|
                                                   uint16_t& aGeneratedCount,
 | 
						|
                                                   ArgsT&&... args) {
 | 
						|
  RefPtr<nsContentList> textlist =
 | 
						|
      NS_GetContentList(&aDocument, kNameSpaceID_XHTML, u"textarea"_ns);
 | 
						|
  uint32_t length = textlist->Length(true);
 | 
						|
  for (uint32_t i = 0; i < length; ++i) {
 | 
						|
    MOZ_ASSERT(textlist->Item(i), "null item in node list!");
 | 
						|
 | 
						|
    HTMLTextAreaElement* textArea =
 | 
						|
        HTMLTextAreaElement::FromNodeOrNull(textlist->Item(i));
 | 
						|
    if (!textArea) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    DOMString autocomplete;
 | 
						|
    textArea->GetAutocomplete(autocomplete);
 | 
						|
    if (autocomplete.AsAString().EqualsLiteral("off")) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    nsAutoString id;
 | 
						|
    textArea->GetId(id);
 | 
						|
    if (id.IsEmpty() && (aGeneratedCount > kMaxTraversedXPaths)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    nsString value;
 | 
						|
    textArea->GetValue(value);
 | 
						|
    // In order to reduce XPath generation (which is slow), we only save data
 | 
						|
    // for form fields that have been changed. (cf. bug 537289)
 | 
						|
    if (textArea->AttrValueIs(kNameSpaceID_None, nsGkAtoms::value, value,
 | 
						|
                              eCaseMatters)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    AppendValueToCollectedData(textArea, id, value, aGeneratedCount,
 | 
						|
                               std::forward<ArgsT>(args)...);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
template <typename... ArgsT>
 | 
						|
void SessionStoreUtils::CollectFromInputElement(Document& aDocument,
 | 
						|
                                                uint16_t& aGeneratedCount,
 | 
						|
                                                ArgsT&&... args) {
 | 
						|
  RefPtr<nsContentList> inputlist =
 | 
						|
      NS_GetContentList(&aDocument, kNameSpaceID_XHTML, u"input"_ns);
 | 
						|
  uint32_t length = inputlist->Length(true);
 | 
						|
  for (uint32_t i = 0; i < length; ++i) {
 | 
						|
    MOZ_ASSERT(inputlist->Item(i), "null item in node list!");
 | 
						|
    nsCOMPtr<nsIFormControl> formControl =
 | 
						|
        do_QueryInterface(inputlist->Item(i));
 | 
						|
    if (formControl) {
 | 
						|
      auto controlType = formControl->ControlType();
 | 
						|
      if (controlType == FormControlType::InputPassword ||
 | 
						|
          controlType == FormControlType::InputHidden ||
 | 
						|
          controlType == FormControlType::InputButton ||
 | 
						|
          controlType == FormControlType::InputImage ||
 | 
						|
          controlType == FormControlType::InputSubmit ||
 | 
						|
          controlType == FormControlType::InputReset) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    RefPtr<HTMLInputElement> input =
 | 
						|
        HTMLInputElement::FromNodeOrNull(inputlist->Item(i));
 | 
						|
    if (!input || !nsContentUtils::IsAutocompleteEnabled(input)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    nsAutoString id;
 | 
						|
    input->GetId(id);
 | 
						|
    if (id.IsEmpty() && (aGeneratedCount > kMaxTraversedXPaths)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    Nullable<AutocompleteInfo> aInfo;
 | 
						|
    input->GetAutocompleteInfo(aInfo);
 | 
						|
    if (!aInfo.IsNull() && !aInfo.Value().mCanAutomaticallyPersist) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (input->ControlType() == FormControlType::InputCheckbox ||
 | 
						|
        input->ControlType() == FormControlType::InputRadio) {
 | 
						|
      bool checked = input->Checked();
 | 
						|
      if (checked == input->DefaultChecked()) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      AppendValueToCollectedData(input, id, checked, aGeneratedCount,
 | 
						|
                                 std::forward<ArgsT>(args)...);
 | 
						|
    } else if (input->ControlType() == FormControlType::InputFile) {
 | 
						|
      IgnoredErrorResult rv;
 | 
						|
      nsTArray<nsString> result;
 | 
						|
      input->MozGetFileNameArray(result, rv);
 | 
						|
      if (rv.Failed() || result.Length() == 0) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      AppendValueToCollectedData(input, id, u"file"_ns, result, aGeneratedCount,
 | 
						|
                                 std::forward<ArgsT>(args)...);
 | 
						|
    } else {
 | 
						|
      nsString value;
 | 
						|
      input->GetValue(value, CallerType::System);
 | 
						|
      // In order to reduce XPath generation (which is slow), we only save data
 | 
						|
      // for form fields that have been changed. (cf. bug 537289)
 | 
						|
      // Also, don't want to collect credit card number.
 | 
						|
      if (value.IsEmpty() || IsValidCCNumber(value) ||
 | 
						|
          input->HasBeenTypePassword() ||
 | 
						|
          input->AttrValueIs(kNameSpaceID_None, nsGkAtoms::value, value,
 | 
						|
                             eCaseMatters)) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      AppendValueToCollectedData(aDocument, input, id, value, aGeneratedCount,
 | 
						|
                                 std::forward<ArgsT>(args)...);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
template <typename... ArgsT>
 | 
						|
void SessionStoreUtils::CollectFromSelectElement(Document& aDocument,
 | 
						|
                                                 uint16_t& aGeneratedCount,
 | 
						|
                                                 ArgsT&&... args) {
 | 
						|
  RefPtr<nsContentList> selectlist =
 | 
						|
      NS_GetContentList(&aDocument, kNameSpaceID_XHTML, u"select"_ns);
 | 
						|
  uint32_t length = selectlist->Length(true);
 | 
						|
  for (uint32_t i = 0; i < length; ++i) {
 | 
						|
    MOZ_ASSERT(selectlist->Item(i), "null item in node list!");
 | 
						|
    RefPtr<HTMLSelectElement> select =
 | 
						|
        HTMLSelectElement::FromNodeOrNull(selectlist->Item(i));
 | 
						|
    if (!select) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    nsAutoString id;
 | 
						|
    select->GetId(id);
 | 
						|
    if (id.IsEmpty() && (aGeneratedCount > kMaxTraversedXPaths)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    AutocompleteInfo aInfo;
 | 
						|
    select->GetAutocompleteInfo(aInfo);
 | 
						|
    if (!aInfo.mCanAutomaticallyPersist) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    nsAutoCString value;
 | 
						|
    if (!select->Multiple()) {
 | 
						|
      // <select>s without the multiple attribute are hard to determine the
 | 
						|
      // default value, so assume we don't have the default.
 | 
						|
      DOMString selectVal;
 | 
						|
      select->GetValue(selectVal);
 | 
						|
      CollectedNonMultipleSelectValue val;
 | 
						|
      val.mSelectedIndex = select->SelectedIndex();
 | 
						|
      val.mValue = selectVal.AsAString();
 | 
						|
      AppendValueToCollectedData(select, id, val, aGeneratedCount,
 | 
						|
                                 std::forward<ArgsT>(args)...);
 | 
						|
    } else {
 | 
						|
      // <select>s with the multiple attribute are easier to determine the
 | 
						|
      // default value since each <option> has a defaultSelected property
 | 
						|
      HTMLOptionsCollection* options = select->GetOptions();
 | 
						|
      if (!options) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      bool hasDefaultValue = true;
 | 
						|
      nsTArray<nsString> selectslist;
 | 
						|
      uint32_t numOptions = options->Length();
 | 
						|
      for (uint32_t idx = 0; idx < numOptions; idx++) {
 | 
						|
        HTMLOptionElement* option = options->ItemAsOption(idx);
 | 
						|
        bool selected = option->Selected();
 | 
						|
        if (!selected) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
        option->GetValue(*selectslist.AppendElement());
 | 
						|
        hasDefaultValue =
 | 
						|
            hasDefaultValue && (selected == option->DefaultSelected());
 | 
						|
      }
 | 
						|
      // In order to reduce XPath generation (which is slow), we only save data
 | 
						|
      // for form fields that have been changed. (cf. bug 537289)
 | 
						|
      if (hasDefaultValue) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      AppendValueToCollectedData(select, id, u"multipleSelect"_ns, selectslist,
 | 
						|
                                 aGeneratedCount, std::forward<ArgsT>(args)...);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
template <typename... ArgsT>
 | 
						|
void SessionStoreUtils::CollectFromFormAssociatedCustomElement(
 | 
						|
    Document& aDocument, uint16_t& aGeneratedCount, ArgsT&&... args) {
 | 
						|
  RefPtr<nsContentList> faceList = GetFormAssociatedCustomElements(&aDocument);
 | 
						|
  uint32_t length = faceList->Length(true);
 | 
						|
  for (uint32_t i = 0; i < length; ++i) {
 | 
						|
    MOZ_ASSERT(faceList->Item(i), "null item in node list!");
 | 
						|
    RefPtr<Element> element = Element::FromNode(faceList->Item(i));
 | 
						|
 | 
						|
    nsAutoString id;
 | 
						|
    element->GetId(id);
 | 
						|
    if (id.IsEmpty() && (aGeneratedCount > kMaxTraversedXPaths)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    auto* internals = element->GetCustomElementData()->GetElementInternals();
 | 
						|
    const auto& state = internals->GetFormState();
 | 
						|
    const auto& value = internals->GetFormSubmissionValue();
 | 
						|
    if (state.IsNull() && value.IsNull()) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    AppendValueToCollectedData(element, id, value, state, aGeneratedCount,
 | 
						|
                               std::forward<ArgsT>(args)...);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void CollectCurrentFormData(JSContext* aCx, Document& aDocument,
 | 
						|
                                   Nullable<CollectedData>& aRetVal) {
 | 
						|
  uint16_t generatedCount = 0;
 | 
						|
  /* textarea element */
 | 
						|
  SessionStoreUtils::CollectFromTextAreaElement(aDocument, generatedCount,
 | 
						|
                                                aRetVal);
 | 
						|
  /* input element */
 | 
						|
  SessionStoreUtils::CollectFromInputElement(aDocument, generatedCount, aCx,
 | 
						|
                                             aRetVal);
 | 
						|
  /* select element */
 | 
						|
  SessionStoreUtils::CollectFromSelectElement(aDocument, generatedCount, aCx,
 | 
						|
                                              aRetVal);
 | 
						|
  /* form-associated custom element */
 | 
						|
  SessionStoreUtils::CollectFromFormAssociatedCustomElement(
 | 
						|
      aDocument, generatedCount, aCx, aRetVal);
 | 
						|
 | 
						|
  Element* bodyElement = aDocument.GetBody();
 | 
						|
  if (bodyElement && bodyElement->IsInDesignMode()) {
 | 
						|
    bodyElement->GetInnerHTML(aRetVal.SetValue().mInnerHTML.Construct(),
 | 
						|
                              IgnoreErrors());
 | 
						|
  }
 | 
						|
 | 
						|
  if (aRetVal.IsNull()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Store the frame's current URL with its form data so that we can compare
 | 
						|
  // it when restoring data to not inject form data into the wrong document.
 | 
						|
  nsIURI* uri = aDocument.GetDocumentURI();
 | 
						|
  if (uri) {
 | 
						|
    uri->GetSpecIgnoringRef(aRetVal.SetValue().mUrl.Construct());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT
 | 
						|
static void SetElementAsString(Element* aElement, const nsAString& aValue) {
 | 
						|
  IgnoredErrorResult rv;
 | 
						|
  if (auto* textArea = HTMLTextAreaElement::FromNode(aElement)) {
 | 
						|
    // Known live because `aElement` is known live.
 | 
						|
    MOZ_KnownLive(textArea)->SetValue(aValue, rv);
 | 
						|
    if (!rv.Failed()) {
 | 
						|
      nsContentUtils::DispatchInputEvent(aElement);
 | 
						|
    }
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (auto* input = HTMLInputElement::FromNode(aElement)) {
 | 
						|
    input->SetValue(aValue, CallerType::NonSystem, rv);
 | 
						|
    if (!rv.Failed()) {
 | 
						|
      nsContentUtils::DispatchInputEvent(aElement);
 | 
						|
    }
 | 
						|
    return;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT
 | 
						|
static void SetElementAsBool(Element* aElement, bool aValue) {
 | 
						|
  HTMLInputElement* input = HTMLInputElement::FromNode(aElement);
 | 
						|
  if (input) {
 | 
						|
    bool checked = input->Checked();
 | 
						|
    if (aValue != checked) {
 | 
						|
      input->SetChecked(aValue);
 | 
						|
      nsContentUtils::DispatchInputEvent(aElement);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT
 | 
						|
static void SetElementAsFiles(HTMLInputElement* aElement,
 | 
						|
                              const CollectedFileListValue& aValue) {
 | 
						|
  IgnoredErrorResult rv;
 | 
						|
  aElement->MozSetFileNameArray(aValue.mFileList, rv);
 | 
						|
  if (rv.Failed()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  nsContentUtils::DispatchInputEvent(aElement);
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT
 | 
						|
static void SetElementAsSelect(HTMLSelectElement* aElement,
 | 
						|
                               const CollectedNonMultipleSelectValue& aValue) {
 | 
						|
  HTMLOptionsCollection* options = aElement->GetOptions();
 | 
						|
  if (!options) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  int32_t selectIdx = options->SelectedIndex();
 | 
						|
  if (selectIdx >= 0) {
 | 
						|
    nsAutoString selectOptionVal;
 | 
						|
    options->ItemAsOption(selectIdx)->GetValue(selectOptionVal);
 | 
						|
    if (aValue.mValue.Equals(selectOptionVal)) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  uint32_t numOptions = options->Length();
 | 
						|
  for (uint32_t idx = 0; idx < numOptions; idx++) {
 | 
						|
    HTMLOptionElement* option = options->ItemAsOption(idx);
 | 
						|
    nsAutoString optionValue;
 | 
						|
    option->GetValue(optionValue);
 | 
						|
    if (aValue.mValue.Equals(optionValue)) {
 | 
						|
      aElement->SetSelectedIndex(idx);
 | 
						|
      nsContentUtils::DispatchInputEvent(aElement);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT
 | 
						|
static void SetElementAsMultiSelect(HTMLSelectElement* aElement,
 | 
						|
                                    const nsTArray<nsString>& aValueArray) {
 | 
						|
  bool fireEvent = false;
 | 
						|
  HTMLOptionsCollection* options = aElement->GetOptions();
 | 
						|
  if (!options) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  uint32_t numOptions = options->Length();
 | 
						|
  for (uint32_t idx = 0; idx < numOptions; idx++) {
 | 
						|
    HTMLOptionElement* option = options->ItemAsOption(idx);
 | 
						|
    nsAutoString optionValue;
 | 
						|
    option->GetValue(optionValue);
 | 
						|
    for (uint32_t i = 0, l = aValueArray.Length(); i < l; ++i) {
 | 
						|
      if (optionValue.Equals(aValueArray[i])) {
 | 
						|
        option->SetSelected(true);
 | 
						|
        if (!option->DefaultSelected()) {
 | 
						|
          fireEvent = true;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (fireEvent) {
 | 
						|
    nsContentUtils::DispatchInputEvent(aElement);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT
 | 
						|
static void SetElementAsObject(JSContext* aCx, Element* aElement,
 | 
						|
                               JS::Handle<JS::Value> aObject) {
 | 
						|
  RefPtr<HTMLInputElement> input = HTMLInputElement::FromNode(aElement);
 | 
						|
  if (input) {
 | 
						|
    if (input->ControlType() == FormControlType::InputFile) {
 | 
						|
      CollectedFileListValue value;
 | 
						|
      if (value.Init(aCx, aObject)) {
 | 
						|
        SetElementAsFiles(input, value);
 | 
						|
      } else {
 | 
						|
        JS_ClearPendingException(aCx);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  RefPtr<HTMLSelectElement> select = HTMLSelectElement::FromNode(aElement);
 | 
						|
  if (select) {
 | 
						|
    // For Single Select Element
 | 
						|
    if (!select->Multiple()) {
 | 
						|
      CollectedNonMultipleSelectValue value;
 | 
						|
      if (value.Init(aCx, aObject)) {
 | 
						|
        SetElementAsSelect(select, value);
 | 
						|
      } else {
 | 
						|
        JS_ClearPendingException(aCx);
 | 
						|
      }
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // For Multiple Selects Element
 | 
						|
    bool isArray = false;
 | 
						|
    JS::IsArrayObject(aCx, aObject, &isArray);
 | 
						|
    if (!isArray) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    JS::Rooted<JSObject*> arrayObj(aCx, &aObject.toObject());
 | 
						|
    uint32_t arrayLength = 0;
 | 
						|
    if (!JS::GetArrayLength(aCx, arrayObj, &arrayLength)) {
 | 
						|
      JS_ClearPendingException(aCx);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    nsTArray<nsString> array(arrayLength);
 | 
						|
    for (uint32_t arrayIdx = 0; arrayIdx < arrayLength; arrayIdx++) {
 | 
						|
      JS::Rooted<JS::Value> element(aCx);
 | 
						|
      if (!JS_GetElement(aCx, arrayObj, arrayIdx, &element)) {
 | 
						|
        JS_ClearPendingException(aCx);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      if (!element.isString()) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      nsAutoJSString value;
 | 
						|
      if (!value.init(aCx, element)) {
 | 
						|
        JS_ClearPendingException(aCx);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      array.AppendElement(value);
 | 
						|
    }
 | 
						|
    SetElementAsMultiSelect(select, array);
 | 
						|
  }
 | 
						|
 | 
						|
  // For Form-Associated Custom Element:
 | 
						|
  if (!aObject.isObject()) {
 | 
						|
    // Don't restore null values.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  auto* data = aElement->GetCustomElementData();
 | 
						|
  if (!data || !data->IsFormAssociated()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  auto* internals = data->GetElementInternals();
 | 
						|
 | 
						|
  CollectedCustomElementValue value;
 | 
						|
  if (!value.Init(aCx, aObject)) {
 | 
						|
    JS_ClearPendingException(aCx);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  internals->RestoreFormValue(std::move(value.mValue), std::move(value.mState));
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT
 | 
						|
static void SetSessionData(JSContext* aCx, Element* aElement,
 | 
						|
                           JS::MutableHandle<JS::Value> aObject) {
 | 
						|
  nsAutoString data;
 | 
						|
  if (nsContentUtils::StringifyJSON(aCx, aObject, data,
 | 
						|
                                    UndefinedIsNullStringLiteral)) {
 | 
						|
    SetElementAsString(aElement, data);
 | 
						|
  } else {
 | 
						|
    JS_ClearPendingException(aCx);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT
 | 
						|
static void SetInnerHTML(Document& aDocument, const nsString& aInnerHTML) {
 | 
						|
  RefPtr<Element> bodyElement = aDocument.GetBody();
 | 
						|
  if (bodyElement && bodyElement->IsInDesignMode()) {
 | 
						|
    IgnoredErrorResult rv;
 | 
						|
    bodyElement->SetInnerHTML(aInnerHTML, aDocument.NodePrincipal(), rv);
 | 
						|
    if (!rv.Failed()) {
 | 
						|
      nsContentUtils::DispatchInputEvent(bodyElement);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class FormDataParseContext : public txIParseContext {
 | 
						|
 public:
 | 
						|
  explicit FormDataParseContext(bool aCaseInsensitive)
 | 
						|
      : mIsCaseInsensitive(aCaseInsensitive) {}
 | 
						|
 | 
						|
  nsresult resolveNamespacePrefix(nsAtom* aPrefix, int32_t& aID) override {
 | 
						|
    if (aPrefix == nsGkAtoms::xul) {
 | 
						|
      aID = kNameSpaceID_XUL;
 | 
						|
    } else {
 | 
						|
      MOZ_ASSERT(nsDependentAtomString(aPrefix).EqualsLiteral("xhtml"));
 | 
						|
      aID = kNameSpaceID_XHTML;
 | 
						|
    }
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult resolveFunctionCall(nsAtom* aName, int32_t aID,
 | 
						|
                               FunctionCall** aFunction) override {
 | 
						|
    return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
 | 
						|
  }
 | 
						|
 | 
						|
  bool caseInsensitiveNameTests() override { return mIsCaseInsensitive; }
 | 
						|
 | 
						|
  void SetErrorOffset(uint32_t aOffset) override {}
 | 
						|
 | 
						|
 private:
 | 
						|
  bool mIsCaseInsensitive;
 | 
						|
};
 | 
						|
 | 
						|
static Element* FindNodeByXPath(Document& aDocument,
 | 
						|
                                const nsAString& aExpression) {
 | 
						|
  FormDataParseContext parsingContext(aDocument.IsHTMLDocument());
 | 
						|
  IgnoredErrorResult rv;
 | 
						|
  UniquePtr<XPathExpression> expression(
 | 
						|
      aDocument.XPathEvaluator()->CreateExpression(aExpression, &parsingContext,
 | 
						|
                                                   &aDocument, rv));
 | 
						|
  if (rv.Failed()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  RefPtr<XPathResult> result = expression->Evaluate(
 | 
						|
      aDocument, XPathResult::FIRST_ORDERED_NODE_TYPE, nullptr, rv);
 | 
						|
  if (rv.Failed()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  return Element::FromNodeOrNull(result->GetSingleNodeValue(rv));
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
 | 
						|
                                        Document& aDocument,
 | 
						|
                                        const CollectedData& aData) {
 | 
						|
  if (!aData.mUrl.WasPassed()) {
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  // Don't restore any data for the given frame if the URL
 | 
						|
  // stored in the form data doesn't match its current URL.
 | 
						|
  nsAutoCString url;
 | 
						|
  Unused << aDocument.GetDocumentURI()->GetSpecIgnoringRef(url);
 | 
						|
  if (!aData.mUrl.Value().Equals(url)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  using Change = SessionStoreChangeListener::Change;
 | 
						|
  SessionStoreChangeListener::CollectSessionStoreData(
 | 
						|
      aDocument.GetWindowContext(), EnumSet<Change>(Change::Input));
 | 
						|
 | 
						|
  if (aData.mInnerHTML.WasPassed()) {
 | 
						|
    SetInnerHTML(aDocument, aData.mInnerHTML.Value());
 | 
						|
  }
 | 
						|
  if (aData.mId.WasPassed()) {
 | 
						|
    for (auto& entry : aData.mId.Value().Entries()) {
 | 
						|
      RefPtr<Element> node = aDocument.GetElementById(entry.mKey);
 | 
						|
      if (node == nullptr) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      if (entry.mValue.IsString()) {
 | 
						|
        SetElementAsString(node, entry.mValue.GetAsString());
 | 
						|
      } else if (entry.mValue.IsBoolean()) {
 | 
						|
        SetElementAsBool(node, entry.mValue.GetAsBoolean());
 | 
						|
      } else {
 | 
						|
        // For about:{sessionrestore,welcomeback} we saved the field as JSON to
 | 
						|
        // avoid nested instances causing humongous sessionstore.js files.
 | 
						|
        // cf. bug 467409
 | 
						|
        JSContext* cx = aGlobal.Context();
 | 
						|
        if (entry.mKey.EqualsLiteral("sessionData")) {
 | 
						|
          if (url.EqualsLiteral("about:sessionrestore") ||
 | 
						|
              url.EqualsLiteral("about:welcomeback")) {
 | 
						|
            JS::Rooted<JS::Value> object(
 | 
						|
                cx, JS::ObjectValue(*entry.mValue.GetAsObject()));
 | 
						|
            SetSessionData(cx, node, &object);
 | 
						|
            continue;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        JS::Rooted<JS::Value> object(
 | 
						|
            cx, JS::ObjectValue(*entry.mValue.GetAsObject()));
 | 
						|
        SetElementAsObject(cx, node, object);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (aData.mXpath.WasPassed()) {
 | 
						|
    for (auto& entry : aData.mXpath.Value().Entries()) {
 | 
						|
      RefPtr<Element> node = FindNodeByXPath(aDocument, entry.mKey);
 | 
						|
      if (node == nullptr) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      if (entry.mValue.IsString()) {
 | 
						|
        SetElementAsString(node, entry.mValue.GetAsString());
 | 
						|
      } else if (entry.mValue.IsBoolean()) {
 | 
						|
        SetElementAsBool(node, entry.mValue.GetAsBoolean());
 | 
						|
      } else {
 | 
						|
        JS::Rooted<JS::Value> object(
 | 
						|
            aGlobal.Context(), JS::ObjectValue(*entry.mValue.GetAsObject()));
 | 
						|
        SetElementAsObject(aGlobal.Context(), node, object);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
MOZ_CAN_RUN_SCRIPT
 | 
						|
void RestoreFormEntry(Element* aNode, const FormEntryValue& aValue) {
 | 
						|
  using Type = sessionstore::FormEntryValue::Type;
 | 
						|
  switch (aValue.type()) {
 | 
						|
    case Type::TCheckbox:
 | 
						|
      SetElementAsBool(aNode, aValue.get_Checkbox().value());
 | 
						|
      break;
 | 
						|
    case Type::TTextField:
 | 
						|
      SetElementAsString(aNode, aValue.get_TextField().value());
 | 
						|
      break;
 | 
						|
    case Type::TFileList: {
 | 
						|
      if (RefPtr<HTMLInputElement> input = HTMLInputElement::FromNode(aNode);
 | 
						|
          input && input->ControlType() == FormControlType::InputFile) {
 | 
						|
        CollectedFileListValue value;
 | 
						|
        value.mFileList = aValue.get_FileList().valueList().Clone();
 | 
						|
        SetElementAsFiles(input, value);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case Type::TSingleSelect: {
 | 
						|
      if (RefPtr<HTMLSelectElement> select = HTMLSelectElement::FromNode(aNode);
 | 
						|
          select && !select->Multiple()) {
 | 
						|
        CollectedNonMultipleSelectValue value;
 | 
						|
        value.mSelectedIndex = aValue.get_SingleSelect().index();
 | 
						|
        value.mValue = aValue.get_SingleSelect().value();
 | 
						|
        SetElementAsSelect(select, value);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case Type::TMultipleSelect: {
 | 
						|
      if (RefPtr<HTMLSelectElement> select = HTMLSelectElement::FromNode(aNode);
 | 
						|
          select && select->Multiple()) {
 | 
						|
        SetElementAsMultiSelect(select,
 | 
						|
                                aValue.get_MultipleSelect().valueList());
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case Type::TCustomElementTuple: {
 | 
						|
      const auto* data = aNode->GetCustomElementData();
 | 
						|
      if (!data || !data->IsFormAssociated()) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      auto* internals = data->GetElementInternals();
 | 
						|
      nsCOMPtr<nsIGlobalObject> global = aNode->GetOwnerGlobal();
 | 
						|
      internals->RestoreFormValue(
 | 
						|
          nsContentUtils::ExtractFormAssociatedCustomElementValue(
 | 
						|
              global, aValue.get_CustomElementTuple().value()),
 | 
						|
          nsContentUtils::ExtractFormAssociatedCustomElementValue(
 | 
						|
              global, aValue.get_CustomElementTuple().state()));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
void SessionStoreUtils::RestoreFormData(
 | 
						|
    Document& aDocument, const nsString& aInnerHTML,
 | 
						|
    const nsTArray<SessionStoreRestoreData::Entry>& aEntries) {
 | 
						|
  using Change = SessionStoreChangeListener::Change;
 | 
						|
  SessionStoreChangeListener::CollectSessionStoreData(
 | 
						|
      aDocument.GetWindowContext(), EnumSet<Change>(Change::Input));
 | 
						|
 | 
						|
  if (!aInnerHTML.IsEmpty()) {
 | 
						|
    SetInnerHTML(aDocument, aInnerHTML);
 | 
						|
  }
 | 
						|
 | 
						|
  for (const auto& entry : aEntries) {
 | 
						|
    RefPtr<Element> node = entry.mIsXPath
 | 
						|
                               ? FindNodeByXPath(aDocument, entry.mData.id())
 | 
						|
                               : aDocument.GetElementById(entry.mData.id());
 | 
						|
    if (node) {
 | 
						|
      RestoreFormEntry(node, entry.mData.value());
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
typedef void (*CollectorFunc)(JSContext* aCx, Document& aDocument,
 | 
						|
                              Nullable<CollectedData>& aRetVal);
 | 
						|
 | 
						|
/**
 | 
						|
 * A function that will recursively call |CollectorFunc| to collect data for all
 | 
						|
 * non-dynamic frames in the current frame/docShell tree.
 | 
						|
 */
 | 
						|
static void CollectFrameTreeData(JSContext* aCx,
 | 
						|
                                 BrowsingContext* aBrowsingContext,
 | 
						|
                                 Nullable<CollectedData>& aRetVal,
 | 
						|
                                 CollectorFunc aFunc) {
 | 
						|
  if (aBrowsingContext->CreatedDynamically()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  nsPIDOMWindowOuter* window = aBrowsingContext->GetDOMWindow();
 | 
						|
  if (!window || !window->GetDocShell()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  Document* document = window->GetExtantDoc();
 | 
						|
  if (!document) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Collect data from current frame */
 | 
						|
  aFunc(aCx, *document, aRetVal);
 | 
						|
 | 
						|
  /* Collect data from all child frame */
 | 
						|
  nsTArray<JSObject*> childrenData;
 | 
						|
  SequenceRooter<JSObject*> rooter(aCx, &childrenData);
 | 
						|
  uint32_t trailingNullCounter = 0;
 | 
						|
 | 
						|
  // This is not going to work for fission. Bug 1572084 for tracking it.
 | 
						|
  for (auto& child : aBrowsingContext->Children()) {
 | 
						|
    NullableRootedDictionary<CollectedData> data(aCx);
 | 
						|
    CollectFrameTreeData(aCx, child, data, aFunc);
 | 
						|
    if (data.IsNull()) {
 | 
						|
      childrenData.AppendElement(nullptr);
 | 
						|
      trailingNullCounter++;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    JS::Rooted<JS::Value> jsval(aCx);
 | 
						|
    if (!ToJSValue(aCx, data.SetValue(), &jsval)) {
 | 
						|
      JS_ClearPendingException(aCx);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    childrenData.AppendElement(&jsval.toObject());
 | 
						|
    trailingNullCounter = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (trailingNullCounter != childrenData.Length()) {
 | 
						|
    childrenData.TruncateLength(childrenData.Length() - trailingNullCounter);
 | 
						|
    aRetVal.SetValue().mChildren.Construct() = std::move(childrenData);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* static */ void SessionStoreUtils::CollectScrollPosition(
 | 
						|
    const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
 | 
						|
    Nullable<CollectedData>& aRetVal) {
 | 
						|
  CollectFrameTreeData(aGlobal.Context(), aWindow.get(), aRetVal,
 | 
						|
                       CollectCurrentScrollPosition);
 | 
						|
}
 | 
						|
 | 
						|
/* static */ void SessionStoreUtils::CollectFormData(
 | 
						|
    const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
 | 
						|
    Nullable<CollectedData>& aRetVal) {
 | 
						|
  CollectFrameTreeData(aGlobal.Context(), aWindow.get(), aRetVal,
 | 
						|
                       CollectCurrentFormData);
 | 
						|
}
 | 
						|
 | 
						|
/* static */ void SessionStoreUtils::ComposeInputData(
 | 
						|
    const nsTArray<CollectedInputDataValue>& aData, InputElementData& ret) {
 | 
						|
  nsTArray<int> selectedIndex, valueIdx;
 | 
						|
  nsTArray<nsString> id, selectVal, strVal, type;
 | 
						|
  nsTArray<bool> boolVal;
 | 
						|
 | 
						|
  for (const CollectedInputDataValue& data : aData) {
 | 
						|
    id.AppendElement(data.id);
 | 
						|
    type.AppendElement(data.type);
 | 
						|
 | 
						|
    if (data.value.is<mozilla::dom::CollectedNonMultipleSelectValue>()) {
 | 
						|
      valueIdx.AppendElement(selectVal.Length());
 | 
						|
      selectedIndex.AppendElement(
 | 
						|
          data.value.as<mozilla::dom::CollectedNonMultipleSelectValue>()
 | 
						|
              .mSelectedIndex);
 | 
						|
      selectVal.AppendElement(
 | 
						|
          data.value.as<mozilla::dom::CollectedNonMultipleSelectValue>()
 | 
						|
              .mValue);
 | 
						|
    } else if (data.value.is<CopyableTArray<nsString>>()) {
 | 
						|
      // The first valueIdx is "index of the first string value"
 | 
						|
      valueIdx.AppendElement(strVal.Length());
 | 
						|
      strVal.AppendElements(data.value.as<CopyableTArray<nsString>>());
 | 
						|
      // The second valueIdx is "index of the last string value" + 1
 | 
						|
      id.AppendElement(data.id);
 | 
						|
      type.AppendElement(data.type);
 | 
						|
      valueIdx.AppendElement(strVal.Length());
 | 
						|
    } else if (data.value.is<nsString>()) {
 | 
						|
      valueIdx.AppendElement(strVal.Length());
 | 
						|
      strVal.AppendElement(data.value.as<nsString>());
 | 
						|
    } else if (data.type.EqualsLiteral("bool")) {
 | 
						|
      valueIdx.AppendElement(boolVal.Length());
 | 
						|
      boolVal.AppendElement(data.value.as<bool>());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (selectedIndex.Length() != 0) {
 | 
						|
    ret.mSelectedIndex.Construct(std::move(selectedIndex));
 | 
						|
  }
 | 
						|
  if (valueIdx.Length() != 0) {
 | 
						|
    ret.mValueIdx.Construct(std::move(valueIdx));
 | 
						|
  }
 | 
						|
  if (id.Length() != 0) {
 | 
						|
    ret.mId.Construct(std::move(id));
 | 
						|
  }
 | 
						|
  if (selectVal.Length() != 0) {
 | 
						|
    ret.mSelectVal.Construct(std::move(selectVal));
 | 
						|
  }
 | 
						|
  if (strVal.Length() != 0) {
 | 
						|
    ret.mStrVal.Construct(std::move(strVal));
 | 
						|
  }
 | 
						|
  if (type.Length() != 0) {
 | 
						|
    ret.mType.Construct(std::move(type));
 | 
						|
  }
 | 
						|
  if (boolVal.Length() != 0) {
 | 
						|
    ret.mBoolVal.Construct(std::move(boolVal));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<nsISessionStoreRestoreData>
 | 
						|
SessionStoreUtils::ConstructSessionStoreRestoreData(
 | 
						|
    const GlobalObject& aGlobal) {
 | 
						|
  nsCOMPtr<nsISessionStoreRestoreData> data = new SessionStoreRestoreData();
 | 
						|
  return data.forget();
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
already_AddRefed<Promise> SessionStoreUtils::InitializeRestore(
 | 
						|
    const GlobalObject& aGlobal, CanonicalBrowsingContext& aContext,
 | 
						|
    nsISessionStoreRestoreData* aData, ErrorResult& aError) {
 | 
						|
  if (!mozilla::SessionHistoryInParent()) {
 | 
						|
    MOZ_CRASH("why were we called?");
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aContext.IsTop());
 | 
						|
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aData);
 | 
						|
  nsCOMPtr<SessionStoreRestoreData> data = do_QueryInterface(aData);
 | 
						|
  aContext.SetRestoreData(data, aError);
 | 
						|
  if (aError.Failed()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsISHistory> shistory = aContext.GetSessionHistory();
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(shistory);
 | 
						|
  shistory->ReloadCurrentEntry();
 | 
						|
 | 
						|
  return aContext.GetRestorePromise();
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
void SessionStoreUtils::RestoreDocShellState(
 | 
						|
    nsIDocShell* aDocShell, const DocShellRestoreState& aState) {
 | 
						|
  if (aDocShell) {
 | 
						|
    if (aState.URI()) {
 | 
						|
      aDocShell->SetCurrentURIForSessionStore(aState.URI());
 | 
						|
    }
 | 
						|
    RestoreDocShellCapabilities(aDocShell, aState.docShellCaps());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
already_AddRefed<Promise> SessionStoreUtils::RestoreDocShellState(
 | 
						|
    const GlobalObject& aGlobal, CanonicalBrowsingContext& aContext,
 | 
						|
    const nsACString& aURL, const nsCString& aDocShellCaps,
 | 
						|
    ErrorResult& aError) {
 | 
						|
  MOZ_RELEASE_ASSERT(mozilla::SessionHistoryInParent());
 | 
						|
  MOZ_RELEASE_ASSERT(aContext.IsTop());
 | 
						|
 | 
						|
  WindowGlobalParent* wgp = aContext.GetCurrentWindowGlobal();
 | 
						|
  if (!wgp) {
 | 
						|
    aError.Throw(NS_ERROR_FAILURE);
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(global);
 | 
						|
 | 
						|
  RefPtr<Promise> promise = Promise::Create(global, aError);
 | 
						|
  if (aError.Failed()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIURI> uri;
 | 
						|
  if (!aURL.IsEmpty()) {
 | 
						|
    if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), aURL))) {
 | 
						|
      aError.Throw(NS_ERROR_FAILURE);
 | 
						|
      return nullptr;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  bool allowJavascript = true;
 | 
						|
  for (const nsACString& token :
 | 
						|
       nsCCharSeparatedTokenizer(aDocShellCaps, ',').ToRange()) {
 | 
						|
    if (token.EqualsLiteral("Javascript")) {
 | 
						|
      allowJavascript = false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Unused << aContext.SetAllowJavascript(allowJavascript);
 | 
						|
 | 
						|
  DocShellRestoreState state = {uri, aDocShellCaps};
 | 
						|
 | 
						|
  // TODO (anny): Investigate removing this roundtrip.
 | 
						|
  wgp->SendRestoreDocShellState(state)->Then(
 | 
						|
      GetMainThreadSerialEventTarget(), __func__,
 | 
						|
      [promise](void) { promise->MaybeResolveWithUndefined(); },
 | 
						|
      [promise](void) { promise->MaybeRejectWithUndefined(); });
 | 
						|
 | 
						|
  return promise.forget();
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
void SessionStoreUtils::RestoreSessionStorageFromParent(
 | 
						|
    const GlobalObject& aGlobal, const CanonicalBrowsingContext& aContext,
 | 
						|
    const Record<nsCString, Record<nsString, nsString>>& aSessionStorage) {
 | 
						|
  nsTArray<SSCacheCopy> cacheInitList;
 | 
						|
  for (const auto& originEntry : aSessionStorage.Entries()) {
 | 
						|
    nsCOMPtr<nsIPrincipal> storagePrincipal =
 | 
						|
        BasePrincipal::CreateContentPrincipal(originEntry.mKey);
 | 
						|
 | 
						|
    nsCString originKey;
 | 
						|
    nsresult rv = storagePrincipal->GetStorageOriginKey(originKey);
 | 
						|
    if (NS_FAILED(rv)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    SSCacheCopy& cacheInit = *cacheInitList.AppendElement();
 | 
						|
 | 
						|
    cacheInit.originKey() = originKey;
 | 
						|
    PrincipalToPrincipalInfo(storagePrincipal, &cacheInit.principalInfo());
 | 
						|
 | 
						|
    for (const auto& entry : originEntry.mValue.Entries()) {
 | 
						|
      SSSetItemInfo& setItemInfo = *cacheInit.data().AppendElement();
 | 
						|
      setItemInfo.key() = entry.mKey;
 | 
						|
      setItemInfo.value() = entry.mValue;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  BackgroundSessionStorageManager::LoadData(aContext.Id(), cacheInitList);
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult SessionStoreUtils::ConstructFormDataValues(
 | 
						|
    JSContext* aCx, const nsTArray<sessionstore::FormEntry>& aValues,
 | 
						|
    nsTArray<Record<nsString, OwningStringOrBooleanOrObject>::EntryType>&
 | 
						|
        aEntries,
 | 
						|
    bool aParseSessionData) {
 | 
						|
  using EntryType = Record<nsString, OwningStringOrBooleanOrObject>::EntryType;
 | 
						|
 | 
						|
  if (!aEntries.SetCapacity(aValues.Length(), fallible)) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  for (const auto& value : aValues) {
 | 
						|
    EntryType* entry = aEntries.AppendElement();
 | 
						|
 | 
						|
    using Type = sessionstore::FormEntryValue::Type;
 | 
						|
    switch (value.value().type()) {
 | 
						|
      case Type::TCheckbox:
 | 
						|
        entry->mValue.SetAsBoolean() = value.value().get_Checkbox().value();
 | 
						|
        break;
 | 
						|
      case Type::TTextField: {
 | 
						|
        if (aParseSessionData && value.id() == u"sessionData"_ns) {
 | 
						|
          JS::Rooted<JS::Value> jsval(aCx);
 | 
						|
          const auto& fieldValue = value.value().get_TextField().value();
 | 
						|
          if (!JS_ParseJSON(aCx, fieldValue.get(), fieldValue.Length(),
 | 
						|
                            &jsval) ||
 | 
						|
              !jsval.isObject()) {
 | 
						|
            return NS_ERROR_FAILURE;
 | 
						|
          }
 | 
						|
          entry->mValue.SetAsObject() = &jsval.toObject();
 | 
						|
        } else {
 | 
						|
          entry->mValue.SetAsString() = value.value().get_TextField().value();
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case Type::TFileList: {
 | 
						|
        CollectedFileListValue file;
 | 
						|
        file.mFileList = value.value().get_FileList().valueList().Clone();
 | 
						|
 | 
						|
        JS::Rooted<JS::Value> jsval(aCx);
 | 
						|
        if (!ToJSValue(aCx, file, &jsval) || !jsval.isObject()) {
 | 
						|
          return NS_ERROR_FAILURE;
 | 
						|
        }
 | 
						|
        entry->mValue.SetAsObject() = &jsval.toObject();
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case Type::TSingleSelect: {
 | 
						|
        CollectedNonMultipleSelectValue select;
 | 
						|
        select.mSelectedIndex = value.value().get_SingleSelect().index();
 | 
						|
        select.mValue = value.value().get_SingleSelect().value();
 | 
						|
 | 
						|
        JS::Rooted<JS::Value> jsval(aCx);
 | 
						|
        if (!ToJSValue(aCx, select, &jsval) || !jsval.isObject()) {
 | 
						|
          return NS_ERROR_FAILURE;
 | 
						|
        }
 | 
						|
        entry->mValue.SetAsObject() = &jsval.toObject();
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case Type::TMultipleSelect: {
 | 
						|
        JS::Rooted<JS::Value> jsval(aCx);
 | 
						|
        if (!ToJSValue(aCx, value.value().get_MultipleSelect().valueList(),
 | 
						|
                       &jsval) ||
 | 
						|
            !jsval.isObject()) {
 | 
						|
          return NS_ERROR_FAILURE;
 | 
						|
        }
 | 
						|
        entry->mValue.SetAsObject() = &jsval.toObject();
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case Type::TCustomElementTuple: {
 | 
						|
        nsCOMPtr<nsIGlobalObject> global;
 | 
						|
        JS::Rooted<JSObject*> globalObject(aCx, JS::CurrentGlobalOrNull(aCx));
 | 
						|
        if (NS_WARN_IF(!globalObject)) {
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        global = xpc::NativeGlobal(globalObject);
 | 
						|
        if (NS_WARN_IF(!global)) {
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        auto formState =
 | 
						|
            nsContentUtils::ExtractFormAssociatedCustomElementValue(
 | 
						|
                global, value.value().get_CustomElementTuple().state());
 | 
						|
        auto formValue =
 | 
						|
            nsContentUtils::ExtractFormAssociatedCustomElementValue(
 | 
						|
                global, value.value().get_CustomElementTuple().value());
 | 
						|
        MOZ_ASSERT(!formValue.IsNull() || !formState.IsNull(),
 | 
						|
                   "Shouldn't be storing null values!");
 | 
						|
 | 
						|
        CollectedCustomElementValue val;
 | 
						|
        val.mValue = formValue;
 | 
						|
        val.mState = formState;
 | 
						|
        JS::Rooted<JS::Value> jsval(aCx);
 | 
						|
        if (!ToJSValue(aCx, val, &jsval)) {
 | 
						|
          return NS_ERROR_FAILURE;
 | 
						|
        }
 | 
						|
        entry->mValue.SetAsObject() = &jsval.toObject();
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    entry->mKey = value.id();
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
static nsresult ConstructSessionStorageValue(
 | 
						|
    const nsTArray<SSSetItemInfo>& aValues,
 | 
						|
    Record<nsString, nsString>& aRecord) {
 | 
						|
  auto& entries = aRecord.Entries();
 | 
						|
  for (const auto& value : aValues) {
 | 
						|
    auto entry = entries.AppendElement();
 | 
						|
    entry->mKey = value.key();
 | 
						|
    entry->mValue = value.value();
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult SessionStoreUtils::ConstructSessionStorageValues(
 | 
						|
    CanonicalBrowsingContext* aBrowsingContext,
 | 
						|
    const nsTArray<SSCacheCopy>& aValues,
 | 
						|
    Record<nsCString, Record<nsString, nsString>>& aRecord) {
 | 
						|
  if (!aRecord.Entries().SetCapacity(aValues.Length(), fallible)) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  for (const auto& value : aValues) {
 | 
						|
    auto storagePrincipal = PrincipalInfoToPrincipal(value.principalInfo());
 | 
						|
    if (storagePrincipal.isErr()) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    auto entry = aRecord.Entries().AppendElement();
 | 
						|
 | 
						|
    if (!entry->mValue.Entries().SetCapacity(value.data().Length(), fallible)) {
 | 
						|
      return NS_ERROR_FAILURE;
 | 
						|
    }
 | 
						|
 | 
						|
    if (NS_FAILED(storagePrincipal.inspect()->GetOrigin(entry->mKey))) {
 | 
						|
      return NS_ERROR_FAILURE;
 | 
						|
    }
 | 
						|
 | 
						|
    ConstructSessionStorageValue(value.data(), entry->mValue);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
bool SessionStoreUtils::CopyProperty(JSContext* aCx, JS::Handle<JSObject*> aDst,
 | 
						|
                                     JS::Handle<JSObject*> aSrc,
 | 
						|
                                     const nsAString& aName) {
 | 
						|
  JS::Rooted<JS::PropertyKey> name(aCx);
 | 
						|
  const char16_t* data;
 | 
						|
  size_t length = aName.GetData(&data);
 | 
						|
 | 
						|
  if (!JS_CharsToId(aCx, JS::TwoByteChars(data, length), &name)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  bool found = false;
 | 
						|
  if (!JS_HasPropertyById(aCx, aSrc, name, &found) || !found) {
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  JS::Rooted<JS::Value> value(aCx);
 | 
						|
  if (!JS_GetPropertyById(aCx, aSrc, name, &value)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (value.isNullOrUndefined()) {
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  return JS_DefinePropertyById(aCx, aDst, name, value, JSPROP_ENUMERATE);
 | 
						|
}
 |