dom/xbl/nsXBLPrototypeBinding.h
author Cosmin Sabou <csabou@mozilla.com>
Sat, 02 Mar 2019 20:40:01 +0200
changeset 462132 bf3cbcc825276823afba2778333cf62cfa75039c
parent 460457 2681b6f4e98fd7447098a5467389297ea1ba17d6
child 462534 5edf9199407cff51e09fda265ed3fe4f1386e1cb
permissions -rw-r--r--
Backed out 16 changesets (bug 827976) for causing bug 1532054. a=backout Backed out changeset 54c0b12443ed (bug 827976) Backed out changeset 92b45080d080 (bug 827976) Backed out changeset 443087a359f9 (bug 827976) Backed out changeset f976c2d4cebb (bug 827976) Backed out changeset 66cffb171024 (bug 827976) Backed out changeset 845e1d0b2402 (bug 827976) Backed out changeset fd94066a1d76 (bug 827976) Backed out changeset e253b264e7bd (bug 827976) Backed out changeset 07ef335770a8 (bug 827976) Backed out changeset 9d3805d77b99 (bug 827976) Backed out changeset eed600ceb606 (bug 827976) Backed out changeset 3b64368cff52 (bug 827976) Backed out changeset 69d0378e0c09 (bug 827976) Backed out changeset 9203871a5c6f (bug 827976) Backed out changeset 96a507c818e2 (bug 827976) Backed out changeset e254496ff95b (bug 827976)

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsXBLPrototypeBinding_h__
#define nsXBLPrototypeBinding_h__

#include "nsAutoPtr.h"
#include "nsClassHashtable.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "nsICSSLoaderObserver.h"
#include "nsInterfaceHashtable.h"
#include "nsXBLDocumentInfo.h"
#include "nsXBLProtoImpl.h"
#include "nsXBLProtoImplMethod.h"
#include "nsXBLPrototypeHandler.h"
#include "nsXBLPrototypeResources.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/StyleSheet.h"

class nsAtom;
class nsIContent;

class nsXBLAttributeEntry;
class nsXBLBinding;
class nsXBLProtoImplField;

// *********************************************************************/
// The XBLPrototypeBinding class

