Bug 1273706 - Part 14: Store custom property registrations on the document. r?heycam draft
authorJonathan Chan <jyc@eqv.io>
Thu, 18 Aug 2016 15:30:36 -0700
changeset 402903 1dd1e55304b0c23951e6097a2b065450477bdeac
parent 402902 2bef8f7bc69d1aaf4da3c7d6cc1050478c1b1dc2
child 402904 9ecf23296a7101b1cbc2f8789705be6e4edec033
push id26775
push userjchan@mozilla.com
push dateThu, 18 Aug 2016 22:38:41 +0000
reviewersheycam
bugs1273706
milestone51.0a1
Bug 1273706 - Part 14: Store custom property registrations on the document. r?heycam Add a CSSVariableRegistration struct to hold registration information. Also add some utility methods for getting access to the registrations mapping from available objects (used in nsCSSParser, nsRuleNode, etc.) that are used in future patches in this series. Add CSSVariableRegistrations, a mapping from registered custom property names to their registrations. Whenever a registration in |mData| is modified, |mVersion| *must* be incremented. (This happens in the bindings for CSS.registerProperty and CSS.unregisterProperty). The reason we need to update |mVersion| is because a Declaration stores a reference to a CSSVariableRegistration that it uses to determine which declaration of a given property is valid. Unlike standard properties, for which we can decide validity at parse time (unless they contain variable references), registered custom property types are not necessarily known at parse time. In particular, CSS.registerProperty, which registers the type of a custom property, can be called in the middle of the life of a Declaration. This means we have to recompute validities every time we try to read from a Declaration and the types of custom properties have changed. We store this monotonically increasing version number so that we only recompute when necessary. This is what is stored on nsDocument. Add a CSSVariableExprContext for storing the context of a variable expression. This is important for when the computed value of a custom property might depend on the context its declared in -- for example, relative URLs need to be resolved based on the stylesheet they were declared in. Some of these type and function names are absurdly long, though I'm not sure how best to shorten them. MozReview-Commit-ID: EvD8AEkzpBK
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsIDocument.h
layout/style/CSSVariableRegistration.h
layout/style/CSSVariableRegistrations.cpp
layout/style/CSSVariableRegistrations.h
layout/style/moz.build
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1448,16 +1448,17 @@ nsIDocument::nsIDocument()
 
 // NOTE! nsDocument::operator new() zeroes out all members, so don't
 // bother initializing members to 0.
 
 nsDocument::nsDocument(const char* aContentType)
   : nsIDocument()
   , mAnimatingImages(true)
   , mViewportType(Unknown)
+  , mCSSVariableRegistrations(new mozilla::CSSVariableRegistrations)
 {
   SetContentTypeInternal(nsDependentCString(aContentType));
 
   if (gDocumentLeakPRLog)
     MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
            ("DOCUMENT %p created", this));
 
   // Start out mLastStyleSheetSet as null, per spec
@@ -1508,16 +1509,22 @@ nsDocument::IsAboutPage()
   principal->GetURI(getter_AddRefs(uri));
   bool isAboutScheme = true;
   if (uri) {
     uri->SchemeIs("about", &isAboutScheme);
   }
   return isAboutScheme;
 }
 
+mozilla::CSSVariableRegistrations*
+nsDocument::GetCSSVariableRegistrations() const
+{
+  return mCSSVariableRegistrations;
+}
+
 nsDocument::~nsDocument()
 {
   if (gDocumentLeakPRLog)
     MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
            ("DOCUMENT %p destroyed", this));
 
   NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
 
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -64,16 +64,17 @@
 #include "nsDataHashtable.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Attributes.h"
 #include "nsIDOMXPathEvaluator.h"
 #include "jsfriendapi.h"
 #include "ImportManager.h"
 #include "mozilla/LinkedList.h"
 #include "CustomElementsRegistry.h"
+#include "mozilla/CSSVariableRegistrations.h"
 
 #define XML_DECLARATION_BITS_DECLARATION_EXISTS   (1 << 0)
 #define XML_DECLARATION_BITS_ENCODING_EXISTS      (1 << 1)
 #define XML_DECLARATION_BITS_STANDALONE_EXISTS    (1 << 2)
 #define XML_DECLARATION_BITS_STANDALONE_YES       (1 << 3)
 
 
 class nsDOMStyleSheetSetList;
@@ -1793,16 +1794,23 @@ private:
   // Set to true when the document is possibly controlled by the ServiceWorker.
   // Used to prevent multiple requests to ServiceWorkerManager.
   bool mMaybeServiceWorkerControlled;
 
 #ifdef DEBUG
 public:
   bool mWillReparent;
 #endif
