tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

XULPersist.cpp (7397B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      6 
      7 #include "XULPersist.h"
      8 
      9 #include "mozilla/BasePrincipal.h"
     10 #include "mozilla/dom/Document.h"
     11 #include "mozilla/dom/Element.h"
     12 #include "nsContentUtils.h"
     13 #include "nsIAppWindow.h"
     14 #include "nsIStringEnumerator.h"
     15 #include "nsIXULStore.h"
     16 #include "nsServiceManagerUtils.h"
     17 
     18 namespace mozilla::dom {
     19 
     20 static bool IsRootElement(Element* aElement) {
     21  return aElement->OwnerDoc()->GetRootElement() == aElement;
     22 }
     23 
     24 // FIXME: This is a hack to differentiate "attribute is missing" from "attribute
     25 // is present but empty". Use a newline to avoid virtually all collisions.
     26 // Ideally the XUL store would be able to store this more reasonably.
     27 constexpr auto kMissingAttributeToken = u"-moz-missing\n"_ns;
     28 
     29 static bool ShouldPersistAttribute(Element* aElement, nsAtom* aAttribute) {
     30  if (IsRootElement(aElement)) {
     31    // This is not an element of the top document, its owner is
     32    // not an AppWindow. Persist it.
     33    if (aElement->OwnerDoc()->GetInProcessParentDocument()) {
     34      return true;
     35    }
     36    // The following attributes of xul:window should be handled in
     37    // AppWindow::SavePersistentAttributes instead of here.
     38    if (aAttribute == nsGkAtoms::screenX || aAttribute == nsGkAtoms::screenY ||
     39        aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
     40        aAttribute == nsGkAtoms::sizemode) {
     41      return false;
     42    }
     43  }
     44  return true;
     45 }
     46 
     47 NS_IMPL_ISUPPORTS(XULPersist, nsIDocumentObserver)
     48 
     49 XULPersist::XULPersist(Document* aDocument)
     50    : nsStubDocumentObserver(), mDocument(aDocument) {}
     51 
     52 XULPersist::~XULPersist() = default;
     53 
     54 void XULPersist::Init() {
     55  ApplyPersistentAttributes();
     56  mDocument->AddObserver(this);
     57 }
     58 
     59 void XULPersist::DropDocumentReference() {
     60  mDocument->RemoveObserver(this);
     61  mDocument = nullptr;
     62 }
     63 
     64 void XULPersist::AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID,
     65                                  nsAtom* aAttribute, AttrModType,
     66                                  const nsAttrValue* aOldValue) {
     67  NS_ASSERTION(aElement->OwnerDoc() == mDocument, "unexpected doc");
     68 
     69  if (aNameSpaceID != kNameSpaceID_None) {
     70    return;
     71  }
     72 
     73  // See if there is anything we need to persist in the localstore.
     74  nsAutoString persist;
     75  // Persistence of attributes of xul:window is handled in AppWindow.
     76  if (aElement->GetAttr(nsGkAtoms::persist, persist) &&
     77      ShouldPersistAttribute(aElement, aAttribute) &&
     78      // XXXldb This should check that it's a token, not just a substring.
     79      persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
     80    // Might not need this, but be safe for now.
     81    nsCOMPtr<nsIDocumentObserver> kungFuDeathGrip(this);
     82    nsContentUtils::AddScriptRunner(NewRunnableMethod<Element*, nsAtom*>(
     83        "dom::XULPersist::Persist", this, &XULPersist::Persist, aElement,
     84        aAttribute));
     85  }
     86 }
     87 
     88 void XULPersist::Persist(Element* aElement, nsAtom* aAttribute) {
     89  if (!mDocument) {
     90    return;
     91  }
     92  // For non-chrome documents, persistence is simply broken
     93  if (!mDocument->NodePrincipal()->IsSystemPrincipal()) {
     94    return;
     95  }
     96 
     97  if (!mLocalStore) {
     98    mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
     99    if (NS_WARN_IF(!mLocalStore)) {
    100      return;
    101    }
    102  }
    103 
    104  nsAutoString id;
    105 
    106  aElement->GetAttr(nsGkAtoms::id, id);
    107  nsAtomString attrstr(aAttribute);
    108 
    109  nsAutoCString utf8uri;
    110  nsresult rv = mDocument->GetDocumentURI()->GetSpec(utf8uri);
    111  if (NS_WARN_IF(NS_FAILED(rv))) {
    112    return;
    113  }
    114 
    115  // Persisting attributes to top level windows is handled by AppWindow.
    116  if (IsRootElement(aElement)) {
    117    if (nsCOMPtr<nsIAppWindow> win =
    118            mDocument->GetAppWindowIfToplevelChrome()) {
    119      return;
    120    }
    121  }
    122 
    123  NS_ConvertUTF8toUTF16 uri(utf8uri);
    124  nsAutoString valuestr;
    125  if (!aElement->GetAttr(aAttribute, valuestr)) {
    126    valuestr = kMissingAttributeToken;
    127  }
    128 
    129  mLocalStore->SetValue(uri, id, attrstr, valuestr);
    130  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "value set");
    131 }
    132 
    133 nsresult XULPersist::ApplyPersistentAttributes() {
    134  if (!mDocument) {
    135    return NS_ERROR_NOT_AVAILABLE;
    136  }
    137  // For non-chrome documents, persistance is simply broken
    138  if (!mDocument->NodePrincipal()->IsSystemPrincipal()) {
    139    return NS_ERROR_NOT_AVAILABLE;
    140  }
    141 
    142  // Add all of the 'persisted' attributes into the content
    143  // model.
    144  if (!mLocalStore) {
    145    mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
    146    if (NS_WARN_IF(!mLocalStore)) {
    147      return NS_ERROR_NOT_INITIALIZED;
    148    }
    149  }
    150 
    151  nsCOMArray<Element> elements;
    152 
    153  nsAutoCString utf8uri;
    154  nsresult rv = mDocument->GetDocumentURI()->GetSpec(utf8uri);
    155  if (NS_WARN_IF(NS_FAILED(rv))) {
    156    return rv;
    157  }
    158  NS_ConvertUTF8toUTF16 uri(utf8uri);
    159 
    160  // Get a list of element IDs for which persisted values are available
    161  nsCOMPtr<nsIStringEnumerator> ids;
    162  rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
    163  if (NS_WARN_IF(NS_FAILED(rv))) {
    164    return rv;
    165  }
    166 
    167  bool hasmore;
    168  while (NS_SUCCEEDED(ids->HasMore(&hasmore)) && hasmore) {
    169    nsAutoString id;
    170    ids->GetNext(id);
    171 
    172    // We want to hold strong refs to the elements while applying
    173    // persistent attributes, just in case.
    174    const Span allElements = mDocument->GetAllElementsForId(id);
    175    if (allElements.IsEmpty()) {
    176      continue;
    177    }
    178    elements.Clear();
    179    elements.SetCapacity(allElements.Length());
    180    for (Element* element : allElements) {
    181      elements.AppendObject(element);
    182    }
    183 
    184    rv = ApplyPersistentAttributesToElements(id, uri, elements);
    185    if (NS_WARN_IF(NS_FAILED(rv))) {
    186      return rv;
    187    }
    188  }
    189 
    190  return NS_OK;
    191 }
    192 
    193 nsresult XULPersist::ApplyPersistentAttributesToElements(
    194    const nsAString& aID, const nsAString& aDocURI,
    195    nsCOMArray<Element>& aElements) {
    196  nsresult rv = NS_OK;
    197  // Get a list of attributes for which persisted values are available
    198  nsCOMPtr<nsIStringEnumerator> attrs;
    199  rv = mLocalStore->GetAttributeEnumerator(aDocURI, aID, getter_AddRefs(attrs));
    200  if (NS_WARN_IF(NS_FAILED(rv))) {
    201    return rv;
    202  }
    203 
    204  bool hasmore;
    205  while (NS_SUCCEEDED(attrs->HasMore(&hasmore)) && hasmore) {
    206    nsAutoString attrstr;
    207    attrs->GetNext(attrstr);
    208 
    209    nsAutoString value;
    210    rv = mLocalStore->GetValue(aDocURI, aID, attrstr, value);
    211    if (NS_WARN_IF(NS_FAILED(rv))) {
    212      return rv;
    213    }
    214 
    215    RefPtr<nsAtom> attr = NS_Atomize(attrstr);
    216    if (NS_WARN_IF(!attr)) {
    217      return NS_ERROR_OUT_OF_MEMORY;
    218    }
    219 
    220    uint32_t cnt = aElements.Length();
    221    for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
    222      Element* element = aElements.SafeElementAt(i);
    223      if (!element) {
    224        continue;
    225      }
    226 
    227      // Applying persistent attributes to top level windows is handled
    228      // by AppWindow.
    229      if (IsRootElement(element)) {
    230        if (nsCOMPtr<nsIAppWindow> win =
    231                mDocument->GetAppWindowIfToplevelChrome()) {
    232          continue;
    233        }
    234      }
    235 
    236      if (value == kMissingAttributeToken) {
    237        (void)element->UnsetAttr(kNameSpaceID_None, attr, true);
    238      } else {
    239        (void)element->SetAttr(kNameSpaceID_None, attr, value, true);
    240      }
    241    }
    242  }
    243 
    244  return NS_OK;
    245 }
    246 
    247 }  // namespace mozilla::dom