// Instances of this class are owned by the nsXBLDocumentInfo object returned
// by XBLDocumentInfo().  Consumers who want to refcount things should refcount
// that.
class nsXBLPrototypeBinding final
    : public mozilla::SupportsWeakPtr<nsXBLPrototypeBinding> {
 public:
  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsXBLPrototypeBinding)

  mozilla::dom::Element* GetBindingElement() const { return mBinding; }
  void SetBindingElement(mozilla::dom::Element* aElement);

  nsIURI* BindingURI() const { return mBindingURI; }
  nsIURI* AlternateBindingURI() const { return mAlternateBindingURI; }
  nsIURI* DocURI() const { return mXBLDocInfoWeak->DocumentURI(); }
  nsIURI* GetBaseBindingURI() const { return mBaseBindingURI; }

  // Checks if aURI refers to this binding by comparing to both possible
  // binding URIs.
  bool CompareBindingURI(nsIURI* aURI) const;

  bool GetAllowScripts() const;

  nsresult BindingAttached(nsIContent* aBoundElement);
  nsresult BindingDetached(nsIContent* aBoundElement);

  // aBoundElement is passed in here because we need to get owner document
  // and PresContext in nsXBLResourceLoader::LoadResources().
  bool LoadResources(nsIContent* aBoundElement);
  nsresult AddResource(nsAtom* aResourceType, const nsAString& aSrc);

  bool InheritsStyle() const { return mInheritStyle; }
  void SetInheritsStyle(bool aInheritStyle) { mInheritStyle = aInheritStyle; }

  nsXBLPrototypeHandler* GetPrototypeHandlers() { return mPrototypeHandler; }
  void SetPrototypeHandlers(nsXBLPrototypeHandler* aHandler) {
    mPrototypeHandler = aHandler;
  }

  nsXBLProtoImplAnonymousMethod* GetConstructor();
  nsresult SetConstructor(nsXBLProtoImplAnonymousMethod* aConstructor);
  nsXBLProtoImplAnonymousMethod* GetDestructor();
  nsresult SetDestructor(nsXBLProtoImplAnonymousMethod* aDestructor);

  nsXBLProtoImplField* FindField(const nsString& aFieldName) const {
    return mImplementation ? mImplementation->FindField(aFieldName) : nullptr;
  }

  // Resolve all the fields for this binding on the object |obj|.
  // False return means a JS exception was set.
  bool ResolveAllFields(JSContext* cx, JS::Handle<JSObject*> obj) const {
    return !mImplementation || mImplementation->ResolveAllFields(cx, obj);
  }

  // Undefine all our fields from object |obj| (which should be a
  // JSObject for a bound element).
  void UndefineFields(JSContext* cx, JS::Handle<JSObject*> obj) const {
    if (mImplementation) {
      mImplementation->UndefineFields(cx, obj);
    }
  }

  const nsString& ClassName() const {
    return mImplementation ? mImplementation->mClassName : EmptyString();
  }

  nsresult InitClass(const nsString& aClassName, JSContext* aContext,
                     JS::Handle<JSObject*> aScriptObject,
                     JS::MutableHandle<JSObject*> aClassObject, bool* aNew);

  nsresult ConstructInterfaceTable(const nsAString& aImpls);

  void SetImplementation(nsXBLProtoImpl* aImpl) { mImplementation = aImpl; }
  nsXBLProtoImpl* GetImplementation() { return mImplementation; }
  nsresult InstallImplementation(nsXBLBinding* aBinding);
  bool HasImplementation() const { return mImplementation != nullptr; }

  void AttributeChanged(nsAtom* aAttribute, int32_t aNameSpaceID,
                        bool aRemoveFlag,
                        mozilla::dom::Element* aChangedElement,
                        nsIContent* aAnonymousContent, bool aNotify);

  void SetBasePrototype(nsXBLPrototypeBinding* aBinding);
  nsXBLPrototypeBinding* GetBasePrototype() { return mBaseBinding; }

  nsXBLDocumentInfo* XBLDocumentInfo() const { return mXBLDocInfoWeak; }
  bool IsChrome() { return mXBLDocInfoWeak->IsChrome(); }

  void SetInitialAttributes(mozilla::dom::Element* aBoundElement,
                            nsIContent* aAnonymousContent);

  void AppendStyleSheet(mozilla::StyleSheet* aSheet);
  void RemoveStyleSheet(mozilla::StyleSheet* aSheet);
  void InsertStyleSheetAt(size_t aIndex, mozilla::StyleSheet* aSheet);
  mozilla::StyleSheet* StyleSheetAt(size_t aIndex) const;
  size_t SheetCount() const;
  bool HasStyleSheets() const;
  void AppendStyleSheetsTo(nsTArray<mozilla::StyleSheet*>& aResult) const;

  const RawServoAuthorStyles* GetServoStyles() const {
    return mResources ? mResources->GetServoStyles() : nullptr;
  }

  void SyncServoStyles() {
    MOZ_ASSERT(mResources);
    mResources->SyncServoStyles();
  }

  RawServoAuthorStyles* GetServoStyles() {
    return mResources
               ? const_cast<RawServoAuthorStyles*>(mResources->GetServoStyles())
               : nullptr;
  }

  mozilla::ServoStyleRuleMap* GetServoStyleRuleMap() {
    return mResources ? mResources->GetServoStyleRuleMap() : nullptr;
  }

  nsresult FlushSkinSheets();

  nsAtom* GetBaseTag(int32_t* aNamespaceID);
  void SetBaseTag(int32_t aNamespaceID, nsAtom* aTag);

  bool ImplementsInterface(REFNSIID aIID) const;

  nsresult AddResourceListener(nsIContent* aBoundElement);

  void Initialize();

  nsresult ResolveBaseBinding();

  const nsCOMArray<nsXBLKeyEventHandler>* GetKeyEventHandlers() {
    if (!mKeyHandlersRegistered) {
      CreateKeyHandlers();
      mKeyHandlersRegistered = true;
    }

    return &mKeyHandlers;
  }

 private:
  nsresult Read(nsIObjectInputStream* aStream, nsXBLDocumentInfo* aDocInfo,
                mozilla::dom::Document* aDocument, uint8_t aFlags);

  /**
   * Read a new binding from the stream aStream into the xbl document aDocument.
   * aDocInfo should be the xbl document info for the binding document.
   * aFlags can contain XBLBinding_Serialize_InheritStyle to indicate that
   * mInheritStyle flag should be set, and XBLBinding_Serialize_IsFirstBinding
   * to indicate the first binding in a document.
   * XBLBinding_Serialize_BindToUntrustedContent indicates that
   * nsXBLPrototypeBinding::mBindToUntrustedContent should be true.
   */
 public:
  static nsresult ReadNewBinding(nsIObjectInputStream* aStream,
                                 nsXBLDocumentInfo* aDocInfo,
                                 mozilla::dom::Document* aDocument,
                                 uint8_t aFlags);

  /**
   * Write this binding to the stream.
   */
  nsresult Write(nsIObjectOutputStream* aStream);

  /**
   * Read a content node from aStream and return it in aChild.
   * aDocument and aNim are the document and node info manager for the document
   * the child will be inserted into.
   */
  nsresult ReadContentNode(nsIObjectInputStream* aStream,
                           mozilla::dom::Document* aDocument,
                           nsNodeInfoManager* aNim, nsIContent** aChild);

  /**
   * Write the content node aNode to aStream.
   *
   * This method is called recursively for each child descendant. For the
   * topmost call, aNode must be an element.
   *
   * Text, CDATA and comment nodes are serialized as:
   *   the constant XBLBinding_Serialize_TextNode,
   * XBLBinding_Serialize_CDATANode or XBLBinding_Serialize_CommentNode the text
   * for the node Elements are serialized in the following format: node's
   * namespace, written with WriteNamespace node's namespace prefix node's tag
   *   32-bit attribute count
   *   table of attributes:
   *     attribute's namespace, written with WriteNamespace
   *     attribute's namespace prefix
   *     attribute's tag
   *     attribute's value
   *   attribute forwarding table:
   *     source namespace
   *     source attribute
   *     destination namespace
   *     destination attribute
   *   the constant XBLBinding_Serialize_NoMoreAttributes
   *   32-bit count of the number of child nodes
   *     each child node is serialized in the same manner in sequence
   *   the constant XBLBinding_Serialize_NoContent
   */
  nsresult WriteContentNode(nsIObjectOutputStream* aStream, nsIContent* aNode);

  /**
   * Read or write a namespace id from or to aStream. If the namespace matches
   * one of the built-in ones defined in nsNameSpaceManager.h, it will be
   * written as a single byte with that value. Otherwise,
   * XBLBinding_Serialize_CustomNamespace is written out, followed by a string
   * written with writeWStringZ.
   */
  nsresult ReadNamespace(nsIObjectInputStream* aStream, int32_t& aNameSpaceID);
  nsresult WriteNamespace(nsIObjectOutputStream* aStream, int32_t aNameSpaceID);

 public:
  nsXBLPrototypeBinding();
  ~nsXBLPrototypeBinding();

  // Init must be called after construction to initialize the prototype
  // binding.  It may well throw errors (eg on out-of-memory).  Do not confuse
  // this with the Initialize() method, which must be called after the
  // binding's handlers, properties, etc are all set.
  nsresult Init(const nsACString& aRef, nsXBLDocumentInfo* aInfo,
                mozilla::dom::Element* aElement, bool aFirstBinding = false);

  void Traverse(nsCycleCollectionTraversalCallback& cb) const;
  void Unlink();
  void Trace(const TraceCallbacks& aCallbacks, void* aClosure) const;

  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;

  // Internal member functions.
 public:
  /**
   * GetImmediateChild locates the immediate child of our binding element which
   * has the localname given by aTag and is in the XBL namespace.
   */
  mozilla::dom::Element* GetImmediateChild(nsAtom* aTag);
  mozilla::dom::Element* LocateInstance(mozilla::dom::Element* aBoundElt,
                                        nsIContent* aTemplRoot,
                                        nsIContent* aCopyRoot,
                                        mozilla::dom::Element* aTemplChild);

  bool SimpleScopeChain() const { return mSimpleScopeChain; }
  bool BindToUntrustedContent() const { return mBindToUntrustedContent; }

  typedef nsClassHashtable<nsRefPtrHashKey<nsAtom>, nsXBLAttributeEntry>
      InnerAttributeTable;

 protected:
  // Ensure that mAttributeTable has been created.
  void EnsureAttributeTable();
  // Ad an entry to the attribute table
  void AddToAttributeTable(int32_t aSourceNamespaceID, nsAtom* aSourceTag,
                           int32_t aDestNamespaceID, nsAtom* aDestTag,
                           mozilla::dom::Element* aContent);
  void ConstructAttributeTable(mozilla::dom::Element* aElement);
  void CreateKeyHandlers();

 private:
  void EnsureResources();

  // MEMBER VARIABLES
 protected:
  nsCOMPtr<nsIURI> mBindingURI;
  nsCOMPtr<nsIURI> mAlternateBindingURI;  // Alternate id-less URI that is only
                                          // non-null on the first binding.
  RefPtr<mozilla::dom::Element>
      mBinding;  // Strong. We own a ref to our content element in the binding
                 // doc.
  nsAutoPtr<nsXBLPrototypeHandler>
      mPrototypeHandler;  // Strong. DocInfo owns us, and we own the handlers.

  // the url of the base binding
  nsCOMPtr<nsIURI> mBaseBindingURI;

  nsXBLProtoImpl* mImplementation;  // Our prototype implementation (includes
                                    // methods, properties, fields, the
                                    // constructor, and the destructor).

  // Weak.  The docinfo will own our base binding.
  mozilla::WeakPtr<nsXBLPrototypeBinding> mBaseBinding;
  // FIXME(emilio): This is dead code now.
  bool mInheritStyle;
  bool mCheckedBaseProto;
  bool mKeyHandlersRegistered;
  // FIXME(emilio): This is dead code now.
  bool mBindToUntrustedContent;
  // True if constructors, handlers, etc for this binding would skip the scope
  // chain for parent elements and go directly to the document.
  // FIXME(emilio): This is dead code now.
  bool mSimpleScopeChain;

  nsAutoPtr<nsXBLPrototypeResources>
      mResources;  // If we have any resources, this will be non-null.

  nsXBLDocumentInfo* mXBLDocInfoWeak;  // A pointer back to our doc info.  Weak,
                                       // since it owns us.

  // A table for attribute containers. Namespace IDs are used as
  // keys in the table. Containers are InnerAttributeTables.
  // This table is used to efficiently handle attribute changes.
  nsAutoPtr<nsClassHashtable<nsUint32HashKey, InnerAttributeTable>>
      mAttributeTable;

  class IIDHashKey : public PLDHashEntryHdr {
   public:
    typedef const nsIID& KeyType;
    typedef const nsIID* KeyTypePointer;

    explicit IIDHashKey(const nsIID* aKey) : mKey(*aKey) {}
    IIDHashKey(const IIDHashKey& aOther) : mKey(aOther.GetKey()) {}
    ~IIDHashKey() {}

    KeyType GetKey() const { return mKey; }
    bool KeyEquals(const KeyTypePointer aKey) const {
      return mKey.Equals(*aKey);
    }

    static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
    static PLDHashNumber HashKey(const KeyTypePointer aKey) {
      // Just use the 32-bit m0 field.
      return aKey->m0;
    }

    enum { ALLOW_MEMMOVE = true };

   private:
    nsIID mKey;
  };
  nsInterfaceHashtable<IIDHashKey, nsIContent>
      mInterfaceTable;  // A table of cached interfaces that we support.

  int32_t mBaseNameSpaceID;  // If we extend a tagname/namespace, then that
                             // information will
  RefPtr<nsAtom> mBaseTag;   // be stored in here.

  nsCOMArray<nsXBLKeyEventHandler> mKeyHandlers;
};

#endif