+
+public:
+  mozilla::CSSVariableRegistrations*
+  GetCSSVariableRegistrations() const override;
+
+private:
+  RefPtr<mozilla::CSSVariableRegistrations> mCSSVariableRegistrations;
 };
 
 class nsDocumentOnStack
 {
 public:
   explicit nsDocumentOnStack(nsDocument* aDoc) : mDoc(aDoc)
   {
     mDoc->IncreaseStackRefCnt();
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -86,16 +86,17 @@ class nsTextNode;
 class nsWindowSizes;
 class nsDOMCaretPosition;
 class nsViewportInfo;
 class nsIGlobalObject;
 struct nsCSSSelectorList;
 
 namespace mozilla {
 class CSSStyleSheet;
+struct CSSVariableRegistrations;
 class ErrorResult;
 class EventStates;
 class PendingAnimationTracker;
 class StyleSetHandle;
 class SVGAttrAnimationRuleProcessor;
 template<typename> class OwningNonNull;
 
 namespace css {
@@ -3214,16 +3215,20 @@ protected:
   // Flags for use counters used by any child documents of this document.
   std::bitset<mozilla::eUseCounter_Count> mChildDocumentUseCounters;
   // Flags for whether we've notified our top-level "page" of a use counter
   // for this child document.
   std::bitset<mozilla::eUseCounter_Count> mNotifiedPageForUseCounter;
 
   // Whether the user has interacted with the document or not:
   bool mUserHasInteracted;
+
+public:
+  virtual mozilla::CSSVariableRegistrations*
+  GetCSSVariableRegistrations() const = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
  * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
  * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
  * object is deleted.
new file mode 100644
--- /dev/null
+++ b/layout/style/CSSVariableRegistration.h
@@ -0,0 +1,103 @@
+/* -*- 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/. */
+
+/* data about a registered CSS Custom property */
+
+#ifndef mozilla_CSSVariableRegistration_h
+#define mozilla_CSSVariableRegistration_h
+
+#include "mozilla/CSSValueType.h"
+#include "mozilla/CSSVariableSyntax.h"
+#include "nsCOMPtr.h"
+#include "nsCSSScanner.h"
+#include "nsCSSValue.h"
+#include "nsStringFwd.h"
+
+class nsIAtom;
+class nsStyleContext;
+
+namespace mozilla {
+
+// CSSVariableExprContext specifies the context of the expression for a
+// custom property (either as the 'initialValue' of a PropertyDescriptor passed
+// to CSS.registerProperty or in a '--property: value' declaration).
+// It is necessary for properties that might involve resource requests or
+// relative URIs (in particular, URL values for <url>s or <image>s.)
+// The members names have the same meanings as they do in nsCSSValue and
+// nsCSSValue).
+struct CSSVariableExprContext
+{
+  CSSVariableExprContext()
+    : mSheetURI(nullptr)
+    , mBaseURI(nullptr)
+    , mSheetPrincipal(nullptr) { }
+
+  CSSVariableExprContext(nsIURI* aSheetURI, nsIURI* aBaseURI,
+                         nsIPrincipal* aSheetPrincipal)
+    : mSheetURI(aSheetURI)
+    , mBaseURI(aBaseURI)
+    , mSheetPrincipal(aSheetPrincipal) { }
+
+  nsCOMPtr<nsIURI> mSheetURI;
+  nsCOMPtr<nsIURI> mBaseURI;
+  nsCOMPtr<nsIPrincipal> mSheetPrincipal;
+};
+
+// CSSVariableRegistration holds registration information for a particular
+// custom property.
+// It is populated based on the values provided to the CSS.registerProperty
+// call exposed as part of the Properties & Values API.
+struct CSSVariableRegistration
+{
+  bool mInherited;
+  CSSVariableSyntax mSyntax;
+
+  // mInitialExpr is the exact string the user specified as as 'initialValue' in
+  // the PropertyDescriptor dictionary.
+  // It is always a non-empty string, unless the user did not specify any
+  // 'initialValue' at all:
+  // If mInitialExpr is non-empty, it is guaranteed to parse to a valid and
+  // computationally idempotent CSS value with mSyntax.
+  //
+  // "If no initialValue is provided and the syntax is *, then a special initial
+  //  value used [sic]. This initial value must be considered parseable by
+  //  registerProperty() but invalid at computed value time."
+  //
+  // See: https://drafts.css-houdini.org/css-properties-values-api/#the-registerproperty-function
+  // See also:
+  // - CSSVariableResolver::VisitNode
+  // - nsCSSParser::ResolveValueWithVariableReferencesRec
+  nsString mInitialExpr;
+
+  // mInitialValue and mInitialType are the value and type of the expression in
+  // mInitialExpr if mInitialExpr is supplied (if mInitialExpr is empty, that
+  // means no 'initialValue' was supplied, and the values of these members are
+  // defined).
+  // Which unit & type to parse to are determined by mSyntax.
+  nsCSSValue mInitialValue;
+  CSSValueType mInitialType;
+
+  // mInitialContext ends up being used when we construct token stream values
+  // (in CSSVariableResolver for unregistered variables and in
+  // nsCSSParser::ParseTypedValue for values with syntax type <anything>) and
+  // when nsCSSParser calls SetValueToURL (and possibly other places in
+  // nsCSSParser that use mSheetURI, etc.)
+  CSSVariableExprContext mInitialContext;
+
+  // mInitialFirstToken and mInitialLastToken end up being used inside of
+  // CSSParserImpl::ResolveVariableValues. It calls AppendTokens to construct
+  // the resulting expanded value, and needs to know whether to insert /**/
+  // between adjacent tokens.
+  nsCSSTokenSerializationType mInitialFirstToken;
+  nsCSSTokenSerializationType mInitialLastToken;
+
+  // mAtom is the variable name, without the '--' prefix.
+  nsCOMPtr<nsIAtom> mAtom;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_CSSVariableRegistration_h
new file mode 100644
--- /dev/null
+++ b/layout/style/CSSVariableRegistrations.cpp
@@ -0,0 +1,30 @@
+/* -*- 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 "mozilla/CSSVariableRegistrations.h"
+
+#include "mozilla/CSSVariableRegistration.h"
+#include "nsGlobalWindow.h"
+#include "nsIDocument.h"
+#include "nsStyleContext.h"
+
+namespace mozilla {
+
+CSSVariableRegistrations*
+CSSVariableRegistrationsOfDocument(const nsIDocument* aDoc)
+{
+  return aDoc ? aDoc->GetCSSVariableRegistrations()
+              : nullptr;
+}
+
+CSSVariableRegistrations*
+CSSVariableRegistrationsOfStyleContext(const nsStyleContext* aStyleContext)
+{
+  nsIDocument* doc = aStyleContext->PresContext()->Document();
+  return CSSVariableRegistrationsOfDocument(doc);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/style/CSSVariableRegistrations.h
@@ -0,0 +1,62 @@
+/* -*- 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/. */
+
+/* A mapping from custom property names to registration information. */
+
+#ifndef mozilla_CSSVariableRegistrations_h
+#define mozilla_CSSVariableRegistrations_h
+
+#include <stdint.h>
+
+#include "mozilla/CSSVariableRegistration.h"
+#include "nsClassHashtable.h"
+#include "nsISupportsImpl.h"
+
+class nsIDocument;
+class nsStyleContext;
+
+namespace mozilla {
+
+/**
+ * CSSVariableRegistrations, a mapping from registered custom property names to
+ * their registrations. Whenever a registration in |mData| is modified,
+ * |mGeneration| *must* be incremented. (This happens in CSS.registerProperty
+ * and CSS.unregisterProperty). The reason we need to update |mGeneration| is
+ * because a Declaration stores a reference to a CSSVariableRegistration that it
+ * uses to determine which declaration of a given property is valid.
+ *
+ * Unlike standard properties, for which we can decide validity at parse time
+ * (unless they contain variable references), registered custom property types
+ * are not necessarily known at parse time. In particular, CSS.registerProperty,
+ * which registers the type of a custom property, can be called in the middle of
+ * the life of a Declaration. This means we have to recompute validities every
+ * time we try to read from a Declaration and the types of custom properties
+ * have changed. We store this monotonically increasing version number so that
+ * we only recompute when necessary.
+ */
+struct CSSVariableRegistrations
+{
+  typedef nsClassHashtable<nsStringHashKey, CSSVariableRegistration> Data;
+
+  NS_INLINE_DECL_REFCOUNTING(CSSVariableRegistrations);
+
+  uint32_t mGeneration = 0;
+  Data mData;
+
+private:
+  ~CSSVariableRegistrations() = default;
+};
+
+CSSVariableRegistrations*
+CSSVariableRegistrationsOfDocument(const nsIDocument* aDoc);
+
+CSSVariableRegistrations*
+CSSVariableRegistrationsOfStyleContext(
+    const nsStyleContext* aStyleContext);
+
+} // namespace mozilla
+
+#endif // mozilla_CSSVariableRegistrations_h
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -83,16 +83,18 @@ EXPORTS += [
 EXPORTS.mozilla += [
     'AnimationCollection.h',
     'CSSComputedValue.h',
     'CSSEnabledState.h',
     'CSSProperty.h',
     'CSSStyleSheet.h',
     'CSSValueType.h',
     'CSSVariableDeclarations.h',
+    'CSSVariableRegistration.h',
+    'CSSVariableRegistrations.h',
     'CSSVariableResolver.h',
     'CSSVariableSyntax.h',
     'CSSVariableValues.h',
     'HandleRefPtr.h',
     'IncrementalClearCOMRuleArray.h',
     'LayerAnimationInfo.h',
     'RuleNodeCacheConditions.h',
     'RuleProcessorCache.h',
@@ -144,16 +146,17 @@ UNIFIED_SOURCES += [
     'AnimationCommon.cpp',
     'CounterStyleManager.cpp',
     'CSS.cpp',
     'CSSComputedValue.cpp',
     'CSSLexer.cpp',
     'CSSRuleList.cpp',
     'CSSStyleSheet.cpp',
     'CSSVariableDeclarations.cpp',
+    'CSSVariableRegistrations.cpp',
     'CSSVariableResolver.cpp',
     'CSSVariableSyntax.cpp',
     'CSSVariableValues.cpp',
     'Declaration.cpp',
     'ErrorReporter.cpp',
     'FontFace.cpp',
     'FontFaceSet.cpp',
     'FontFaceSetIterator.cpp',