content/base/src/nsReferencedElement.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/base/src/nsReferencedElement.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,240 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 sw=2 et tw=78: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsReferencedElement.h"
    1.11 +#include "nsContentUtils.h"
    1.12 +#include "nsIURI.h"
    1.13 +#include "nsBindingManager.h"
    1.14 +#include "nsEscape.h"
    1.15 +#include "nsXBLPrototypeBinding.h"
    1.16 +#include "nsIDOMNode.h"
    1.17 +#include "nsIDOMElement.h"
    1.18 +#include "nsCycleCollectionParticipant.h"
    1.19 +
    1.20 +void
    1.21 +nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI,
    1.22 +                           bool aWatch, bool aReferenceImage)
    1.23 +{
    1.24 +  NS_ABORT_IF_FALSE(aFromContent, "Reset() expects non-null content pointer");
    1.25 +
    1.26 +  Unlink();
    1.27 +
    1.28 +  if (!aURI)
    1.29 +    return;
    1.30 +
    1.31 +  nsAutoCString refPart;
    1.32 +  aURI->GetRef(refPart);
    1.33 +  // Unescape %-escapes in the reference. The result will be in the
    1.34 +  // origin charset of the URL, hopefully...
    1.35 +  NS_UnescapeURL(refPart);
    1.36 +
    1.37 +  nsAutoCString charset;
    1.38 +  aURI->GetOriginCharset(charset);
    1.39 +  nsAutoString ref;
    1.40 +  nsresult rv = nsContentUtils::ConvertStringFromEncoding(charset,
    1.41 +                                                          refPart,
    1.42 +                                                          ref);
    1.43 +  if (NS_FAILED(rv)) {
    1.44 +    // XXX Eww. If fallible malloc failed, using a conversion method that
    1.45 +    // assumes UTF-8 and doesn't handle UTF-8 errors.
    1.46 +    // https://bugzilla.mozilla.org/show_bug.cgi?id=951082
    1.47 +    CopyUTF8toUTF16(refPart, ref);
    1.48 +  }
    1.49 +  if (ref.IsEmpty())
    1.50 +    return;
    1.51 +
    1.52 +  // Get the current document
    1.53 +  nsIDocument *doc = aFromContent->GetCurrentDoc();
    1.54 +  if (!doc)
    1.55 +    return;
    1.56 +
    1.57 +  nsIContent* bindingParent = aFromContent->GetBindingParent();
    1.58 +  if (bindingParent) {
    1.59 +    nsXBLBinding* binding = bindingParent->GetXBLBinding();
    1.60 +    if (binding) {
    1.61 +      bool isEqualExceptRef;
    1.62 +      rv = aURI->EqualsExceptRef(binding->PrototypeBinding()->DocURI(),
    1.63 +                                 &isEqualExceptRef);
    1.64 +      if (NS_SUCCEEDED(rv) && isEqualExceptRef) {
    1.65 +        // XXX sXBL/XBL2 issue
    1.66 +        // Our content is an anonymous XBL element from a binding inside the
    1.67 +        // same document that the referenced URI points to. In order to avoid
    1.68 +        // the risk of ID collisions we restrict ourselves to anonymous
    1.69 +        // elements from this binding; specifically, URIs that are relative to
    1.70 +        // the binding document should resolve to the copy of the target
    1.71 +        // element that has been inserted into the bound document.
    1.72 +        // If the URI points to a different document we don't need this
    1.73 +        // restriction.
    1.74 +        nsINodeList* anonymousChildren =
    1.75 +          doc->BindingManager()->GetAnonymousNodesFor(bindingParent);
    1.76 +
    1.77 +        if (anonymousChildren) {
    1.78 +          uint32_t length;
    1.79 +          anonymousChildren->GetLength(&length);
    1.80 +          for (uint32_t i = 0; i < length && !mElement; ++i) {
    1.81 +            mElement =
    1.82 +              nsContentUtils::MatchElementId(anonymousChildren->Item(i), ref);
    1.83 +          }
    1.84 +        }
    1.85 +
    1.86 +        // We don't have watching working yet for XBL, so bail out here.
    1.87 +        return;
    1.88 +      }
    1.89 +    }
    1.90 +  }
    1.91 +
    1.92 +  bool isEqualExceptRef;
    1.93 +  rv = aURI->EqualsExceptRef(doc->GetDocumentURI(), &isEqualExceptRef);
    1.94 +  if (NS_FAILED(rv) || !isEqualExceptRef) {
    1.95 +    nsRefPtr<nsIDocument::ExternalResourceLoad> load;
    1.96 +    doc = doc->RequestExternalResource(aURI, aFromContent,
    1.97 +                                       getter_AddRefs(load));
    1.98 +    if (!doc) {
    1.99 +      if (!load || !aWatch) {
   1.100 +        // Nothing will ever happen here
   1.101 +        return;
   1.102 +      }
   1.103 +
   1.104 +      DocumentLoadNotification* observer =
   1.105 +        new DocumentLoadNotification(this, ref);
   1.106 +      mPendingNotification = observer;
   1.107 +      if (observer) {
   1.108 +        load->AddObserver(observer);
   1.109 +      }
   1.110 +      // Keep going so we set up our watching stuff a bit
   1.111 +    }
   1.112 +  }
   1.113 +
   1.114 +  if (aWatch) {
   1.115 +    nsCOMPtr<nsIAtom> atom = do_GetAtom(ref);
   1.116 +    if (!atom)
   1.117 +      return;
   1.118 +    atom.swap(mWatchID);
   1.119 +  }
   1.120 +
   1.121 +  mReferencingImage = aReferenceImage;
   1.122 +
   1.123 +  HaveNewDocument(doc, aWatch, ref);
   1.124 +}
   1.125 +
   1.126 +void
   1.127 +nsReferencedElement::ResetWithID(nsIContent* aFromContent, const nsString& aID,
   1.128 +                                 bool aWatch)
   1.129 +{
   1.130 +  nsIDocument *doc = aFromContent->GetCurrentDoc();
   1.131 +  if (!doc)
   1.132 +    return;
   1.133 +
   1.134 +  // XXX Need to take care of XBL/XBL2
   1.135 +
   1.136 +  if (aWatch) {
   1.137 +    nsCOMPtr<nsIAtom> atom = do_GetAtom(aID);
   1.138 +    if (!atom)
   1.139 +      return;
   1.140 +    atom.swap(mWatchID);
   1.141 +  }
   1.142 +
   1.143 +  mReferencingImage = false;
   1.144 +
   1.145 +  HaveNewDocument(doc, aWatch, aID);
   1.146 +}
   1.147 +
   1.148 +void
   1.149 +nsReferencedElement::HaveNewDocument(nsIDocument* aDocument, bool aWatch,
   1.150 +                                     const nsString& aRef)
   1.151 +{
   1.152 +  if (aWatch) {
   1.153 +    mWatchDocument = aDocument;
   1.154 +    if (mWatchDocument) {
   1.155 +      mElement = mWatchDocument->AddIDTargetObserver(mWatchID, Observe, this,
   1.156 +                                                     mReferencingImage);
   1.157 +    }
   1.158 +    return;
   1.159 +  }
   1.160 +  
   1.161 +  if (!aDocument) {
   1.162 +    return;
   1.163 +  }
   1.164 +
   1.165 +  Element *e = mReferencingImage ? aDocument->LookupImageElement(aRef) :
   1.166 +                                   aDocument->GetElementById(aRef);
   1.167 +  if (e) {
   1.168 +    mElement = e;
   1.169 +  }
   1.170 +}
   1.171 +
   1.172 +void
   1.173 +nsReferencedElement::Traverse(nsCycleCollectionTraversalCallback* aCB)
   1.174 +{
   1.175 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB, "mWatchDocument");
   1.176 +  aCB->NoteXPCOMChild(mWatchDocument);
   1.177 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB, "mContent");
   1.178 +  aCB->NoteXPCOMChild(mElement);
   1.179 +}
   1.180 +
   1.181 +void
   1.182 +nsReferencedElement::Unlink()
   1.183 +{
   1.184 +  if (mWatchDocument && mWatchID) {
   1.185 +    mWatchDocument->RemoveIDTargetObserver(mWatchID, Observe, this,
   1.186 +                                           mReferencingImage);
   1.187 +  }
   1.188 +  if (mPendingNotification) {
   1.189 +    mPendingNotification->Clear();
   1.190 +    mPendingNotification = nullptr;
   1.191 +  }
   1.192 +  mWatchDocument = nullptr;
   1.193 +  mWatchID = nullptr;
   1.194 +  mElement = nullptr;
   1.195 +  mReferencingImage = false;
   1.196 +}
   1.197 +
   1.198 +bool
   1.199 +nsReferencedElement::Observe(Element* aOldElement,
   1.200 +                             Element* aNewElement, void* aData)
   1.201 +{
   1.202 +  nsReferencedElement* p = static_cast<nsReferencedElement*>(aData);
   1.203 +  if (p->mPendingNotification) {
   1.204 +    p->mPendingNotification->SetTo(aNewElement);
   1.205 +  } else {
   1.206 +    NS_ASSERTION(aOldElement == p->mElement, "Failed to track content!");
   1.207 +    ChangeNotification* watcher =
   1.208 +      new ChangeNotification(p, aOldElement, aNewElement);
   1.209 +    p->mPendingNotification = watcher;
   1.210 +    nsContentUtils::AddScriptRunner(watcher);
   1.211 +  }
   1.212 +  bool keepTracking = p->IsPersistent();
   1.213 +  if (!keepTracking) {
   1.214 +    p->mWatchDocument = nullptr;
   1.215 +    p->mWatchID = nullptr;
   1.216 +  }
   1.217 +  return keepTracking;
   1.218 +}
   1.219 +
   1.220 +NS_IMPL_ISUPPORTS_INHERITED0(nsReferencedElement::ChangeNotification,
   1.221 +                             nsRunnable)
   1.222 +
   1.223 +NS_IMPL_ISUPPORTS(nsReferencedElement::DocumentLoadNotification,
   1.224 +                  nsIObserver)
   1.225 +
   1.226 +NS_IMETHODIMP
   1.227 +nsReferencedElement::DocumentLoadNotification::Observe(nsISupports* aSubject,
   1.228 +                                                       const char* aTopic,
   1.229 +                                                       const char16_t* aData)
   1.230 +{
   1.231 +  NS_ASSERTION(PL_strcmp(aTopic, "external-resource-document-created") == 0,
   1.232 +               "Unexpected topic");
   1.233 +  if (mTarget) {
   1.234 +    nsCOMPtr<nsIDocument> doc = do_QueryInterface(aSubject);
   1.235 +    mTarget->mPendingNotification = nullptr;
   1.236 +    NS_ASSERTION(!mTarget->mElement, "Why do we have content here?");
   1.237 +    // If we got here, that means we had Reset() called with aWatch ==
   1.238 +    // true.  So keep watching if IsPersistent().
   1.239 +    mTarget->HaveNewDocument(doc, mTarget->IsPersistent(), mRef);
   1.240 +    mTarget->ElementChanged(nullptr, mTarget->mElement);
   1.241 +  }
   1.242 +  return NS_OK;
   1.243 +}

mercurial