tor-browser

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

nsXULPrototypeDocument.cpp (14569B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "nsXULPrototypeDocument.h"
      7 
      8 #include "jsapi.h"
      9 #include "jsfriendapi.h"
     10 #include "mozilla/BasePrincipal.h"
     11 #include "mozilla/DeclarationBlock.h"
     12 #include "mozilla/dom/BindingUtils.h"
     13 #include "mozilla/dom/CustomElementRegistry.h"
     14 #include "mozilla/dom/Document.h"
     15 #include "mozilla/dom/Element.h"
     16 #include "mozilla/dom/Text.h"
     17 #include "nsAString.h"
     18 #include "nsCCUncollectableMarker.h"
     19 #include "nsContentUtils.h"
     20 #include "nsDOMCID.h"
     21 #include "nsIObjectInputStream.h"
     22 #include "nsIObjectOutputStream.h"
     23 #include "nsIPrincipal.h"
     24 #include "nsIScriptObjectPrincipal.h"
     25 #include "nsIURI.h"
     26 #include "nsJSPrincipals.h"
     27 #include "nsNodeInfoManager.h"
     28 #include "nsString.h"
     29 #include "nsXULElement.h"
     30 #include "nsXULPrototypeCache.h"
     31 #include "xpcpublic.h"
     32 
     33 using namespace mozilla;
     34 using namespace mozilla::dom;
     35 using mozilla::dom::DestroyProtoAndIfaceCache;
     36 
     37 uint32_t nsXULPrototypeDocument::gRefCnt;
     38 
     39 //----------------------------------------------------------------------
     40 //
     41 // ctors, dtors, n' stuff
     42 //
     43 
     44 nsXULPrototypeDocument::nsXULPrototypeDocument()
     45    : mRoot(nullptr), mLoaded(false), mCCGeneration(0), mWasL10nCached(false) {
     46  ++gRefCnt;
     47 }
     48 
     49 nsresult nsXULPrototypeDocument::Init() {
     50  mNodeInfoManager = new nsNodeInfoManager(nullptr, nullptr);
     51  return NS_OK;
     52 }
     53 
     54 nsXULPrototypeDocument::~nsXULPrototypeDocument() {
     55  if (mRoot) mRoot->ReleaseSubtree();
     56 }
     57 
     58 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument)
     59 
     60 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeDocument)
     61  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeWaiters)
     62 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     63 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeDocument)
     64  if (nsCCUncollectableMarker::InGeneration(cb, tmp->mCCGeneration)) {
     65    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
     66  }
     67  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
     68  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
     69 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     70 
     71 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPrototypeDocument)
     72  NS_INTERFACE_MAP_ENTRY(nsISerializable)
     73  NS_INTERFACE_MAP_ENTRY(nsISupports)
     74 NS_INTERFACE_MAP_END
     75 
     76 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPrototypeDocument)
     77 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPrototypeDocument)
     78 
     79 NS_IMETHODIMP
     80 NS_NewXULPrototypeDocument(nsXULPrototypeDocument** aResult) {
     81  *aResult = nullptr;
     82  RefPtr<nsXULPrototypeDocument> doc = new nsXULPrototypeDocument();
     83 
     84  nsresult rv = doc->Init();
     85  if (NS_FAILED(rv)) {
     86    return rv;
     87  }
     88 
     89  doc.forget(aResult);
     90  return rv;
     91 }
     92 
     93 //----------------------------------------------------------------------
     94 //
     95 // nsISerializable methods
     96 //
     97 
     98 NS_IMETHODIMP
     99 nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream) {
    100  nsCOMPtr<nsISupports> supports;
    101  nsresult rv = aStream->ReadObject(true, getter_AddRefs(supports));
    102  if (NS_FAILED(rv)) {
    103    return rv;
    104  }
    105  mURI = do_QueryInterface(supports);
    106 
    107  // nsIPrincipal mNodeInfoManager->mPrincipal
    108  nsAutoCString JSON;
    109  rv = aStream->ReadCString(JSON);
    110  if (NS_FAILED(rv)) {
    111    return rv;
    112  }
    113  nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::FromJSON(JSON);
    114 
    115  // Better safe than sorry....
    116  mNodeInfoManager->SetDocumentPrincipal(principal);
    117 
    118  rv = aStream->ReadBoolean(&mWasL10nCached);
    119  if (NS_FAILED(rv)) {
    120    return rv;
    121  }
    122 
    123  mRoot = new nsXULPrototypeElement();
    124 
    125  // mozilla::dom::NodeInfo table
    126  nsTArray<RefPtr<mozilla::dom::NodeInfo>> nodeInfos;
    127 
    128  uint32_t count, i;
    129  rv = aStream->Read32(&count);
    130  if (NS_FAILED(rv)) {
    131    return rv;
    132  }
    133  nsAutoString namespaceURI, prefixStr, localName;
    134  bool prefixIsNull;
    135  RefPtr<nsAtom> prefix;
    136  for (i = 0; i < count; ++i) {
    137    rv = aStream->ReadString(namespaceURI);
    138    if (NS_FAILED(rv)) {
    139      return rv;
    140    }
    141    rv = aStream->ReadBoolean(&prefixIsNull);
    142    if (NS_FAILED(rv)) {
    143      return rv;
    144    }
    145    if (prefixIsNull) {
    146      prefix = nullptr;
    147    } else {
    148      rv = aStream->ReadString(prefixStr);
    149      if (NS_FAILED(rv)) {
    150        return rv;
    151      }
    152      prefix = NS_Atomize(prefixStr);
    153    }
    154    rv = aStream->ReadString(localName);
    155    if (NS_FAILED(rv)) {
    156      return rv;
    157    }
    158 
    159    RefPtr<mozilla::dom::NodeInfo> nodeInfo;
    160    // Using UINT16_MAX here as we don't know which nodeinfos will be
    161    // used for attributes and which for elements. And that doesn't really
    162    // matter.
    163    rv = mNodeInfoManager->GetNodeInfo(localName, prefix, namespaceURI,
    164                                       UINT16_MAX, getter_AddRefs(nodeInfo));
    165    if (NS_FAILED(rv)) {
    166      return rv;
    167    }
    168    nodeInfos.AppendElement(nodeInfo);
    169  }
    170 
    171  // Document contents
    172  uint32_t type;
    173  while (NS_SUCCEEDED(rv)) {
    174    rv = aStream->Read32(&type);
    175    if (NS_FAILED(rv)) {
    176      return rv;
    177      break;
    178    }
    179 
    180    if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_PI) {
    181      RefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
    182 
    183      rv = pi->Deserialize(aStream, this, mURI, &nodeInfos);
    184      if (NS_FAILED(rv)) {
    185        return rv;
    186      }
    187      rv = AddProcessingInstruction(pi);
    188      if (NS_FAILED(rv)) {
    189        return rv;
    190      }
    191    } else if ((nsXULPrototypeNode::Type)type ==
    192               nsXULPrototypeNode::eType_Element) {
    193      rv = mRoot->Deserialize(aStream, this, mURI, &nodeInfos);
    194      if (NS_FAILED(rv)) {
    195        return rv;
    196      }
    197      break;
    198    } else {
    199      MOZ_ASSERT_UNREACHABLE("Unexpected prototype node type");
    200      return NS_ERROR_FAILURE;
    201    }
    202  }
    203 
    204  return NotifyLoadDone();
    205 }
    206 
    207 static void GetNodeInfos(nsXULPrototypeElement* aPrototype,
    208                         nsTArray<RefPtr<mozilla::dom::NodeInfo>>& aArray) {
    209  if (aArray.IndexOf(aPrototype->mNodeInfo) == aArray.NoIndex) {
    210    aArray.AppendElement(aPrototype->mNodeInfo);
    211  }
    212 
    213  // Search attributes
    214  for (nsXULPrototypeAttribute& attr : aPrototype->mAttributes) {
    215    RefPtr<mozilla::dom::NodeInfo> ni;
    216    nsAttrName* name = &attr.mName;
    217    if (name->IsAtom()) {
    218      ni = aPrototype->mNodeInfo->NodeInfoManager()->GetNodeInfo(
    219          name->Atom(), nullptr, kNameSpaceID_None, nsINode::ATTRIBUTE_NODE);
    220    } else {
    221      ni = name->NodeInfo();
    222    }
    223 
    224    if (aArray.IndexOf(ni) == aArray.NoIndex) {
    225      aArray.AppendElement(ni);
    226    }
    227  }
    228 
    229  // Search children
    230  for (nsXULPrototypeNode* child : aPrototype->mChildren) {
    231    if (child->mType == nsXULPrototypeNode::eType_Element) {
    232      GetNodeInfos(static_cast<nsXULPrototypeElement*>(child), aArray);
    233    }
    234  }
    235 }
    236 
    237 NS_IMETHODIMP
    238 nsXULPrototypeDocument::Write(nsIObjectOutputStream* aStream) {
    239  nsresult rv;
    240 
    241  rv = aStream->WriteCompoundObject(mURI, NS_GET_IID(nsIURI), true);
    242 
    243  // nsIPrincipal mNodeInfoManager->mPrincipal
    244  nsAutoCString JSON;
    245  mozilla::BasePrincipal::Cast(mNodeInfoManager->DocumentPrincipal())
    246      ->ToJSON(JSON);
    247  nsresult tmp = aStream->WriteStringZ(JSON.get());
    248  if (NS_FAILED(tmp)) {
    249    rv = tmp;
    250  }
    251 
    252 #ifdef DEBUG
    253  // XXX Worrisome if we're caching things without system principal.
    254  if (!mNodeInfoManager->DocumentPrincipal()->IsSystemPrincipal()) {
    255    NS_WARNING("Serializing document without system principal");
    256  }
    257 #endif
    258 
    259  tmp = aStream->WriteBoolean(mWasL10nCached);
    260  if (NS_FAILED(tmp)) {
    261    rv = tmp;
    262  }
    263 
    264  // mozilla::dom::NodeInfo table
    265  nsTArray<RefPtr<mozilla::dom::NodeInfo>> nodeInfos;
    266  if (mRoot) {
    267    GetNodeInfos(mRoot, nodeInfos);
    268  }
    269 
    270  uint32_t nodeInfoCount = nodeInfos.Length();
    271  tmp = aStream->Write32(nodeInfoCount);
    272  if (NS_FAILED(tmp)) {
    273    rv = tmp;
    274  }
    275  uint32_t i;
    276  for (i = 0; i < nodeInfoCount; ++i) {
    277    mozilla::dom::NodeInfo* nodeInfo = nodeInfos[i];
    278    NS_ENSURE_TRUE(nodeInfo, NS_ERROR_FAILURE);
    279 
    280    nsAutoString namespaceURI;
    281    nodeInfo->GetNamespaceURI(namespaceURI);
    282    tmp = aStream->WriteWStringZ(namespaceURI.get());
    283    if (NS_FAILED(tmp)) {
    284      rv = tmp;
    285    }
    286 
    287    nsAutoString prefix;
    288    nodeInfo->GetPrefix(prefix);
    289    bool nullPrefix = DOMStringIsNull(prefix);
    290    tmp = aStream->WriteBoolean(nullPrefix);
    291    if (NS_FAILED(tmp)) {
    292      rv = tmp;
    293    }
    294    if (!nullPrefix) {
    295      tmp = aStream->WriteWStringZ(prefix.get());
    296      if (NS_FAILED(tmp)) {
    297        rv = tmp;
    298      }
    299    }
    300 
    301    nsAutoString localName;
    302    nodeInfo->GetName(localName);
    303    tmp = aStream->WriteWStringZ(localName.get());
    304    if (NS_FAILED(tmp)) {
    305      rv = tmp;
    306    }
    307  }
    308 
    309  // Now serialize the document contents
    310  uint32_t count = mProcessingInstructions.Length();
    311  for (i = 0; i < count; ++i) {
    312    nsXULPrototypePI* pi = mProcessingInstructions[i];
    313    tmp = pi->Serialize(aStream, this, &nodeInfos);
    314    if (NS_FAILED(tmp)) {
    315      rv = tmp;
    316    }
    317  }
    318 
    319  if (mRoot) {
    320    tmp = mRoot->Serialize(aStream, this, &nodeInfos);
    321    if (NS_FAILED(tmp)) {
    322      rv = tmp;
    323    }
    324  }
    325 
    326  return rv;
    327 }
    328 
    329 //----------------------------------------------------------------------
    330 //
    331 
    332 nsresult nsXULPrototypeDocument::InitPrincipal(nsIURI* aURI,
    333                                               nsIPrincipal* aPrincipal) {
    334  NS_ENSURE_ARG_POINTER(aURI);
    335 
    336  mURI = aURI;
    337  mNodeInfoManager->SetDocumentPrincipal(aPrincipal);
    338  return NS_OK;
    339 }
    340 
    341 nsIURI* nsXULPrototypeDocument::GetURI() {
    342  NS_ASSERTION(mURI, "null URI");
    343  return mURI;
    344 }
    345 
    346 nsXULPrototypeElement* nsXULPrototypeDocument::GetRootElement() {
    347  return mRoot;
    348 }
    349 
    350 void nsXULPrototypeDocument::SetRootElement(nsXULPrototypeElement* aElement) {
    351  mRoot = aElement;
    352 }
    353 
    354 nsresult nsXULPrototypeDocument::AddProcessingInstruction(
    355    nsXULPrototypePI* aPI) {
    356  MOZ_ASSERT(aPI, "null ptr");
    357  // XXX(Bug 1631371) Check if this should use a fallible operation as it
    358  // pretended earlier, or change the return type to void.
    359  mProcessingInstructions.AppendElement(aPI);
    360  return NS_OK;
    361 }
    362 
    363 const nsTArray<RefPtr<nsXULPrototypePI>>&
    364 nsXULPrototypeDocument::GetProcessingInstructions() const {
    365  return mProcessingInstructions;
    366 }
    367 
    368 nsIPrincipal* nsXULPrototypeDocument::DocumentPrincipal() {
    369  MOZ_ASSERT(mNodeInfoManager, "missing nodeInfoManager");
    370  return mNodeInfoManager->DocumentPrincipal();
    371 }
    372 
    373 void nsXULPrototypeDocument::SetDocumentPrincipal(nsIPrincipal* aPrincipal) {
    374  mNodeInfoManager->SetDocumentPrincipal(aPrincipal);
    375 }
    376 
    377 void nsXULPrototypeDocument::MarkInCCGeneration(uint32_t aCCGeneration) {
    378  mCCGeneration = aCCGeneration;
    379 }
    380 
    381 nsNodeInfoManager* nsXULPrototypeDocument::GetNodeInfoManager() {
    382  return mNodeInfoManager;
    383 }
    384 
    385 nsresult nsXULPrototypeDocument::AwaitLoadDone(Callback&& aCallback,
    386                                               bool* aResult) {
    387  nsresult rv = NS_OK;
    388 
    389  *aResult = mLoaded;
    390 
    391  if (!mLoaded) {
    392    // XXX(Bug 1631371) Check if this should use a fallible operation as it
    393    // pretended earlier, or change the return type to void.
    394    mPrototypeWaiters.AppendElement(std::move(aCallback));
    395  }
    396 
    397  return rv;
    398 }
    399 
    400 nsresult nsXULPrototypeDocument::NotifyLoadDone() {
    401  // Call back to each XUL document that raced to start the same
    402  // prototype document load, lost the race, but hit the XUL
    403  // prototype cache because the winner filled the cache with
    404  // the not-yet-loaded prototype object.
    405 
    406  mLoaded = true;
    407 
    408  for (uint32_t i = mPrototypeWaiters.Length(); i > 0;) {
    409    --i;
    410    mPrototypeWaiters[i]();
    411  }
    412  mPrototypeWaiters.Clear();
    413 
    414  return NS_OK;
    415 }
    416 
    417 void nsXULPrototypeDocument::SetIsL10nCached(bool aIsCached) {
    418  mWasL10nCached = aIsCached;
    419 }
    420 
    421 void nsXULPrototypeDocument::RebuildPrototypeFromElement(
    422    nsXULPrototypeElement* aPrototype, Element* aElement, bool aDeep) {
    423  NodeInfo* oldNodeInfo = aElement->NodeInfo();
    424  RefPtr<NodeInfo> newNodeInfo = mNodeInfoManager->GetNodeInfo(
    425      oldNodeInfo->NameAtom(), oldNodeInfo->GetPrefixAtom(),
    426      oldNodeInfo->NamespaceID(), nsINode::ELEMENT_NODE);
    427  aPrototype->mNodeInfo = newNodeInfo;
    428 
    429  // First replace the prototype attributes with the new ones from this element.
    430  aPrototype->mAttributes.Clear();
    431 
    432  uint32_t count = aElement->GetAttrCount();
    433  nsXULPrototypeAttribute* protoAttr =
    434      aPrototype->mAttributes.AppendElements(count);
    435  for (uint32_t index = 0; index < count; index++) {
    436    BorrowedAttrInfo attr = aElement->GetAttrInfoAt(index);
    437 
    438    if (attr.mName->IsAtom()) {
    439      protoAttr->mName.SetTo(attr.mName->Atom());
    440    } else {
    441      NodeInfo* oldNodeInfo = attr.mName->NodeInfo();
    442      RefPtr<NodeInfo> newNodeInfo = mNodeInfoManager->GetNodeInfo(
    443          oldNodeInfo->NameAtom(), oldNodeInfo->GetPrefixAtom(),
    444          oldNodeInfo->NamespaceID(), nsINode::ATTRIBUTE_NODE);
    445      protoAttr->mName.SetTo(newNodeInfo);
    446    }
    447    protoAttr->mValue.SetTo(*attr.mValue);
    448    if (protoAttr->mValue.Type() == nsAttrValue::eCSSDeclaration) {
    449      // Ensure declarations get copied-on-write if needed.
    450      protoAttr->mValue.GetCSSDeclarationValue()->SetImmutable();
    451    }
    452    protoAttr++;
    453  }
    454 
    455  // Make sure the mIsAtom is correct in case this prototype element has been
    456  // completely rebuilt.
    457  CustomElementData* ceData = aElement->GetCustomElementData();
    458  nsAtom* isAtom = ceData ? ceData->GetIs(aElement) : nullptr;
    459  aPrototype->mIsAtom = isAtom;
    460 
    461  if (aDeep) {
    462    // We have to rebuild the prototype children from this element.
    463    // First release the tree under this element.
    464    aPrototype->ReleaseSubtree();
    465 
    466    RefPtr<nsXULPrototypeNode>* children =
    467        aPrototype->mChildren.AppendElements(aElement->GetChildCount());
    468    for (nsIContent* child = aElement->GetFirstChild(); child;
    469         child = child->GetNextSibling()) {
    470      if (child->IsElement()) {
    471        Element* element = child->AsElement();
    472        RefPtr<nsXULPrototypeElement> elemProto = new nsXULPrototypeElement;
    473        RebuildPrototypeFromElement(elemProto, element, true);
    474        *children = elemProto;
    475      } else if (child->IsText()) {
    476        Text* text = child->AsText();
    477        RefPtr<nsXULPrototypeText> textProto = new nsXULPrototypeText();
    478        text->AppendTextTo(textProto->mValue);
    479        *children = textProto;
    480      } else {
    481        MOZ_ASSERT(false, "We handle only elements and text nodes here.");
    482      }
    483 
    484      children++;
    485    }
    486  }
    487 }
    488 
    489 void nsXULPrototypeDocument::RebuildL10nPrototype(Element* aElement,
    490                                                  bool aDeep) {
    491  if (mWasL10nCached) {
    492    return;
    493  }
    494 
    495  MOZ_ASSERT(aElement->HasAttr(nsGkAtoms::datal10nid));
    496 
    497  Document* doc = aElement->OwnerDoc();
    498  if (RefPtr<nsXULPrototypeElement> proto =
    499          doc->mL10nProtoElements.Get(aElement)) {
    500    RebuildPrototypeFromElement(proto, aElement, aDeep);
    501  }
    502 }