import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,172 @@
/* -*- 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/AnimationCollection.h"
#include "mozilla/RestyleManagerHandle.h"
#include "mozilla/RestyleManagerHandleInlines.h"
#include "nsAnimationManager.h" // For dom::CSSAnimation
#include "nsPresContext.h"
#include "nsTransitionManager.h" // For dom::CSSTransition
namespace mozilla {
template <class AnimationType>
/* static */ void
AnimationCollection<AnimationType>::PropertyDtor(void* aObject,
nsIAtom* aPropertyName,
void* aPropertyValue,
void* aData)
{
AnimationCollection* collection =
static_cast<AnimationCollection*>(aPropertyValue);
#ifdef DEBUG
MOZ_ASSERT(!collection->mCalledPropertyDtor, "can't call dtor twice");
collection->mCalledPropertyDtor = true;
#endif
{
nsAutoAnimationMutationBatch mb(collection->mElement->OwnerDoc());
for (size_t animIdx = collection->mAnimations.Length(); animIdx-- != 0; ) {
collection->mAnimations[animIdx]->CancelFromStyle();
}
}
delete collection;
}
template <class AnimationType>
/* static */ AnimationCollection<AnimationType>*
AnimationCollection<AnimationType>::GetAnimationCollection(
dom::Element *aElement,
CSSPseudoElementType aPseudoType)
{
if (!aElement->MayHaveAnimations()) {
// Early return for the most common case.
return nullptr;
}
nsIAtom* propName = GetPropertyAtomForPseudoType(aPseudoType);
if (!propName) {
return nullptr;
}
return
static_cast<AnimationCollection<AnimationType>*>(aElement->
GetProperty(propName));
}
template <class AnimationType>
/* static */ AnimationCollection<AnimationType>*
AnimationCollection<AnimationType>::GetAnimationCollection(
const nsIFrame* aFrame)
{
Maybe<NonOwningAnimationTarget> pseudoElement =
EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame);
if (!pseudoElement) {
return nullptr;
}
if (!pseudoElement->mElement->MayHaveAnimations()) {
return nullptr;
}
return GetAnimationCollection(pseudoElement->mElement,
pseudoElement->mPseudoType);
}
template <class AnimationType>
/* static */ AnimationCollection<AnimationType>*
AnimationCollection<AnimationType>::GetOrCreateAnimationCollection(
dom::Element* aElement,
CSSPseudoElementType aPseudoType,
bool* aCreatedCollection)
{
MOZ_ASSERT(aCreatedCollection);
*aCreatedCollection = false;
nsIAtom* propName = GetPropertyAtomForPseudoType(aPseudoType);
MOZ_ASSERT(propName, "Should only try to create animations for one of the"
" recognized pseudo types");
auto collection = static_cast<AnimationCollection<AnimationType>*>(
aElement->GetProperty(propName));
if (!collection) {
// FIXME: Consider arena-allocating?
collection = new AnimationCollection<AnimationType>(aElement, propName);
nsresult rv =
aElement->SetProperty(propName, collection,
&AnimationCollection<AnimationType>::PropertyDtor,
false);
if (NS_FAILED(rv)) {
NS_WARNING("SetProperty failed");
// The collection must be destroyed via PropertyDtor, otherwise
// mCalledPropertyDtor assertion is triggered in destructor.
AnimationCollection<AnimationType>::PropertyDtor(aElement, propName,
collection, nullptr);
return nullptr;
}
*aCreatedCollection = true;
aElement->SetMayHaveAnimations();
}
return collection;
}
template <class AnimationType>
/* static */ nsString
AnimationCollection<AnimationType>::PseudoTypeAsString(
CSSPseudoElementType aPseudoType)
{
switch (aPseudoType) {
case CSSPseudoElementType::before:
return NS_LITERAL_STRING("::before");
case CSSPseudoElementType::after:
return NS_LITERAL_STRING("::after");
default:
MOZ_ASSERT(aPseudoType == CSSPseudoElementType::NotPseudo,
"Unexpected pseudo type");
return EmptyString();
}
}
template <class AnimationType>
void
AnimationCollection<AnimationType>::UpdateCheckGeneration(
nsPresContext* aPresContext)
{
if (aPresContext->RestyleManager()->IsServo()) {
// stylo: ServoRestyleManager does not support animations yet.
return;
}
mCheckGeneration =
aPresContext->RestyleManager()->AsGecko()->GetAnimationGeneration();
}
template<class AnimationType>
/*static*/ nsIAtom*
AnimationCollection<AnimationType>::GetPropertyAtomForPseudoType(
CSSPseudoElementType aPseudoType)
{
nsIAtom* propName = nullptr;
if (aPseudoType == CSSPseudoElementType::NotPseudo) {
propName = TraitsType::ElementPropertyAtom();
} else if (aPseudoType == CSSPseudoElementType::before) {
propName = TraitsType::BeforePropertyAtom();
} else if (aPseudoType == CSSPseudoElementType::after) {
propName = TraitsType::AfterPropertyAtom();
}
return propName;
}
// Explicit class instantiations
template class AnimationCollection<dom::CSSAnimation>;
template class AnimationCollection<dom::CSSTransition>;
} // namespace mozilla

View file

@ -0,0 +1,147 @@
/* -*- 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 mozilla_AnimationCollection_h
#define mozilla_AnimationCollection_h
#include "mozilla/dom/Animation.h"
#include "mozilla/dom/Element.h"
#include "mozilla/Assertions.h"
#include "mozilla/LinkedList.h"
#include "mozilla/RefPtr.h"
#include "nsCSSPseudoElements.h"
#include "nsDOMMutationObserver.h"
#include "nsTArray.h"
class nsIAtom;
class nsPresContext;
namespace mozilla {
// Traits class to define the specific atoms used when storing specializations
// of AnimationCollection as a property on an Element (e.g. which atom
// to use when storing an AnimationCollection<CSSAnimation> for a ::before
// pseudo-element).
template <class AnimationType>
struct AnimationTypeTraits { };
template <class AnimationType>
class AnimationCollection
: public LinkedListElement<AnimationCollection<AnimationType>>
{
typedef AnimationCollection<AnimationType> SelfType;
typedef AnimationTypeTraits<AnimationType> TraitsType;
AnimationCollection(dom::Element* aElement, nsIAtom* aElementProperty)
: mElement(aElement)
, mElementProperty(aElementProperty)
, mCheckGeneration(0)
#ifdef DEBUG
, mCalledPropertyDtor(false)
#endif
{
MOZ_COUNT_CTOR(AnimationCollection);
}
public:
~AnimationCollection()
{
MOZ_ASSERT(mCalledPropertyDtor,
"must call destructor through element property dtor");
MOZ_COUNT_DTOR(AnimationCollection);
LinkedListElement<SelfType>::remove();
}
void Destroy()
{
// This will call our destructor.
mElement->DeleteProperty(mElementProperty);
}
static void PropertyDtor(void *aObject, nsIAtom *aPropertyName,
void *aPropertyValue, void *aData);
// Get the collection of animations for the given |aElement| and
// |aPseudoType|.
static AnimationCollection<AnimationType>*
GetAnimationCollection(dom::Element* aElement,
CSSPseudoElementType aPseudoType);
// Given the frame |aFrame| with possibly animated content, finds its
// associated collection of animations. If |aFrame| is a generated content
// frame, this function may examine the parent frame to search for such
// animations.
static AnimationCollection<AnimationType>* GetAnimationCollection(
const nsIFrame* aFrame);
// Get the collection of animations for the given |aElement| and
// |aPseudoType| or create it if it does not already exist.
//
// We'll set the outparam |aCreatedCollection| to true if we have
// to create the collection and we successfully do so. Otherwise,
// we'll set it to false.
static AnimationCollection<AnimationType>*
GetOrCreateAnimationCollection(dom::Element* aElement,
CSSPseudoElementType aPseudoType,
bool* aCreatedCollection);
bool IsForElement() const { // rather than for a pseudo-element
return mElementProperty == TraitsType::ElementPropertyAtom();
}
bool IsForBeforePseudo() const {
return mElementProperty == TraitsType::BeforePropertyAtom();
}
bool IsForAfterPseudo() const {
return mElementProperty == TraitsType::AfterPropertyAtom();
}
CSSPseudoElementType PseudoElementType() const
{
if (IsForElement()) {
return CSSPseudoElementType::NotPseudo;
}
if (IsForBeforePseudo()) {
return CSSPseudoElementType::before;
}
MOZ_ASSERT(IsForAfterPseudo(),
"::before & ::after should be the only pseudo-elements here");
return CSSPseudoElementType::after;
}
static nsString PseudoTypeAsString(CSSPseudoElementType aPseudoType);
dom::Element *mElement;
// the atom we use in mElement's prop table (must be a static atom,
// i.e., in an atom list)
nsIAtom *mElementProperty;
InfallibleTArray<RefPtr<AnimationType>> mAnimations;
// For CSS transitions only, we record the most recent generation
// for which we've done the transition update, so that we avoid doing
// it more than once per style change.
// (Note that we also store an animation generation on each EffectSet in
// order to track when we need to update animations on layers.)
uint64_t mCheckGeneration;
// Update mCheckGeneration to RestyleManager's count
void UpdateCheckGeneration(nsPresContext* aPresContext);
private:
static nsIAtom* GetPropertyAtomForPseudoType(
CSSPseudoElementType aPseudoType);
#ifdef DEBUG
bool mCalledPropertyDtor;
#endif
};
} // namespace mozilla
#endif // mozilla_AnimationCollection_h

View file

@ -0,0 +1,54 @@
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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 "AnimationCommon.h"
#include "nsTransitionManager.h"
#include "nsAnimationManager.h"
#include "ActiveLayerTracker.h"
#include "gfxPlatform.h"
#include "nsCSSPropertyIDSet.h"
#include "nsCSSValue.h"
#include "nsCycleCollectionParticipant.h"
#include "nsStyleContext.h"
#include "nsIFrame.h"
#include "nsLayoutUtils.h"
#include "FrameLayerBuilder.h"
#include "nsDisplayList.h"
#include "mozilla/AnimationUtils.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/KeyframeEffectReadOnly.h"
#include "nsRuleProcessorData.h"
#include "nsStyleSet.h"
#include "nsStyleChangeList.h"
using mozilla::dom::Animation;
using mozilla::dom::KeyframeEffectReadOnly;
namespace mozilla {
nsPresContext*
OwningElementRef::GetRenderedPresContext() const
{
if (!mElement) {
return nullptr;
}
nsIDocument* doc = mElement->GetComposedDoc();
if (!doc) {
return nullptr;
}
nsIPresShell* shell = doc->GetShell();
if (!shell) {
return nullptr;
}
return shell->GetPresContext();
}
} // namespace mozilla

View file

@ -0,0 +1,256 @@
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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 mozilla_css_AnimationCommon_h
#define mozilla_css_AnimationCommon_h
#include <algorithm> // For <std::stable_sort>
#include "mozilla/AnimationCollection.h"
#include "mozilla/AnimationComparator.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/Animation.h"
#include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF
#include "mozilla/Assertions.h"
#include "nsContentUtils.h"
#include "nsCSSPseudoElements.h"
#include "nsCycleCollectionParticipant.h"
class nsIFrame;
class nsPresContext;
namespace mozilla {
namespace dom {
class Element;
}
template <class AnimationType>
class CommonAnimationManager {
public:
explicit CommonAnimationManager(nsPresContext *aPresContext)
: mPresContext(aPresContext)
{
}
// NOTE: This can return null after Disconnect().
nsPresContext* PresContext() const { return mPresContext; }
/**
* Notify the manager that the pres context is going away.
*/
void Disconnect()
{
// Content nodes might outlive the transition or animation manager.
RemoveAllElementCollections();
mPresContext = nullptr;
}
protected:
virtual ~CommonAnimationManager()
{
MOZ_ASSERT(!mPresContext, "Disconnect should have been called");
}
void AddElementCollection(AnimationCollection<AnimationType>* aCollection)
{
mElementCollections.insertBack(aCollection);
}
void RemoveAllElementCollections()
{
while (AnimationCollection<AnimationType>* head =
mElementCollections.getFirst()) {
head->Destroy(); // Note: this removes 'head' from mElementCollections.
}
}
LinkedList<AnimationCollection<AnimationType>> mElementCollections;
nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect)
};
/**
* Utility class for referencing the element that created a CSS animation or
* transition. It is non-owning (i.e. it uses a raw pointer) since it is only
* expected to be set by the owned animation while it actually being managed
* by the owning element.
*
* This class also abstracts the comparison of an element/pseudo-class pair
* for the sake of composite ordering since this logic is common to both CSS
* animations and transitions.
*
* (We call this OwningElementRef instead of just OwningElement so that we can
* call the getter on CSSAnimation/CSSTransition OwningElement() without
* clashing with this object's contructor.)
*/
class OwningElementRef final
{
public:
OwningElementRef()
: mElement(nullptr)
, mPseudoType(CSSPseudoElementType::NotPseudo)
{ }
OwningElementRef(dom::Element& aElement,
CSSPseudoElementType aPseudoType)
: mElement(&aElement)
, mPseudoType(aPseudoType)
{ }
bool Equals(const OwningElementRef& aOther) const
{
return mElement == aOther.mElement &&
mPseudoType == aOther.mPseudoType;
}
bool LessThan(const OwningElementRef& aOther) const
{
MOZ_ASSERT(mElement && aOther.mElement,
"Elements to compare should not be null");
if (mElement != aOther.mElement) {
return nsContentUtils::PositionIsBefore(mElement, aOther.mElement);
}
return mPseudoType == CSSPseudoElementType::NotPseudo ||
(mPseudoType == CSSPseudoElementType::before &&
aOther.mPseudoType == CSSPseudoElementType::after);
}
bool IsSet() const { return !!mElement; }
void GetElement(dom::Element*& aElement,
CSSPseudoElementType& aPseudoType) const {
aElement = mElement;
aPseudoType = mPseudoType;
}
nsPresContext* GetRenderedPresContext() const;
private:
dom::Element* MOZ_NON_OWNING_REF mElement;
CSSPseudoElementType mPseudoType;
};
template <class EventInfo>
class DelayedEventDispatcher
{
public:
DelayedEventDispatcher() : mIsSorted(true) { }
void QueueEvent(EventInfo&& aEventInfo)
{
mPendingEvents.AppendElement(Forward<EventInfo>(aEventInfo));
mIsSorted = false;
}
// This is exposed as a separate method so that when we are dispatching
// *both* transition events and animation events we can sort both lists
// once using the current state of the document before beginning any
// dispatch.
void SortEvents()
{
if (mIsSorted) {
return;
}
// FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is
// fixed.
std::stable_sort(mPendingEvents.begin(), mPendingEvents.end(),
EventInfoLessThan());
mIsSorted = true;
}
// Takes a reference to the owning manager's pres context so it can
// detect if the pres context is destroyed while dispatching one of
// the events.
//
// This will call SortEvents automatically if it has not already been
// called.
void DispatchEvents(nsPresContext* const & aPresContext)
{
if (!aPresContext || mPendingEvents.IsEmpty()) {
return;
}
SortEvents();
EventArray events;
mPendingEvents.SwapElements(events);
// mIsSorted will be set to true by SortEvents above, and we leave it
// that way since mPendingEvents is now empty
for (EventInfo& info : events) {
EventDispatcher::Dispatch(info.mElement, aPresContext, &info.mEvent);
if (!aPresContext) {
break;
}
}
}
void ClearEventQueue()
{
mPendingEvents.Clear();
mIsSorted = true;
}
bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); }
// Methods for supporting cycle-collection
void Traverse(nsCycleCollectionTraversalCallback* aCallback,
const char* aName)
{
for (EventInfo& info : mPendingEvents) {
ImplCycleCollectionTraverse(*aCallback, info.mElement, aName);
ImplCycleCollectionTraverse(*aCallback, info.mAnimation, aName);
}
}
void Unlink() { ClearEventQueue(); }
protected:
class EventInfoLessThan
{
public:
bool operator()(const EventInfo& a, const EventInfo& b) const
{
if (a.mTimeStamp != b.mTimeStamp) {
// Null timestamps sort first
if (a.mTimeStamp.IsNull() || b.mTimeStamp.IsNull()) {
return a.mTimeStamp.IsNull();
} else {
return a.mTimeStamp < b.mTimeStamp;
}
}
AnimationPtrComparator<RefPtr<dom::Animation>> comparator;
return comparator.LessThan(a.mAnimation, b.mAnimation);
}
};
typedef nsTArray<EventInfo> EventArray;
EventArray mPendingEvents;
bool mIsSorted;
};
template <class EventInfo>
inline void
ImplCycleCollectionUnlink(DelayedEventDispatcher<EventInfo>& aField)
{
aField.Unlink();
}
template <class EventInfo>
inline void
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
DelayedEventDispatcher<EventInfo>& aField,
const char* aName,
uint32_t aFlags = 0)
{
aField.Traverse(&aCallback, aName);
}
} // namespace mozilla
#endif /* !defined(mozilla_css_AnimationCommon_h) */

107
layout/style/CSS.cpp Normal file
View file

@ -0,0 +1,107 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* DOM object holding utility CSS functions */
#include "CSS.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/ServoBindings.h"
#include "nsCSSParser.h"
#include "nsGlobalWindow.h"
#include "nsIDocument.h"
#include "nsIURI.h"
#include "nsStyleUtil.h"
#include "xpcpublic.h"
namespace mozilla {
namespace dom {
struct SupportsParsingInfo
{
nsIURI* mDocURI;
nsIURI* mBaseURI;
nsIPrincipal* mPrincipal;
StyleBackendType mStyleBackendType;
};
static nsresult
GetParsingInfo(const GlobalObject& aGlobal,
SupportsParsingInfo& aInfo)
{
nsGlobalWindow* win = xpc::WindowOrNull(aGlobal.Get());
if (!win) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocument> doc = win->GetDoc();
if (!doc) {
return NS_ERROR_FAILURE;
}
aInfo.mDocURI = nsCOMPtr<nsIURI>(doc->GetDocumentURI()).get();
aInfo.mBaseURI = nsCOMPtr<nsIURI>(doc->GetBaseURI()).get();
aInfo.mPrincipal = win->GetPrincipal();
aInfo.mStyleBackendType = doc->GetStyleBackendType();
return NS_OK;
}
/* static */ bool
CSS::Supports(const GlobalObject& aGlobal,
const nsAString& aProperty,
const nsAString& aValue,
ErrorResult& aRv)
{
SupportsParsingInfo info;
nsresult rv = GetParsingInfo(aGlobal, info);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
if (info.mStyleBackendType == StyleBackendType::Servo) {
NS_ConvertUTF16toUTF8 property(aProperty);
NS_ConvertUTF16toUTF8 value(aValue);
return Servo_CSSSupports(&property, &value);
}
nsCSSParser parser;
return parser.EvaluateSupportsDeclaration(aProperty, aValue, info.mDocURI,
info.mBaseURI, info.mPrincipal);
}
/* static */ bool
CSS::Supports(const GlobalObject& aGlobal,
const nsAString& aCondition,
ErrorResult& aRv)
{
SupportsParsingInfo info;
nsresult rv = GetParsingInfo(aGlobal, info);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
if (info.mStyleBackendType == StyleBackendType::Servo) {
MOZ_CRASH("stylo: CSS.supports() with arguments is not yet implemented");
}
nsCSSParser parser;
return parser.EvaluateSupportsCondition(aCondition, info.mDocURI,
info.mBaseURI, info.mPrincipal);
}
/* static */ void
CSS::Escape(const GlobalObject& aGlobal,
const nsAString& aIdent,
nsAString& aReturn)
{
nsStyleUtil::AppendEscapedCSSIdent(aIdent, aReturn);
}
} // namespace dom
} // namespace mozilla

44
layout/style/CSS.h Normal file
View file

@ -0,0 +1,44 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* DOM object holding utility CSS functions */
#ifndef mozilla_dom_CSS_h_
#define mozilla_dom_CSS_h_
#include "mozilla/Attributes.h"
#include "mozilla/Preferences.h"
namespace mozilla {
class ErrorResult;
namespace dom {
class GlobalObject;
class CSS {
private:
CSS() = delete;
public:
static bool Supports(const GlobalObject& aGlobal,
const nsAString& aProperty,
const nsAString& aValue,
ErrorResult& aRv);
static bool Supports(const GlobalObject& aGlobal,
const nsAString& aDeclaration,
ErrorResult& aRv);
static void Escape(const GlobalObject& aGlobal,
const nsAString& aIdent,
nsAString& aReturn);
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_CSS_h_

361
layout/style/CSSCalc.h Normal file
View file

@ -0,0 +1,361 @@
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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 CSSCalc_h_
#define CSSCalc_h_
#include "nsCSSValue.h"
#include "nsStyleCoord.h"
#include <math.h>
namespace mozilla {
namespace css {
/**
* ComputeCalc computes the result of a calc() expression tree.
*
* It is templatized over a CalcOps class that is expected to provide:
*
* // input_type and input_array_type have a bunch of very specific
* // expectations (which happen to be met by two classes (nsCSSValue
* // and nsStyleCoord). There must be methods (roughly):
* // input_array_type* input_type::GetArrayValue();
* // uint32_t input_array_type::Count() const;
* // input_type& input_array_type::Item(uint32_t);
* typedef ... input_type;
* typedef ... input_array_type;
*
* typedef ... result_type;
*
* // GetUnit(avalue) must return the correct nsCSSUnit for any
* // value that represents a calc tree node (eCSSUnit_Calc*). For
* // other nodes, it may return any non eCSSUnit_Calc* unit.
* static nsCSSUnit GetUnit(const input_type& aValue);
*
* result_type
* MergeAdditive(nsCSSUnit aCalcFunction,
* result_type aValue1, result_type aValue2);
*
* result_type
* MergeMultiplicativeL(nsCSSUnit aCalcFunction,
* float aValue1, result_type aValue2);
*
* result_type
* MergeMultiplicativeR(nsCSSUnit aCalcFunction,
* result_type aValue1, float aValue2);
*
* result_type
* ComputeLeafValue(const input_type& aValue);
*
* float
* ComputeNumber(const input_type& aValue);
*
* The CalcOps methods might compute the calc() expression down to a
* number, reduce some parts of it to a number but replicate other
* parts, or produce a tree with a different data structure (for
* example, nsCSS* for specified values vs nsStyle* for computed
* values).
*
* For each leaf in the calc() expression, ComputeCalc will call either
* ComputeNumber (when the leaf is the left side of a Times_L or the
* right side of a Times_R or Divided) or ComputeLeafValue (otherwise).
* (The CalcOps in the CSS parser that reduces purely numeric
* expressions in turn calls ComputeCalc on numbers; other ops can
* presume that expressions in the number positions have already been
* normalized to a single numeric value and derive from
* NumbersAlreadyNormalizedCalcOps.)
*
* For non-leaves, one of the Merge functions will be called:
* MergeAdditive for Plus and Minus
* MergeMultiplicativeL for Times_L (number * value)
* MergeMultiplicativeR for Times_R (value * number) and Divided
*/
template <class CalcOps>
static typename CalcOps::result_type
ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
{
switch (CalcOps::GetUnit(aValue)) {
case eCSSUnit_Calc: {
typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
MOZ_ASSERT(arr->Count() == 1, "unexpected length");
return ComputeCalc(arr->Item(0), aOps);
}
case eCSSUnit_Calc_Plus:
case eCSSUnit_Calc_Minus: {
typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
MOZ_ASSERT(arr->Count() == 2, "unexpected length");
typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps),
rhs = ComputeCalc(arr->Item(1), aOps);
return aOps.MergeAdditive(CalcOps::GetUnit(aValue), lhs, rhs);
}
case eCSSUnit_Calc_Times_L: {
typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
MOZ_ASSERT(arr->Count() == 2, "unexpected length");
float lhs = aOps.ComputeNumber(arr->Item(0));
typename CalcOps::result_type rhs = ComputeCalc(arr->Item(1), aOps);
return aOps.MergeMultiplicativeL(CalcOps::GetUnit(aValue), lhs, rhs);
}
case eCSSUnit_Calc_Times_R:
case eCSSUnit_Calc_Divided: {
typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
MOZ_ASSERT(arr->Count() == 2, "unexpected length");
typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps);
float rhs = aOps.ComputeNumber(arr->Item(1));
return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs);
}
default: {
return aOps.ComputeLeafValue(aValue);
}
}
}
/**
* The input unit operation for input_type being nsCSSValue.
*/
struct CSSValueInputCalcOps
{
typedef nsCSSValue input_type;
typedef nsCSSValue::Array input_array_type;
static nsCSSUnit GetUnit(const nsCSSValue& aValue)
{
return aValue.GetUnit();
}
};
/**
* Basic*CalcOps provide a partial implementation of the CalcOps
* template parameter to ComputeCalc, for those callers whose merging
* just consists of mathematics (rather than tree construction).
*/
struct BasicCoordCalcOps
{
typedef nscoord result_type;
result_type
MergeAdditive(nsCSSUnit aCalcFunction,
result_type aValue1, result_type aValue2)
{
if (aCalcFunction == eCSSUnit_Calc_Plus) {
return NSCoordSaturatingAdd(aValue1, aValue2);
}
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus,
"unexpected unit");
return NSCoordSaturatingSubtract(aValue1, aValue2, 0);
}
result_type
MergeMultiplicativeL(nsCSSUnit aCalcFunction,
float aValue1, result_type aValue2)
{
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
"unexpected unit");
return NSCoordSaturatingMultiply(aValue2, aValue1);
}
result_type
MergeMultiplicativeR(nsCSSUnit aCalcFunction,
result_type aValue1, float aValue2)
{
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_R ||
aCalcFunction == eCSSUnit_Calc_Divided,
"unexpected unit");
if (aCalcFunction == eCSSUnit_Calc_Divided) {
aValue2 = 1.0f / aValue2;
}
return NSCoordSaturatingMultiply(aValue1, aValue2);
}
};
struct BasicFloatCalcOps
{
typedef float result_type;
result_type
MergeAdditive(nsCSSUnit aCalcFunction,
result_type aValue1, result_type aValue2)
{
if (aCalcFunction == eCSSUnit_Calc_Plus) {
return aValue1 + aValue2;
}
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus,
"unexpected unit");
return aValue1 - aValue2;
}
result_type
MergeMultiplicativeL(nsCSSUnit aCalcFunction,
float aValue1, result_type aValue2)
{
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
"unexpected unit");
return aValue1 * aValue2;
}
result_type
MergeMultiplicativeR(nsCSSUnit aCalcFunction,
result_type aValue1, float aValue2)
{
if (aCalcFunction == eCSSUnit_Calc_Times_R) {
return aValue1 * aValue2;
}
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Divided,
"unexpected unit");
return aValue1 / aValue2;
}
};
/**
* A ComputeNumber implementation for callers that can assume numbers
* are already normalized (i.e., anything past the parser).
*/
struct NumbersAlreadyNormalizedOps : public CSSValueInputCalcOps
{
float ComputeNumber(const nsCSSValue& aValue)
{
MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
return aValue.GetFloatValue();
}
};
/**
* SerializeCalc appends the serialization of aValue to a string.
*
* It is templatized over a CalcOps class that is expected to provide:
*
* // input_type and input_array_type have a bunch of very specific
* // expectations (which happen to be met by two classes (nsCSSValue
* // and nsStyleCoord). There must be methods (roughly):
* // input_array_type* input_type::GetArrayValue();
* // uint32_t input_array_type::Count() const;
* // input_type& input_array_type::Item(uint32_t);
* typedef ... input_type;
* typedef ... input_array_type;
*
* static nsCSSUnit GetUnit(const input_type& aValue);
*
* void Append(const char* aString);
* void AppendLeafValue(const input_type& aValue);
* void AppendNumber(const input_type& aValue);
*
* Data structures given may or may not have a toplevel eCSSUnit_Calc
* node representing a calc whose toplevel is not min() or max().
*/
template <class CalcOps>
static void
SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps);
// Serialize the toplevel value in a calc() tree. See big comment
// above.
template <class CalcOps>
static void
SerializeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
{
aOps.Append("calc(");
nsCSSUnit unit = CalcOps::GetUnit(aValue);
if (unit == eCSSUnit_Calc) {
const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
MOZ_ASSERT(array->Count() == 1, "unexpected length");
SerializeCalcInternal(array->Item(0), aOps);
} else {
SerializeCalcInternal(aValue, aOps);
}
aOps.Append(")");
}
static inline bool
IsCalcAdditiveUnit(nsCSSUnit aUnit)
{
return aUnit == eCSSUnit_Calc_Plus ||
aUnit == eCSSUnit_Calc_Minus;
}
static inline bool
IsCalcMultiplicativeUnit(nsCSSUnit aUnit)
{
return aUnit == eCSSUnit_Calc_Times_L ||
aUnit == eCSSUnit_Calc_Times_R ||
aUnit == eCSSUnit_Calc_Divided;
}
// Serialize a non-toplevel value in a calc() tree. See big comment
// above.
template <class CalcOps>
/* static */ void
SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
{
nsCSSUnit unit = CalcOps::GetUnit(aValue);
if (IsCalcAdditiveUnit(unit)) {
const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
MOZ_ASSERT(array->Count() == 2, "unexpected length");
SerializeCalcInternal(array->Item(0), aOps);
if (eCSSUnit_Calc_Plus == unit) {
aOps.Append(" + ");
} else {
MOZ_ASSERT(eCSSUnit_Calc_Minus == unit, "unexpected unit");
aOps.Append(" - ");
}
bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(1)));
if (needParens) {
aOps.Append("(");
}
SerializeCalcInternal(array->Item(1), aOps);
if (needParens) {
aOps.Append(")");
}
} else if (IsCalcMultiplicativeUnit(unit)) {
const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
MOZ_ASSERT(array->Count() == 2, "unexpected length");
bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(0)));
if (needParens) {
aOps.Append("(");
}
if (unit == eCSSUnit_Calc_Times_L) {
aOps.AppendNumber(array->Item(0));
} else {
SerializeCalcInternal(array->Item(0), aOps);
}
if (needParens) {
aOps.Append(")");
}
if (eCSSUnit_Calc_Times_L == unit || eCSSUnit_Calc_Times_R == unit) {
aOps.Append(" * ");
} else {
MOZ_ASSERT(eCSSUnit_Calc_Divided == unit, "unexpected unit");
aOps.Append(" / ");
}
nsCSSUnit subUnit = CalcOps::GetUnit(array->Item(1));
needParens = IsCalcAdditiveUnit(subUnit) ||
IsCalcMultiplicativeUnit(subUnit);
if (needParens) {
aOps.Append("(");
}
if (unit == eCSSUnit_Calc_Times_L) {
SerializeCalcInternal(array->Item(1), aOps);
} else {
aOps.AppendNumber(array->Item(1));
}
if (needParens) {
aOps.Append(")");
}
} else {
aOps.AppendLeafValue(aValue);
}
}
} // namespace css
} // namespace mozilla
#endif /* !defined(CSSCalc_h_) */

View file

@ -0,0 +1,39 @@
/* -*- 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/. */
/*
* enum for whether a CSS feature (property, pseudo-class, etc.) is
* enabled in a specific context
*/
#ifndef mozilla_CSSEnabledState_h
#define mozilla_CSSEnabledState_h
#include "mozilla/TypedEnumBits.h"
namespace mozilla {
enum class CSSEnabledState
{
// The default CSSEnabledState: only enable what's enabled for all
// content, given the current values of preferences.
eForAllContent = 0,
// Enable features available in UA sheets.
eInUASheets = 0x01,
// Enable features available in chrome code.
eInChrome = 0x02,
// Special value to unconditionally enable everything. This implies
// all the bits above, but is strictly more than just their OR-ed
// union. This just skips any test so a feature will be enabled even
// if it would have been disabled with all the bits above set.
eIgnoreEnabledState = 0xff
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CSSEnabledState)
} // namespace mozilla
#endif // mozilla_CSSEnabledState_h

166
layout/style/CSSLexer.cpp Normal file
View file

@ -0,0 +1,166 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/dom/CSSLexer.h"
#include "js/Value.h"
#include "mozilla/dom/CSSLexerBinding.h"
#include "mozilla/dom/ToJSValue.h"
namespace mozilla {
namespace dom {
// Ensure that constants are consistent.
#define CHECK(X, Y) \
static_assert(static_cast<int>(X) == static_cast<int>(Y), \
"nsCSSToken and CSSTokenType should have identical values")
CHECK(eCSSToken_Whitespace, CSSTokenType::Whitespace);
CHECK(eCSSToken_Comment, CSSTokenType::Comment);
CHECK(eCSSToken_Ident, CSSTokenType::Ident);
CHECK(eCSSToken_Function, CSSTokenType::Function);
CHECK(eCSSToken_AtKeyword, CSSTokenType::At);
CHECK(eCSSToken_ID, CSSTokenType::Id);
CHECK(eCSSToken_Hash, CSSTokenType::Hash);
CHECK(eCSSToken_Number, CSSTokenType::Number);
CHECK(eCSSToken_Dimension, CSSTokenType::Dimension);
CHECK(eCSSToken_Percentage, CSSTokenType::Percentage);
CHECK(eCSSToken_String, CSSTokenType::String);
CHECK(eCSSToken_Bad_String, CSSTokenType::Bad_string);
CHECK(eCSSToken_URL, CSSTokenType::Url);
CHECK(eCSSToken_Bad_URL, CSSTokenType::Bad_url);
CHECK(eCSSToken_Symbol, CSSTokenType::Symbol);
CHECK(eCSSToken_Includes, CSSTokenType::Includes);
CHECK(eCSSToken_Dashmatch, CSSTokenType::Dashmatch);
CHECK(eCSSToken_Beginsmatch, CSSTokenType::Beginsmatch);
CHECK(eCSSToken_Endsmatch, CSSTokenType::Endsmatch);
CHECK(eCSSToken_Containsmatch, CSSTokenType::Containsmatch);
CHECK(eCSSToken_URange, CSSTokenType::Urange);
CHECK(eCSSToken_HTMLComment, CSSTokenType::Htmlcomment);
#undef CHECK
CSSLexer::CSSLexer(const nsAString& aText)
: mInput(aText)
, mScanner(mInput, 1)
{
}
CSSLexer::~CSSLexer()
{
}
bool
CSSLexer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector)
{
return CSSLexerBinding::Wrap(aCx, this, aGivenProto, aReflector);
}
uint32_t
CSSLexer::LineNumber()
{
// The scanner uses 1-based line numbers, but our callers expect
// 0-based.
return mScanner.GetLineNumber() - 1;
}
uint32_t
CSSLexer::ColumnNumber()
{
return mScanner.GetColumnNumber();
}
void
CSSLexer::PerformEOFFixup(const nsAString& aInputString, bool aPreserveBackslash,
nsAString& aResult)
{
aResult.Append(aInputString);
uint32_t eofChars = mScanner.GetEOFCharacters();
if (aPreserveBackslash &&
(eofChars & (nsCSSScanner::eEOFCharacters_DropBackslash |
nsCSSScanner::eEOFCharacters_ReplacementChar)) != 0) {
eofChars &= ~(nsCSSScanner::eEOFCharacters_DropBackslash |
nsCSSScanner::eEOFCharacters_ReplacementChar);
aResult.Append('\\');
}
if ((eofChars & nsCSSScanner::eEOFCharacters_DropBackslash) != 0 &&
aResult.Length() > 0 && aResult.Last() == '\\') {
aResult.Truncate(aResult.Length() - 1);
}
nsCSSScanner::AppendImpliedEOFCharacters(nsCSSScanner::EOFCharacters(eofChars),
aResult);
}
void
CSSLexer::NextToken(Nullable<CSSToken>& aResult)
{
nsCSSToken token;
if (!mScanner.Next(token, eCSSScannerExclude_None)) {
return;
}
CSSToken& resultToken(aResult.SetValue());
resultToken.mTokenType = static_cast<CSSTokenType>(token.mType);
resultToken.mStartOffset = mScanner.GetTokenOffset();
resultToken.mEndOffset = mScanner.GetTokenEndOffset();
switch (token.mType) {
case eCSSToken_Whitespace:
break;
case eCSSToken_Ident:
case eCSSToken_Function:
case eCSSToken_AtKeyword:
case eCSSToken_ID:
case eCSSToken_Hash:
resultToken.mText.Construct(token.mIdent);
break;
case eCSSToken_Dimension:
resultToken.mText.Construct(token.mIdent);
MOZ_FALLTHROUGH;
case eCSSToken_Number:
case eCSSToken_Percentage:
resultToken.mNumber.Construct(token.mNumber);
resultToken.mHasSign.Construct(token.mHasSign);
resultToken.mIsInteger.Construct(token.mIntegerValid);
break;
case eCSSToken_String:
case eCSSToken_Bad_String:
case eCSSToken_URL:
case eCSSToken_Bad_URL:
resultToken.mText.Construct(token.mIdent);
/* Don't bother emitting the delimiter, as it is readily extracted
from the source string when needed. */
break;
case eCSSToken_Symbol:
resultToken.mText.Construct(nsString(&token.mSymbol, 1));
break;
case eCSSToken_Includes:
case eCSSToken_Dashmatch:
case eCSSToken_Beginsmatch:
case eCSSToken_Endsmatch:
case eCSSToken_Containsmatch:
case eCSSToken_URange:
break;
case eCSSToken_Comment:
case eCSSToken_HTMLComment:
/* The comment text is easily extracted from the source string,
and is rarely useful. */
break;
}
}
} // namespace dom
} // namespace mozilla

39
layout/style/CSSLexer.h Normal file
View file

@ -0,0 +1,39 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 CSSLexer_h___
#define CSSLexer_h___
#include "mozilla/UniquePtr.h"
#include "nsCSSScanner.h"
#include "mozilla/dom/CSSLexerBinding.h"
namespace mozilla {
namespace dom {
class CSSLexer : public NonRefcountedDOMObject
{
public:
explicit CSSLexer(const nsAString&);
~CSSLexer();
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
uint32_t LineNumber();
uint32_t ColumnNumber();
void PerformEOFFixup(const nsAString& aInputString, bool aPreserveBackslash,
nsAString& aResult);
void NextToken(Nullable<CSSToken>& aResult);
private:
nsString mInput;
nsCSSScanner mScanner;
};
} // namespace dom
} // namespace mozilla
#endif /* CSSLexer_h___ */

View file

@ -0,0 +1,34 @@
/* -*- 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/dom/CSSRuleList.h"
#include "mozilla/dom/CSSRuleListBinding.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(CSSRuleList)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSRuleList)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(CSSRuleList)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRuleList)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(CSSRuleList)
NS_IMPL_CYCLE_COLLECTING_RELEASE(CSSRuleList)
/* virtual */ JSObject*
CSSRuleList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return CSSRuleListBinding::Wrap(aCx, this, aGivenProto);
}
} // namespace dom
} // namespace mozilla

View file

@ -0,0 +1,66 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 mozilla_dom_CSSRuleList_h
#define mozilla_dom_CSSRuleList_h
#include "mozilla/StyleSheetInlines.h"
#include "nsIDOMCSSRule.h"
#include "nsIDOMCSSRuleList.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
// IID for the CSSRuleList interface
#define NS_ICSSRULELIST_IID \
{ 0x56ac8d1c, 0xc1ed, 0x45fe, \
{ 0x9a, 0x4d, 0x3a, 0xdc, 0xf9, 0xd1, 0xb9, 0x3f } }
class CSSRuleList : public nsIDOMCSSRuleList
, public nsWrapperCache
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICSSRULELIST_IID)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CSSRuleList)
virtual CSSStyleSheet* GetParentObject() = 0;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override final;
NS_IMETHOD
GetLength(uint32_t* aLength) override final
{
*aLength = Length();
return NS_OK;
}
NS_IMETHOD
Item(uint32_t aIndex, nsIDOMCSSRule** aReturn) override final
{
NS_IF_ADDREF(*aReturn = Item(aIndex));
return NS_OK;
}
// WebIDL API
nsIDOMCSSRule* Item(uint32_t aIndex)
{
bool unused;
return IndexedGetter(aIndex, unused);
}
virtual nsIDOMCSSRule* IndexedGetter(uint32_t aIndex, bool& aFound) = 0;
virtual uint32_t Length() = 0;
protected:
virtual ~CSSRuleList() {}
};
NS_DEFINE_STATIC_IID_ACCESSOR(CSSRuleList, NS_ICSSRULELIST_IID)
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_CSSRuleList_h */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,281 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// vim:cindent:tabstop=2:expandtab:shiftwidth=2:
/* 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/. */
/* representation of a CSS style sheet */
#ifndef mozilla_CSSStyleSheet_h
#define mozilla_CSSStyleSheet_h
#include "mozilla/Attributes.h"
#include "mozilla/IncrementalClearCOMRuleArray.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInfo.h"
#include "mozilla/css/SheetParsingMode.h"
#include "mozilla/dom/Element.h"
#include "nscore.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsICSSLoaderObserver.h"
#include "nsTArrayForwardDeclare.h"
#include "nsString.h"
#include "mozilla/CORSMode.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/net/ReferrerPolicy.h"
#include "mozilla/dom/SRIMetadata.h"
class CSSRuleListImpl;
class nsCSSRuleProcessor;
class nsIURI;
class nsMediaList;
class nsMediaQueryResultCacheKey;
class nsStyleSet;
class nsPresContext;
class nsXMLNameSpaceMap;
namespace mozilla {
struct ChildSheetListBuilder;
class CSSStyleSheet;
namespace css {
class Rule;
class GroupRule;
class ImportRule;
} // namespace css
namespace dom {
class CSSRuleList;
} // namespace dom
// -------------------------------
// CSS Style Sheet Inner Data Container
//
struct CSSStyleSheetInner : public StyleSheetInfo
{
CSSStyleSheetInner(CSSStyleSheet* aPrimarySheet,
CORSMode aCORSMode,
ReferrerPolicy aReferrerPolicy,
const dom::SRIMetadata& aIntegrity);
CSSStyleSheetInner(CSSStyleSheetInner& aCopy,
CSSStyleSheet* aPrimarySheet);
~CSSStyleSheetInner();
CSSStyleSheetInner* CloneFor(CSSStyleSheet* aPrimarySheet);
void AddSheet(CSSStyleSheet* aSheet);
void RemoveSheet(CSSStyleSheet* aSheet);
void RebuildNameSpaces();
// Create a new namespace map
nsresult CreateNamespaceMap();
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
AutoTArray<CSSStyleSheet*, 8> mSheets;
IncrementalClearCOMRuleArray mOrderedRules;
nsAutoPtr<nsXMLNameSpaceMap> mNameSpaceMap;
// Linked list of child sheets. This is al fundamentally broken, because
// each of the child sheets has a unique parent... We can only hope (and
// currently this is the case) that any time page JS can get ts hands on a
// child sheet that means we've already ensured unique inners throughout its
// parent chain and things are good.
RefPtr<CSSStyleSheet> mFirstChild;
};
// -------------------------------
// CSS Style Sheet
//
// CID for the CSSStyleSheet class
// 7985c7ac-9ddc-444d-9899-0c86ec122f54
#define NS_CSS_STYLE_SHEET_IMPL_CID \
{ 0x7985c7ac, 0x9ddc, 0x444d, \
{ 0x98, 0x99, 0x0c, 0x86, 0xec, 0x12, 0x2f, 0x54 } }
class CSSStyleSheet final : public StyleSheet
, public nsICSSLoaderObserver
{
public:
typedef net::ReferrerPolicy ReferrerPolicy;
CSSStyleSheet(css::SheetParsingMode aParsingMode,
CORSMode aCORSMode, ReferrerPolicy aReferrerPolicy);
CSSStyleSheet(css::SheetParsingMode aParsingMode,
CORSMode aCORSMode, ReferrerPolicy aReferrerPolicy,
const dom::SRIMetadata& aIntegrity);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CSSStyleSheet, StyleSheet)
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_STYLE_SHEET_IMPL_CID)
bool HasRules() const;
/**
* Set the stylesheet to be enabled. This may or may not make it
* applicable. Note that this WILL inform the sheet's document of
* its new applicable state if the state changes but WILL NOT call
* BeginUpdate() or EndUpdate() on the document -- calling those is
* the caller's responsibility. This allows use of SetEnabled when
* batched updates are desired. If you want updates handled for
* you, see nsIDOMStyleSheet::SetDisabled().
*/
void SetEnabled(bool aEnabled);
// style sheet owner info
CSSStyleSheet* GetParentSheet() const; // may be null
void SetOwningDocument(nsIDocument* aDocument);
// Find the ID of the owner inner window.
uint64_t FindOwningWindowInnerID() const;
#ifdef DEBUG
void List(FILE* out = stdout, int32_t aIndent = 0) const;
#endif
void AppendStyleSheet(CSSStyleSheet* aSheet);
// XXX do these belong here or are they generic?
void AppendStyleRule(css::Rule* aRule);
int32_t StyleRuleCount() const;
css::Rule* GetStyleRuleAt(int32_t aIndex) const;
nsresult DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex);
nsresult InsertRuleIntoGroup(const nsAString& aRule, css::GroupRule* aGroup, uint32_t aIndex, uint32_t* _retval);
void SetTitle(const nsAString& aTitle) { mTitle = aTitle; }
void SetMedia(nsMediaList* aMedia);
void SetOwnerRule(css::ImportRule* aOwnerRule) { mOwnerRule = aOwnerRule; /* Not ref counted */ }
css::ImportRule* GetOwnerRule() const { return mOwnerRule; }
// Workaround overloaded-virtual warning in GCC.
using StyleSheet::GetOwnerRule;
nsXMLNameSpaceMap* GetNameSpaceMap() const { return mInner->mNameSpaceMap; }
already_AddRefed<CSSStyleSheet> Clone(CSSStyleSheet* aCloneParent,
css::ImportRule* aCloneOwnerRule,
nsIDocument* aCloneDocument,
nsINode* aCloneOwningNode) const;
bool IsModified() const { return mDirty; }
void SetModifiedByChildRule() {
NS_ASSERTION(mDirty,
"sheet must be marked dirty before handing out child rules");
DidDirty();
}
nsresult AddRuleProcessor(nsCSSRuleProcessor* aProcessor);
nsresult DropRuleProcessor(nsCSSRuleProcessor* aProcessor);
void AddStyleSet(nsStyleSet* aStyleSet);
void DropStyleSet(nsStyleSet* aStyleSet);
// nsICSSLoaderObserver interface
NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasAlternate,
nsresult aStatus) override;
void EnsureUniqueInner();
// Append all of this sheet's child sheets to aArray.
void AppendAllChildSheets(nsTArray<CSSStyleSheet*>& aArray);
bool UseForPresentation(nsPresContext* aPresContext,
nsMediaQueryResultCacheKey& aKey) const;
nsresult ReparseSheet(const nsAString& aInput);
void SetInRuleProcessorCache() { mInRuleProcessorCache = true; }
// Function used as a callback to rebuild our inner's child sheet
// list after we clone a unique inner for ourselves.
static bool RebuildChildList(css::Rule* aRule, void* aBuilder);
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
dom::Element* GetScopeElement() const { return mScopeElement; }
void SetScopeElement(dom::Element* aScopeElement)
{
mScopeElement = aScopeElement;
}
// WebIDL StyleSheet API
nsMediaList* Media() final;
// WebIDL CSSStyleSheet API
// Can't be inline because we can't include ImportRule here. And can't be
// called GetOwnerRule because that would be ambiguous with the ImportRule
// version.
nsIDOMCSSRule* GetDOMOwnerRule() const final;
void WillDirty();
void DidDirty();
private:
CSSStyleSheet(const CSSStyleSheet& aCopy,
CSSStyleSheet* aParentToUse,
css::ImportRule* aOwnerRuleToUse,
nsIDocument* aDocumentToUse,
nsINode* aOwningNodeToUse);
CSSStyleSheet(const CSSStyleSheet& aCopy) = delete;
CSSStyleSheet& operator=(const CSSStyleSheet& aCopy) = delete;
protected:
virtual ~CSSStyleSheet();
void ClearRuleCascades();
// Add the namespace mapping from this @namespace rule to our namespace map
nsresult RegisterNamespaceRule(css::Rule* aRule);
// Drop our reference to mRuleCollection
void DropRuleCollection();
// Drop our reference to mMedia
void DropMedia();
// Unlink our inner, if needed, for cycle collection
void UnlinkInner();
// Traverse our inner, if needed, for cycle collection
void TraverseInner(nsCycleCollectionTraversalCallback &);
protected:
// Internal methods which do not have security check and completeness check.
dom::CSSRuleList* GetCssRulesInternal(ErrorResult& aRv);
uint32_t InsertRuleInternal(const nsAString& aRule,
uint32_t aIndex, ErrorResult& aRv);
void DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv);
RefPtr<nsMediaList> mMedia;
RefPtr<CSSStyleSheet> mNext;
CSSStyleSheet* mParent; // weak ref
css::ImportRule* mOwnerRule; // weak ref
RefPtr<CSSRuleListImpl> mRuleCollection;
bool mDirty; // has been modified
bool mInRuleProcessorCache;
RefPtr<dom::Element> mScopeElement;
CSSStyleSheetInner* mInner;
AutoTArray<nsCSSRuleProcessor*, 8>* mRuleProcessors;
nsTArray<nsStyleSet*> mStyleSets;
friend class ::nsMediaList;
friend class ::nsCSSRuleProcessor;
friend class mozilla::StyleSheet;
friend struct mozilla::ChildSheetListBuilder;
};
NS_DEFINE_STATIC_IID_ACCESSOR(CSSStyleSheet, NS_CSS_STYLE_SHEET_IMPL_CID)
} // namespace mozilla
#endif /* !defined(mozilla_CSSStyleSheet_h) */

View file

@ -0,0 +1,342 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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/. */
/* Implementation of a service that converts certain vendor-prefixed CSS
properties to their unprefixed equivalents, for sites on a whitelist. */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function CSSUnprefixingService() {
}
CSSUnprefixingService.prototype = {
// Boilerplate:
classID: Components.ID("{f0729490-e15c-4a2f-a3fb-99e1cc946b42}"),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(CSSUnprefixingService),
QueryInterface: XPCOMUtils.generateQI([Ci.nsICSSUnprefixingService]),
// See documentation in nsICSSUnprefixingService.idl
generateUnprefixedDeclaration: function(aPropName, aRightHalfOfDecl,
aUnprefixedDecl /*out*/) {
// Convert our input strings to lower-case, for easier string-matching.
// (NOTE: If we ever need to add support for unprefixing properties that
// have case-sensitive parts, then we should do these toLowerCase()
// conversions in a more targeted way, to avoid breaking those properties.)
aPropName = aPropName.toLowerCase();
aRightHalfOfDecl = aRightHalfOfDecl.toLowerCase();
// We have several groups of supported properties:
// FIRST GROUP: Properties that can just be handled as aliases:
// ============================================================
const propertiesThatAreJustAliases = {
"-webkit-background-size": "background-size",
"-webkit-box-flex": "flex-grow",
"-webkit-box-ordinal-group": "order",
"-webkit-box-sizing": "box-sizing",
"-webkit-transform": "transform",
"-webkit-transform-origin": "transform-origin",
};
let unprefixedPropName = propertiesThatAreJustAliases[aPropName];
if (unprefixedPropName !== undefined) {
aUnprefixedDecl.value = unprefixedPropName + ":" + aRightHalfOfDecl;
return true;
}
// SECOND GROUP: Properties that take a single keyword, where the
// unprefixed version takes a different (but analogous) set of keywords:
// =====================================================================
const propertiesThatNeedKeywordMapping = {
"-webkit-box-align" : {
unprefixedPropName : "align-items",
valueMap : {
"start" : "flex-start",
"center" : "center",
"end" : "flex-end",
"baseline" : "baseline",
"stretch" : "stretch"
}
},
"-webkit-box-orient" : {
unprefixedPropName : "flex-direction",
valueMap : {
"horizontal" : "row",
"inline-axis" : "row",
"vertical" : "column",
"block-axis" : "column"
}
},
"-webkit-box-pack" : {
unprefixedPropName : "justify-content",
valueMap : {
"start" : "flex-start",
"center" : "center",
"end" : "flex-end",
"justify" : "space-between"
}
},
};
let propInfo = propertiesThatNeedKeywordMapping[aPropName];
if (typeof(propInfo) != "undefined") {
// Regexp for parsing the right half of a declaration, for keyword-valued
// properties. Divides the right half of the declaration into:
// 1) any leading whitespace
// 2) the property value (one or more alphabetical character or hyphen)
// 3) anything after that (e.g. "!important", ";")
// Then we can look up the appropriate unprefixed-property value for the
// value (part 2), and splice that together with the other parts and with
// the unprefixed property-name to make the final declaration.
const keywordValuedPropertyRegexp = /^(\s*)([a-z\-]+)(.*)/;
let parts = keywordValuedPropertyRegexp.exec(aRightHalfOfDecl);
if (!parts) {
// Failed to parse a keyword out of aRightHalfOfDecl. (It probably has
// no alphabetical characters.)
return false;
}
let mappedKeyword = propInfo.valueMap[parts[2]];
if (mappedKeyword === undefined) {
// We found a keyword in aRightHalfOfDecl, but we don't have a mapping
// to an equivalent keyword for the unprefixed version of the property.
return false;
}
aUnprefixedDecl.value = propInfo.unprefixedPropName + ":" +
parts[1] + // any leading whitespace
mappedKeyword +
parts[3]; // any trailing text (e.g. !important, semicolon, etc)
return true;
}
// THIRD GROUP: Properties that may need arbitrary string-replacement:
// ===================================================================
const propertiesThatNeedStringReplacement = {
// "-webkit-transition" takes a multi-part value. If "-webkit-transform"
// appears as part of that value, replace it w/ "transform".
// And regardless, we unprefix the "-webkit-transition" property-name.
// (We could handle other prefixed properties in addition to 'transform'
// here, but in practice "-webkit-transform" is the main one that's
// likely to be transitioned & that we're concerned about supporting.)
"-webkit-transition": {
unprefixedPropName : "transition",
stringMap : {
"-webkit-transform" : "transform",
}
},
};
propInfo = propertiesThatNeedStringReplacement[aPropName];
if (typeof(propInfo) != "undefined") {
let newRightHalf = aRightHalfOfDecl;
for (let strToReplace in propInfo.stringMap) {
let replacement = propInfo.stringMap[strToReplace];
newRightHalf = newRightHalf.split(strToReplace).join(replacement);
}
aUnprefixedDecl.value = propInfo.unprefixedPropName + ":" + newRightHalf;
return true;
}
// No known mapping for property aPropName.
return false;
},
// See documentation in nsICSSUnprefixingService.idl
generateUnprefixedGradientValue: function(aPrefixedFuncName,
aPrefixedFuncBody,
aUnprefixedFuncName, /*[out]*/
aUnprefixedFuncBody /*[out]*/) {
var unprefixedFuncName, newValue;
if (aPrefixedFuncName == "-webkit-gradient") {
// Create expression for oldGradientParser:
var parts = this.oldGradientParser(aPrefixedFuncBody);
var type = parts[0].name;
newValue = this.standardizeOldGradientArgs(type, parts.slice(1));
unprefixedFuncName = type + "-gradient";
}else{ // we're dealing with more modern syntax - should be somewhat easier, at least for linear gradients.
// Fix three things: remove -webkit-, add 'to ' before reversed top/bottom keywords (linear) or 'at ' before position keywords (radial), recalculate deg-values
// -webkit-linear-gradient( [ [ <angle> | [top | bottom] || [left | right] ],]? <color-stop>[, <color-stop>]+);
if (aPrefixedFuncName != "-webkit-linear-gradient" &&
aPrefixedFuncName != "-webkit-radial-gradient") {
// Unrecognized prefixed gradient type
return false;
}
unprefixedFuncName = aPrefixedFuncName.replace(/-webkit-/, '');
// Keywords top, bottom, left, right: can be stand-alone or combined pairwise but in any order ('top left' or 'left top')
// These give the starting edge or corner in the -webkit syntax. The standardised equivalent is 'to ' plus opposite values for linear gradients, 'at ' plus same values for radial gradients
if(unprefixedFuncName.indexOf('linear') > -1){
newValue = aPrefixedFuncBody.replace(/(top|bottom|left|right)+\s*(top|bottom|left|right)*/, function(str){
var words = str.split(/\s+/);
for(var i=0; i<words.length; i++){
switch(words[i].toLowerCase()){
case 'top':
words[i] = 'bottom';
break;
case 'bottom':
words[i] = 'top';
break;
case 'left':
words[i] = 'right';
break;
case 'right':
words[i] = 'left';
}
}
str = words.join(' ');
return ( 'to ' + str);
});
}else{
newValue = aPrefixedFuncBody.replace(/(top|bottom|left|right)+\s/, 'at $1 ');
}
newValue = newValue.replace(/\d+deg/, function (val) {
return (360 - (parseInt(val)-90))+'deg';
});
}
aUnprefixedFuncName.value = unprefixedFuncName;
aUnprefixedFuncBody.value = newValue;
return true;
},
// Helpers for generateUnprefixedGradientValue():
// ----------------------------------------------
oldGradientParser : function(str){
/** This method takes a legacy -webkit-gradient() method call and parses it
to pull out the values, function names and their arguments.
It returns something like [{name:'-webkit-gradient',args:[{name:'linear'}, {name:'top left'} ... ]}]
*/
var objs = [{}], path=[], current, word='', separator_chars = [',', '(', ')'];
current = objs[0], path[0] = objs;
//str = str.replace(/\s*\(/g, '('); // sorry, ws in front of ( would make parsing a lot harder
for(var i = 0; i < str.length; i++){
if(separator_chars.indexOf(str[i]) === -1){
word += str[i];
}else{ // now we have a "separator" - presumably we've also got a "word" or value
current.name = word.trim();
//GM_log(word+' '+path.length+' '+str[i])
word = '';
if(str[i] === '('){ // we assume the 'word' is a function, for example -webkit-gradient() or rgb(), so we create a place to record the arguments
if(!('args' in current)){
current.args = [];
}
current.args.push({});
path.push(current.args);
current = current.args[current.args.length - 1];
path.push(current);
}else if(str[i] === ')'){ // function is ended, no more arguments - go back to appending details to the previous branch of the tree
current = path.pop(); // drop 'current'
current = path.pop(); // drop 'args' reference
}else{
path.pop(); // remove 'current' object from path, we have no arguments to add
var current_parent = path[path.length - 1] || objs; // last object on current path refers to array that contained the previous "current"
current_parent.push({}); // we need a new object to hold this "word" or value
current = current_parent[current_parent.length - 1]; // that object is now the 'current'
path.push(current);
//GM_log(path.length)
}
}
}
return objs;
},
/* Given an array of args for "-webkit-gradient(...)" returned by
* oldGradientParser(), this function constructs a string representing the
* equivalent arguments for a standard "linear-gradient(...)" or
* "radial-gradient(...)" expression.
*
* @param type Either 'linear' or 'radial'.
* @param args An array of args for a "-webkit-gradient(...)" expression,
* provided by oldGradientParser() (not including gradient type).
*/
standardizeOldGradientArgs : function(type, args){
var stdArgStr = "";
var stops = [];
if(/^linear/.test(type)){
// linear gradient, args 1 and 2 tend to be start/end keywords
var points = [].concat(args[0].name.split(/\s+/), args[1].name.split(/\s+/)); // example: [left, top, right, top]
// Old webkit syntax "uses a two-point syntax that lets you explicitly state where a linear gradient starts and ends"
// if start/end keywords are percentages, let's massage the values a little more..
var rxPercTest = /\d+\%/;
if(rxPercTest.test(points[0]) || points[0] == 0){
var startX = parseInt(points[0]), startY = parseInt(points[1]), endX = parseInt(points[2]), endY = parseInt(points[3]);
stdArgStr += ((Math.atan2(endY- startY, endX - startX)) * (180 / Math.PI)+90) + 'deg';
}else{
if(points[1] === points[3]){ // both 'top' or 'bottom, this linear gradient goes left-right
stdArgStr += 'to ' + points[2];
}else if(points[0] === points[2]){ // both 'left' or 'right', this linear gradient goes top-bottom
stdArgStr += 'to ' + points[3];
}else if(points[1] === 'top'){ // diagonal gradient - from top left to opposite corner is 135deg
stdArgStr += '135deg';
}else{
stdArgStr += '45deg';
}
}
}else if(/^radial/i.test(type)){ // oooh, radial gradients..
stdArgStr += 'circle ' + args[3].name.replace(/(\d+)$/, '$1px') + ' at ' + args[0].name.replace(/(\d+) /, '$1px ').replace(/(\d+)$/, '$1px');
}
var toColor;
for(var j = type === 'linear' ? 2 : 4; j < args.length; j++){
var position, color, colorIndex;
if(args[j].name === 'color-stop'){
position = args[j].args[0].name;
colorIndex = 1;
}else if (args[j].name === 'to') {
position = '100%';
colorIndex = 0;
}else if (args[j].name === 'from') {
position = '0%';
colorIndex = 0;
};
if (position.indexOf('%') === -1) { // original Safari syntax had 0.5 equivalent to 50%
position = (parseFloat(position) * 100) +'%';
};
color = args[j].args[colorIndex].name;
if (args[j].args[colorIndex].args) { // the color is itself a function call, like rgb()
color += '(' + this.colorValue(args[j].args[colorIndex].args) + ')';
};
if (args[j].name === 'from'){
stops.unshift(color + ' ' + position);
}else if(args[j].name === 'to'){
toColor = color;
}else{
stops.push(color + ' ' + position);
}
}
// translating values to right syntax
for(var j = 0; j < stops.length; j++){
stdArgStr += ', ' + stops[j];
}
if(toColor){
stdArgStr += ', ' + toColor + ' 100%';
}
return stdArgStr;
},
colorValue: function(obj){
var ar = [];
for (var i = 0; i < obj.length; i++) {
ar.push(obj[i].name);
};
return ar.join(', ');
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([CSSUnprefixingService]);

View file

@ -0,0 +1,2 @@
component {f0729490-e15c-4a2f-a3fb-99e1cc946b42} CSSUnprefixingService.js
contract @mozilla.org/css-unprefixing-service;1 {f0729490-e15c-4a2f-a3fb-99e1cc946b42}

47
layout/style/CSSValue.h Normal file
View file

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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/. */
/* DOM object representing values in DOM computed style */
#ifndef mozilla_dom_CSSValue_h_
#define mozilla_dom_CSSValue_h_
#include "nsWrapperCache.h"
#include "nsStringFwd.h"
class nsROCSSPrimitiveValue;
namespace mozilla {
class ErrorResult;
} // namespace mozilla
namespace mozilla {
namespace dom {
/**
* CSSValue - a DOM object representing values in DOM computed style.
*/
class CSSValue : public nsISupports,
public nsWrapperCache
{
public:
// CSSValue
virtual void GetCssText(nsString& aText, mozilla::ErrorResult& aRv) = 0;
virtual void SetCssText(const nsAString& aText, mozilla::ErrorResult& aRv) = 0;
virtual uint16_t CssValueType() const = 0;
// Downcasting
/**
* Return this as a nsROCSSPrimitiveValue* if its a primitive value, and null
* otherwise.
*/
nsROCSSPrimitiveValue *AsPrimitiveValue();
};
} // namespace dom
} // namespace mozilla
#endif

View file

@ -0,0 +1,195 @@
/* -*- 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/. */
/* CSS Custom Property assignments for a Declaration at a given priority */
#include "CSSVariableDeclarations.h"
#include "CSSVariableResolver.h"
#include "nsCSSScanner.h"
#include "nsRuleData.h"
// These three special string values are used to represent specified values of
// 'initial', 'inherit' and 'unset'. (Note that none of these are valid
// variable values.)
#define INITIAL_VALUE "!"
#define INHERIT_VALUE ";"
#define UNSET_VALUE ")"
namespace mozilla {
CSSVariableDeclarations::CSSVariableDeclarations()
{
MOZ_COUNT_CTOR(CSSVariableDeclarations);
}
CSSVariableDeclarations::CSSVariableDeclarations(const CSSVariableDeclarations& aOther)
{
MOZ_COUNT_CTOR(CSSVariableDeclarations);
CopyVariablesFrom(aOther);
}
#ifdef DEBUG
CSSVariableDeclarations::~CSSVariableDeclarations()
{
MOZ_COUNT_DTOR(CSSVariableDeclarations);
}
#endif
CSSVariableDeclarations&
CSSVariableDeclarations::operator=(const CSSVariableDeclarations& aOther)
{
if (this == &aOther) {
return *this;
}
mVariables.Clear();
CopyVariablesFrom(aOther);
return *this;
}
void
CSSVariableDeclarations::CopyVariablesFrom(const CSSVariableDeclarations& aOther)
{
for (auto iter = aOther.mVariables.ConstIter(); !iter.Done(); iter.Next()) {
mVariables.Put(iter.Key(), iter.UserData());
}
}
bool
CSSVariableDeclarations::Has(const nsAString& aName) const
{
nsString value;
return mVariables.Get(aName, &value);
}
bool
CSSVariableDeclarations::Get(const nsAString& aName,
Type& aType,
nsString& aTokenStream) const
{
nsString value;
if (!mVariables.Get(aName, &value)) {
return false;
}
if (value.EqualsLiteral(INITIAL_VALUE)) {
aType = eInitial;
aTokenStream.Truncate();
} else if (value.EqualsLiteral(INHERIT_VALUE)) {
aType = eInitial;
aTokenStream.Truncate();
} else if (value.EqualsLiteral(UNSET_VALUE)) {
aType = eUnset;
aTokenStream.Truncate();
} else {
aType = eTokenStream;
aTokenStream = value;
}
return true;
}
void
CSSVariableDeclarations::PutTokenStream(const nsAString& aName,
const nsString& aTokenStream)
{
MOZ_ASSERT(!aTokenStream.EqualsLiteral(INITIAL_VALUE) &&
!aTokenStream.EqualsLiteral(INHERIT_VALUE) &&
!aTokenStream.EqualsLiteral(UNSET_VALUE));
mVariables.Put(aName, aTokenStream);
}
void
CSSVariableDeclarations::PutInitial(const nsAString& aName)
{
mVariables.Put(aName, NS_LITERAL_STRING(INITIAL_VALUE));
}
void
CSSVariableDeclarations::PutInherit(const nsAString& aName)
{
mVariables.Put(aName, NS_LITERAL_STRING(INHERIT_VALUE));
}
void
CSSVariableDeclarations::PutUnset(const nsAString& aName)
{
mVariables.Put(aName, NS_LITERAL_STRING(UNSET_VALUE));
}
void
CSSVariableDeclarations::Remove(const nsAString& aName)
{
mVariables.Remove(aName);
}
void
CSSVariableDeclarations::MapRuleInfoInto(nsRuleData* aRuleData)
{
if (!(aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Variables))) {
return;
}
if (!aRuleData->mVariables) {
aRuleData->mVariables = new CSSVariableDeclarations(*this);
} else {
for (auto iter = mVariables.Iter(); !iter.Done(); iter.Next()) {
nsDataHashtable<nsStringHashKey, nsString>& variables =
aRuleData->mVariables->mVariables;
const nsAString& aName = iter.Key();
if (!variables.Contains(aName)) {
variables.Put(aName, iter.UserData());
}
}
}
}
void
CSSVariableDeclarations::AddVariablesToResolver(
CSSVariableResolver* aResolver) const
{
for (auto iter = mVariables.ConstIter(); !iter.Done(); iter.Next()) {
const nsAString& name = iter.Key();
nsString value = iter.UserData();
if (value.EqualsLiteral(INITIAL_VALUE)) {
// Values of 'initial' are treated the same as an invalid value in the
// variable resolver.
aResolver->Put(name, EmptyString(),
eCSSTokenSerialization_Nothing,
eCSSTokenSerialization_Nothing,
false);
} else if (value.EqualsLiteral(INHERIT_VALUE) ||
value.EqualsLiteral(UNSET_VALUE)) {
// Values of 'inherit' and 'unset' don't need any handling, since it means
// we just need to keep whatever value is currently in the resolver. This
// is because the specified variable declarations already have only the
// winning declaration for the variable and no longer have any of the
// others.
} else {
// At this point, we don't know what token types are at the start and end
// of the specified variable value. These will be determined later during
// the resolving process.
aResolver->Put(name, value,
eCSSTokenSerialization_Nothing,
eCSSTokenSerialization_Nothing,
false);
}
}
}
size_t
CSSVariableDeclarations::SizeOfIncludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
n += mVariables.ShallowSizeOfExcludingThis(aMallocSizeOf);
for (auto iter = mVariables.ConstIter(); !iter.Done(); iter.Next()) {
n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
n += iter.Data().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
}
return n;
}
} // namespace mozilla

View file

@ -0,0 +1,139 @@
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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/. */
/* CSS Custom Property assignments for a Declaration at a given priority */
#ifndef mozilla_CSSVariableDeclarations_h
#define mozilla_CSSVariableDeclarations_h
#include "nsDataHashtable.h"
namespace mozilla {
class CSSVariableResolver;
} // namespace mozilla
struct nsRuleData;
namespace mozilla {
class CSSVariableDeclarations
{
public:
CSSVariableDeclarations();
CSSVariableDeclarations(const CSSVariableDeclarations& aOther);
#ifdef DEBUG
~CSSVariableDeclarations();
#endif
CSSVariableDeclarations& operator=(const CSSVariableDeclarations& aOther);
/**
* Returns whether this set of variable declarations includes a variable
* with a given name.
*
* @param aName The variable name (not including any "--" prefix that would
* be part of the custom property name).
*/
bool Has(const nsAString& aName) const;
/**
* Represents the type of a variable value.
*/
enum Type {
eTokenStream, // a stream of CSS tokens (the usual type for variables)
eInitial, // 'initial'
eInherit, // 'inherit'
eUnset // 'unset'
};
/**
* Gets the value of a variable in this set of variable declarations.
*
* @param aName The variable name (not including any "--" prefix that would
* be part of the custom property name).
* @param aType Out parameter into which the type of the variable value will
* be stored.
* @param aValue Out parameter into which the value of the variable will
* be stored. If the variable is 'initial', 'inherit' or 'unset', this will
* be the empty string.
* @return Whether a variable with the given name was found. When false
* is returned, aType and aValue will not be modified.
*/
bool Get(const nsAString& aName, Type& aType, nsString& aValue) const;
/**
* Adds or modifies an existing entry in this set of variable declarations
* to have the value 'initial'.
*
* @param aName The variable name (not including any "--" prefix that would
* be part of the custom property name) whose value is to be set.
*/
void PutInitial(const nsAString& aName);
/**
* Adds or modifies an existing entry in this set of variable declarations
* to have the value 'inherit'.
*
* @param aName The variable name (not including any "--" prefix that would
* be part of the custom property name) whose value is to be set.
*/
void PutInherit(const nsAString& aName);
/**
* Adds or modifies an existing entry in this set of variable declarations
* to have the value 'unset'.
*
* @param aName The variable name (not including any "--" prefix that would
* be part of the custom property name) whose value is to be set.
*/
void PutUnset(const nsAString& aName);
/**
* Adds or modifies an existing entry in this set of variable declarations
* to have a token stream value.
*
* @param aName The variable name (not including any "--" prefix that would
* be part of the custom property name) whose value is to be set.
* @param aTokenStream The CSS token stream.
*/
void PutTokenStream(const nsAString& aName, const nsString& aTokenStream);
/**
* Removes an entry in this set of variable declarations.
*
* @param aName The variable name (not including any "--" prefix that would
* be part of the custom property name) whose entry is to be removed.
*/
void Remove(const nsAString& aName);
/**
* Returns the number of entries in this set of variable declarations.
*/
uint32_t Count() const { return mVariables.Count(); }
/**
* Copies each variable value from this object into aRuleData, unless that
* variable already exists on aRuleData.
*/
void MapRuleInfoInto(nsRuleData* aRuleData);
/**
* Copies the variables from this object into aResolver, marking them as
* specified values.
*/
void AddVariablesToResolver(CSSVariableResolver* aResolver) const;
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
private:
/**
* Adds all the variable declarations from aOther into this object.
*/
void CopyVariablesFrom(const CSSVariableDeclarations& aOther);
nsDataHashtable<nsStringHashKey, nsString> mVariables;
};
} // namespace mozilla
#endif

View file

@ -0,0 +1,190 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 global table that tracks images referenced by CSS variables. */
#ifndef mozilla_CSSVariableImageTable_h
#define mozilla_CSSVariableImageTable_h
#include "nsClassHashtable.h"
#include "nsCSSPropertyID.h"
#include "nsCSSValue.h"
#include "nsStyleContext.h"
#include "nsTArray.h"
/**
* CSSVariableImageTable maintains a global mapping
* (nsStyleContext, nsCSSPropertyID) -> nsTArray<ImageValue>
* which allows us to track the relationship between CSS property values
* involving variables and any images they may reference.
*
* When properties like background-image contain a normal url(), the
* Declaration's data block will hold a reference to the ImageValue. When a
* token stream is used, the Declaration only holds on to an
* nsCSSValueTokenStream object, and the ImageValue would only exist for the
* duration of nsRuleNode::WalkRuleTree, in the AutoCSSValueArray. So instead
* when we re-parse a token stream and get an ImageValue, we record it in the
* CSSVariableImageTable to keep the ImageValue alive. Such ImageValues are
* eventually freed the next time the token stream is re-parsed, or when the
* associated style context is destroyed.
*
* To add ImageValues to the CSSVariableImageTable, callers should pass a lambda
* to CSSVariableImageTable::ReplaceAll() that calls
* CSSVariableImageTable::Add() for each ImageValue that needs to be added to
* the table. When callers are sure that the ImageValues for a given
* nsStyleContext won't be needed anymore, they can call
* CSSVariableImageTable::RemoveAll() to release them.
*/
namespace mozilla {
namespace CSSVariableImageTable {
namespace detail {
typedef nsTArray<RefPtr<css::ImageValue>> ImageValueArray;
typedef nsClassHashtable<nsGenericHashKey<nsCSSPropertyID>, ImageValueArray>
PerPropertyImageHashtable;
typedef nsClassHashtable<nsPtrHashKey<nsStyleContext>, PerPropertyImageHashtable>
CSSVariableImageHashtable;
inline CSSVariableImageHashtable& GetTable()
{
static CSSVariableImageHashtable imageTable;
return imageTable;
}
#ifdef DEBUG
inline bool& IsReplacing()
{
static bool isReplacing = false;
return isReplacing;
}
#endif
} // namespace detail
/**
* ReplaceAll() allows callers to replace the ImageValues associated with a
* (nsStyleContext, nsCSSPropertyID) pair. The memory used by the previous list of
* ImageValues is automatically released.
*
* @param aContext The style context the ImageValues are associated with.
* @param aProp The CSS property the ImageValues are associated with.
* @param aFunc A lambda that calls CSSVariableImageTable::Add() to add new
* ImageValues which will replace the old ones.
*/
template <typename Lambda>
inline void ReplaceAll(nsStyleContext* aContext,
nsCSSPropertyID aProp,
Lambda aFunc)
{
MOZ_ASSERT(aContext);
auto& imageTable = detail::GetTable();
// Clear the existing image array, if any, for this property.
{
auto* perPropertyImageTable = imageTable.Get(aContext);
auto* imageList = perPropertyImageTable ? perPropertyImageTable->Get(aProp)
: nullptr;
if (imageList) {
imageList->ClearAndRetainStorage();
}
}
#ifdef DEBUG
MOZ_ASSERT(!detail::IsReplacing());
detail::IsReplacing() = true;
#endif
aFunc();
#ifdef DEBUG
detail::IsReplacing() = false;
#endif
// Clean up.
auto* perPropertyImageTable = imageTable.Get(aContext);
auto* imageList = perPropertyImageTable ? perPropertyImageTable->Get(aProp)
: nullptr;
if (imageList) {
if (imageList->IsEmpty()) {
// We used to have an image array for this property, but now we don't.
// Remove the entry in the per-property image table for this property.
// That may then allow us to remove the entire per-property image table.
perPropertyImageTable->Remove(aProp);
if (perPropertyImageTable->Count() == 0) {
imageTable.Remove(aContext);
}
} else {
// We still have a non-empty image array for this property. Compact the
// storage it's using if possible.
imageList->Compact();
}
}
}
/**
* Adds a new ImageValue @aValue to the CSSVariableImageTable, which will be
* associated with @aContext and @aProp.
*
* It's illegal to call this function outside of a lambda passed to
* CSSVariableImageTable::ReplaceAll().
*/
inline void
Add(nsStyleContext* aContext, nsCSSPropertyID aProp, css::ImageValue* aValue)
{
MOZ_ASSERT(aValue);
MOZ_ASSERT(aContext);
MOZ_ASSERT(detail::IsReplacing());
auto& imageTable = detail::GetTable();
// Ensure there's a per-property image table for this style context.
auto* perPropertyImageTable = imageTable.Get(aContext);
if (!perPropertyImageTable) {
perPropertyImageTable = new detail::PerPropertyImageHashtable();
imageTable.Put(aContext, perPropertyImageTable);
}
// Ensure there's an image array for this property.
auto* imageList = perPropertyImageTable->Get(aProp);
if (!imageList) {
imageList = new detail::ImageValueArray();
perPropertyImageTable->Put(aProp, imageList);
}
// Append the provided ImageValue to the list.
imageList->AppendElement(aValue);
}
/**
* Removes all ImageValues stored in the CSSVariableImageTable for the provided
* @aContext.
*/
inline void
RemoveAll(nsStyleContext* aContext)
{
// Move all ImageValue references into removedImageList so that we can
// release them outside of any hashtable methods. (If we just call
// Remove(aContext) on the table then we can end up calling back
// re-entrantly into hashtable methods, as other style contexts
// are released.)
detail::ImageValueArray removedImages;
auto& imageTable = detail::GetTable();
auto* perPropertyImageTable = imageTable.Get(aContext);
if (perPropertyImageTable) {
for (auto it = perPropertyImageTable->Iter(); !it.Done(); it.Next()) {
auto* imageList = it.UserData();
removedImages.AppendElements(Move(*imageList));
}
}
imageTable.Remove(aContext);
}
} // namespace CSSVariableImageTable
} // namespace mozilla
#endif // mozilla_CSSVariableImageTable_h

View file

@ -0,0 +1,266 @@
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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/. */
/* object that resolves CSS variables using specified and inherited variable
* values
*/
#include "CSSVariableResolver.h"
#include "CSSVariableDeclarations.h"
#include "CSSVariableValues.h"
#include "mozilla/PodOperations.h"
#include "mozilla/UniquePtr.h"
#include <algorithm>
namespace mozilla {
/**
* Data used by the EnumerateVariableReferences callback. Reset must be called
* on it before it is used.
*/
class EnumerateVariableReferencesData
{
public:
explicit EnumerateVariableReferencesData(CSSVariableResolver& aResolver)
: mResolver(aResolver)
, mReferences(MakeUnique<bool[]>(aResolver.mVariables.Length()))
{
}
/**
* Resets the data so that it can be passed to another call of
* EnumerateVariableReferences for a different variable.
*/
void Reset()
{
PodZero(mReferences.get(), mResolver.mVariables.Length());
mReferencesNonExistentVariable = false;
}
void RecordVariableReference(const nsAString& aVariableName)
{
size_t id;
if (mResolver.mVariableIDs.Get(aVariableName, &id)) {
mReferences[id] = true;
} else {
mReferencesNonExistentVariable = true;
}
}
bool HasReferenceToVariable(size_t aID) const
{
return mReferences[aID];
}
bool ReferencesNonExistentVariable() const
{
return mReferencesNonExistentVariable;
}
private:
CSSVariableResolver& mResolver;
// Array of booleans, where each index is a variable ID. If an element is
// true, it indicates that the variable we have called
// EnumerateVariableReferences for has a reference to the variable with
// that ID.
const UniquePtr<bool[]> mReferences;
// Whether the variable we have called EnumerateVariableReferences for
// references a variable that does not exist in the resolver.
bool mReferencesNonExistentVariable;
};
static void
RecordVariableReference(const nsAString& aVariableName,
void* aData)
{
static_cast<EnumerateVariableReferencesData*>(aData)->
RecordVariableReference(aVariableName);
}
void
CSSVariableResolver::RemoveCycles(size_t v)
{
mVariables[v].mIndex = mNextIndex;
mVariables[v].mLowLink = mNextIndex;
mVariables[v].mInStack = true;
mStack.AppendElement(v);
mNextIndex++;
for (size_t i = 0, n = mReferences[v].Length(); i < n; i++) {
size_t w = mReferences[v][i];
if (!mVariables[w].mIndex) {
RemoveCycles(w);
mVariables[v].mLowLink = std::min(mVariables[v].mLowLink,
mVariables[w].mLowLink);
} else if (mVariables[w].mInStack) {
mVariables[v].mLowLink = std::min(mVariables[v].mLowLink,
mVariables[w].mIndex);
}
}
if (mVariables[v].mLowLink == mVariables[v].mIndex) {
if (mStack.LastElement() == v) {
// A strongly connected component consisting of a single variable is not
// necessarily invalid. We handle variables that reference themselves
// earlier, in CSSVariableResolver::Resolve.
mVariables[mStack.LastElement()].mInStack = false;
mStack.TruncateLength(mStack.Length() - 1);
} else {
size_t w;
do {
w = mStack.LastElement();
mVariables[w].mValue.Truncate(0);
mVariables[w].mInStack = false;
mStack.TruncateLength(mStack.Length() - 1);
} while (w != v);
}
}
}
void
CSSVariableResolver::ResolveVariable(size_t aID)
{
if (mVariables[aID].mValue.IsEmpty() || mVariables[aID].mWasInherited) {
// The variable is invalid or was inherited. We can just copy the value
// and its first/last token information across.
mOutput->Put(mVariables[aID].mVariableName,
mVariables[aID].mValue,
mVariables[aID].mFirstToken,
mVariables[aID].mLastToken);
} else {
// Otherwise we need to resolve the variable references, after resolving
// all of our dependencies first. We do this even for variables that we
// know do not reference other variables so that we can find their
// first/last token.
//
// XXX We might want to do this first/last token finding during
// EnumerateVariableReferences, so that we can avoid calling
// ResolveVariableValue and parsing the value again.
for (size_t i = 0, n = mReferences[aID].Length(); i < n; i++) {
size_t j = mReferences[aID][i];
if (aID != j && !mVariables[j].mResolved) {
ResolveVariable(j);
}
}
nsString resolvedValue;
nsCSSTokenSerializationType firstToken, lastToken;
if (!mParser.ResolveVariableValue(mVariables[aID].mValue, mOutput,
resolvedValue, firstToken, lastToken)) {
resolvedValue.Truncate(0);
}
mOutput->Put(mVariables[aID].mVariableName, resolvedValue,
firstToken, lastToken);
}
mVariables[aID].mResolved = true;
}
void
CSSVariableResolver::Resolve(const CSSVariableValues* aInherited,
const CSSVariableDeclarations* aSpecified)
{
MOZ_ASSERT(!mResolved);
// The only time we would be worried about having a null aInherited is
// for the root, but in that case nsRuleNode::ComputeVariablesData will
// happen to pass in whatever we're using as mOutput for aInherited,
// which will initially be empty.
MOZ_ASSERT(aInherited);
MOZ_ASSERT(aSpecified);
aInherited->AddVariablesToResolver(this);
aSpecified->AddVariablesToResolver(this);
// First we look at each variable's value and record which other variables
// it references.
size_t n = mVariables.Length();
mReferences.SetLength(n);
EnumerateVariableReferencesData data(*this);
for (size_t id = 0; id < n; id++) {
data.Reset();
if (!mVariables[id].mWasInherited &&
!mVariables[id].mValue.IsEmpty()) {
if (mParser.EnumerateVariableReferences(mVariables[id].mValue,
RecordVariableReference,
&data)) {
// Convert the boolean array of dependencies in |data| to a list
// of dependencies.
for (size_t i = 0; i < n; i++) {
if (data.HasReferenceToVariable(i)) {
mReferences[id].AppendElement(i);
}
}
// If a variable references itself, it is invalid. (RemoveCycles
// does not check for cycles consisting of a single variable, so we
// check here.)
if (data.HasReferenceToVariable(id)) {
mVariables[id].mValue.Truncate();
}
// Also record whether it referenced any variables that don't exist
// in the resolver, so that we can ensure we still resolve its value
// in ResolveVariable, even though its mReferences list is empty.
mVariables[id].mReferencesNonExistentVariable =
data.ReferencesNonExistentVariable();
} else {
MOZ_ASSERT(false, "EnumerateVariableReferences should not have failed "
"if we previously parsed the specified value");
mVariables[id].mValue.Truncate(0);
}
}
}
// Next we remove any cycles in variable references using Tarjan's strongly
// connected components finding algorithm, setting variables in cycles to
// have an invalid value.
mNextIndex = 1;
for (size_t id = 0; id < n; id++) {
if (!mVariables[id].mIndex) {
RemoveCycles(id);
MOZ_ASSERT(mStack.IsEmpty());
}
}
// Finally we construct the computed value for the variable by substituting
// any variable references.
for (size_t id = 0; id < n; id++) {
if (!mVariables[id].mResolved) {
ResolveVariable(id);
}
}
#ifdef DEBUG
mResolved = true;
#endif
}
void
CSSVariableResolver::Put(const nsAString& aVariableName,
nsString aValue,
nsCSSTokenSerializationType aFirstToken,
nsCSSTokenSerializationType aLastToken,
bool aWasInherited)
{
MOZ_ASSERT(!mResolved);
size_t id;
if (mVariableIDs.Get(aVariableName, &id)) {
MOZ_ASSERT(mVariables[id].mWasInherited && !aWasInherited,
"should only overwrite inherited variables with specified "
"variables");
mVariables[id].mValue = aValue;
mVariables[id].mFirstToken = aFirstToken;
mVariables[id].mLastToken = aLastToken;
mVariables[id].mWasInherited = aWasInherited;
} else {
id = mVariables.Length();
mVariableIDs.Put(aVariableName, id);
mVariables.AppendElement(Variable(aVariableName, aValue,
aFirstToken, aLastToken, aWasInherited));
}
}
} // namespace mozilla

View file

@ -0,0 +1,148 @@
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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/. */
/* object that resolves CSS variables using specified and inherited variable
* values
*/
#ifndef mozilla_CSSVariableResolver_h
#define mozilla_CSSVariableResolver_h
#include "mozilla/DebugOnly.h"
#include "nsCSSParser.h"
#include "nsCSSScanner.h"
#include "nsDataHashtable.h"
#include "nsTArray.h"
namespace mozilla {
class CSSVariableDeclarations;
class CSSVariableValues;
class EnumerateVariableReferencesData;
class CSSVariableResolver
{
friend class CSSVariableDeclarations;
friend class CSSVariableValues;
friend class EnumerateVariableReferencesData;
public:
/**
* Creates a new CSSVariableResolver that will output a set of resolved,
* computed variables into aOutput.
*/
explicit CSSVariableResolver(CSSVariableValues* aOutput)
: mOutput(aOutput)
#ifdef DEBUG
, mResolved(false)
#endif
{
MOZ_ASSERT(aOutput);
}
/**
* Resolves the set of inherited variables from aInherited and the
* set of specified variables from aSpecified. The resolved variables
* are written into mOutput.
*/
void Resolve(const CSSVariableValues* aInherited,
const CSSVariableDeclarations* aSpecified);
private:
struct Variable
{
Variable(const nsAString& aVariableName,
nsString aValue,
nsCSSTokenSerializationType aFirstToken,
nsCSSTokenSerializationType aLastToken,
bool aWasInherited)
: mVariableName(aVariableName)
, mValue(aValue)
, mFirstToken(aFirstToken)
, mLastToken(aLastToken)
, mWasInherited(aWasInherited)
, mResolved(false)
, mReferencesNonExistentVariable(false)
, mInStack(false)
, mIndex(0)
, mLowLink(0) { }
nsString mVariableName;
nsString mValue;
nsCSSTokenSerializationType mFirstToken;
nsCSSTokenSerializationType mLastToken;
// Whether this variable came from the set of inherited variables.
bool mWasInherited;
// Whether this variable has been resolved yet.
bool mResolved;
// Whether this variables includes any references to non-existent variables.
bool mReferencesNonExistentVariable;
// Bookkeeping for the cycle remover algorithm.
bool mInStack;
size_t mIndex;
size_t mLowLink;
};
/**
* Adds or modifies an existing entry in the set of variables to be resolved.
* This is intended to be called by the AddVariablesToResolver functions on
* the CSSVariableDeclarations and CSSVariableValues objects passed in to
* Resolve.
*
* @param aName The variable name (not including any "--" prefix that would
* be part of the custom property name) whose value is to be set.
* @param aValue The variable value.
* @param aFirstToken The type of token at the start of the variable value.
* @param aLastToken The type of token at the en of the variable value.
* @param aWasInherited Whether this variable came from the set of inherited
* variables.
*/
void Put(const nsAString& aVariableName,
nsString aValue,
nsCSSTokenSerializationType aFirstToken,
nsCSSTokenSerializationType aLastToken,
bool aWasInherited);
// Helper functions for Resolve.
void RemoveCycles(size_t aID);
void ResolveVariable(size_t aID);
// A mapping of variable names to an ID that indexes into mVariables
// and mReferences.
nsDataHashtable<nsStringHashKey, size_t> mVariableIDs;
// The set of variables.
nsTArray<Variable> mVariables;
// The list of variables that each variable references.
nsTArray<nsTArray<size_t> > mReferences;
// The next index to assign to a variable found during the cycle removing
// algorithm's traversal of the variable reference graph.
size_t mNextIndex;
// Stack of variable IDs that we push to as we traverse the variable reference
// graph while looking for cycles. Variable::mInStack reflects whether a
// given variable has its ID in mStack.
nsTArray<size_t> mStack;
// CSS parser to use for parsing property values with variable references.
nsCSSParser mParser;
// The object to output the resolved variables into.
CSSVariableValues* mOutput;
#ifdef DEBUG
// Whether Resolve has been called.
bool mResolved;
#endif
};
} // namespace mozilla
#endif

View file

@ -0,0 +1,147 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* computed CSS Variable values */
#include "CSSVariableValues.h"
#include "CSSVariableResolver.h"
namespace mozilla {
CSSVariableValues::CSSVariableValues()
{
MOZ_COUNT_CTOR(CSSVariableValues);
}
CSSVariableValues::CSSVariableValues(const CSSVariableValues& aOther)
{
MOZ_COUNT_CTOR(CSSVariableValues);
CopyVariablesFrom(aOther);
}
#ifdef DEBUG
CSSVariableValues::~CSSVariableValues()
{
MOZ_COUNT_DTOR(CSSVariableValues);
}
#endif
CSSVariableValues&
CSSVariableValues::operator=(const CSSVariableValues& aOther)
{
if (this == &aOther) {
return *this;
}
mVariableIDs.Clear();
mVariables.Clear();
CopyVariablesFrom(aOther);
return *this;
}
bool
CSSVariableValues::operator==(const CSSVariableValues& aOther) const
{
if (mVariables.Length() != aOther.mVariables.Length()) {
return false;
}
for (size_t thisIndex = 0; thisIndex < mVariables.Length(); ++thisIndex) {
size_t otherIndex;
if (!aOther.mVariableIDs.Get(mVariables[thisIndex].mVariableName,
&otherIndex)) {
return false;
}
const nsString& otherValue = aOther.mVariables[otherIndex].mValue;
if (!mVariables[thisIndex].mValue.Equals(otherValue)) {
return false;
}
}
return true;
}
size_t
CSSVariableValues::Count() const
{
return mVariables.Length();
}
bool
CSSVariableValues::Get(const nsAString& aName, nsString& aValue) const
{
size_t id;
if (!mVariableIDs.Get(aName, &id)) {
return false;
}
aValue = mVariables[id].mValue;
return true;
}
bool
CSSVariableValues::Get(const nsAString& aName,
nsString& aValue,
nsCSSTokenSerializationType& aFirstToken,
nsCSSTokenSerializationType& aLastToken) const
{
size_t id;
if (!mVariableIDs.Get(aName, &id)) {
return false;
}
aValue = mVariables[id].mValue;
aFirstToken = mVariables[id].mFirstToken;
aLastToken = mVariables[id].mLastToken;
return true;
}
void
CSSVariableValues::GetVariableAt(size_t aIndex, nsAString& aName) const
{
aName = mVariables[aIndex].mVariableName;
}
void
CSSVariableValues::Put(const nsAString& aName,
nsString aValue,
nsCSSTokenSerializationType aFirstToken,
nsCSSTokenSerializationType aLastToken)
{
size_t id;
if (mVariableIDs.Get(aName, &id)) {
mVariables[id].mValue = aValue;
mVariables[id].mFirstToken = aFirstToken;
mVariables[id].mLastToken = aLastToken;
} else {
id = mVariables.Length();
mVariableIDs.Put(aName, id);
mVariables.AppendElement(Variable(aName, aValue, aFirstToken, aLastToken));
}
}
void
CSSVariableValues::CopyVariablesFrom(const CSSVariableValues& aOther)
{
for (size_t i = 0, n = aOther.mVariables.Length(); i < n; i++) {
Put(aOther.mVariables[i].mVariableName,
aOther.mVariables[i].mValue,
aOther.mVariables[i].mFirstToken,
aOther.mVariables[i].mLastToken);
}
}
void
CSSVariableValues::AddVariablesToResolver(CSSVariableResolver* aResolver) const
{
for (size_t i = 0, n = mVariables.Length(); i < n; i++) {
aResolver->Put(mVariables[i].mVariableName,
mVariables[i].mValue,
mVariables[i].mFirstToken,
mVariables[i].mLastToken,
true);
}
}
} // namespace mozilla

View file

@ -0,0 +1,147 @@
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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/. */
/* computed CSS Variable values */
#ifndef mozilla_CSSVariableValues_h
#define mozilla_CSSVariableValues_h
#include "nsCSSScanner.h"
#include "nsDataHashtable.h"
#include "nsTArray.h"
namespace mozilla {
class CSSVariableResolver;
class CSSVariableValues
{
public:
CSSVariableValues();
CSSVariableValues(const CSSVariableValues& aOther);
#ifdef DEBUG
~CSSVariableValues();
#endif
CSSVariableValues& operator=(const CSSVariableValues& aOther);
bool operator==(const CSSVariableValues& aOther) const;
bool operator!=(const CSSVariableValues& aOther) const
{ return !(*this == aOther); }
/**
* Gets the value of a variable in this set of computed variables.
*
* @param aName The variable name (not including any "--" prefix that would
* be part of the custom property name).
* @param aValue Out parameter into which the value of the variable will
* be stored.
* @return Whether a variable with the given name was found. When false
* is returned, aValue will not be modified.
*/
bool Get(const nsAString& aName, nsString& aValue) const;
/**
* Gets the value of a variable in this set of computed variables, along
* with information on the types of tokens that appear at the start and
* end of the token stream.
*
* @param aName The variable name (not including any "--" prefix that would
* be part of the custom property name).
* @param aValue Out parameter into which the value of the variable will
* be stored.
* @param aFirstToken The type of token at the start of the variable value.
* @param aLastToken The type of token at the en of the variable value.
* @return Whether a variable with the given name was found. When false
* is returned, aValue, aFirstToken and aLastToken will not be modified.
*/
bool Get(const nsAString& aName,
nsString& aValue,
nsCSSTokenSerializationType& aFirstToken,
nsCSSTokenSerializationType& aLastToken) const;
/**
* Gets the name of the variable at the given index.
*
* Variables on this object are ordered, and that order is just determined
* based on the order that they are added to the object. A consistent
* ordering is required for CSSDeclaration objects in the DOM.
* CSSDeclarations expose property names as indexed properties, which need to
* be stable.
*
* @param aIndex The index of the variable to get.
* @param aName Out parameter into which the name of the variable will be
* stored.
*/
void GetVariableAt(size_t aIndex, nsAString& aName) const;
/**
* Gets the number of variables stored on this object.
*/
size_t Count() const;
/**
* Adds or modifies an existing entry in this set of variable values.
*
* @param aName The variable name (not including any "--" prefix that would
* be part of the custom property name) whose value is to be set.
* @param aValue The variable value.
* @param aFirstToken The type of token at the start of the variable value.
* @param aLastToken The type of token at the en of the variable value.
*/
void Put(const nsAString& aName,
nsString aValue,
nsCSSTokenSerializationType aFirstToken,
nsCSSTokenSerializationType aLastToken);
/**
* Copies the variables from this object into aResolver, marking them as
* computed, inherited values.
*/
void AddVariablesToResolver(CSSVariableResolver* aResolver) const;
private:
struct Variable
{
Variable()
: mFirstToken(eCSSTokenSerialization_Nothing)
, mLastToken(eCSSTokenSerialization_Nothing)
{}
Variable(const nsAString& aVariableName,
nsString aValue,
nsCSSTokenSerializationType aFirstToken,
nsCSSTokenSerializationType aLastToken)
: mVariableName(aVariableName)
, mValue(aValue)
, mFirstToken(aFirstToken)
, mLastToken(aLastToken)
{}
nsString mVariableName;
nsString mValue;
nsCSSTokenSerializationType mFirstToken;
nsCSSTokenSerializationType mLastToken;
};
/**
* Adds all the variables from aOther into this object.
*/
void CopyVariablesFrom(const CSSVariableValues& aOther);
/**
* Map of variable names to IDs. Variable IDs are indexes into
* mVariables.
*/
nsDataHashtable<nsStringHashKey, size_t> mVariableIDs;
/**
* Array of variables, indexed by variable ID.
*/
nsTArray<Variable> mVariables;
};
} // namespace mozilla
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,192 @@
/* -*- 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 mozilla_CounterStyleManager_h_
#define mozilla_CounterStyleManager_h_
#include "nsStringFwd.h"
#include "nsRefPtrHashtable.h"
#include "nsHashKeys.h"
#include "nsStyleConsts.h"
#include "mozilla/Attributes.h"
#include "nsCSSValue.h"
class nsPresContext;
namespace mozilla {
class WritingMode;
typedef int32_t CounterValue;
class CounterStyleManager;
class AnonymousCounterStyle;
struct NegativeType;
struct PadType;
class CounterStyle
{
protected:
explicit constexpr CounterStyle(int32_t aStyle)
: mStyle(aStyle)
{
}
private:
CounterStyle(const CounterStyle& aOther) = delete;
void operator=(const CounterStyle& other) = delete;
public:
int32_t GetStyle() const { return mStyle; }
bool IsNone() const { return mStyle == NS_STYLE_LIST_STYLE_NONE; }
bool IsCustomStyle() const { return mStyle == NS_STYLE_LIST_STYLE_CUSTOM; }
// A style is dependent if it depends on the counter style manager.
// Custom styles are certainly dependent. In addition, some builtin
// styles are dependent for fallback.
bool IsDependentStyle() const;
virtual void GetStyleName(nsSubstring& aResult) = 0;
virtual void GetPrefix(nsSubstring& aResult) = 0;
virtual void GetSuffix(nsSubstring& aResult) = 0;
void GetCounterText(CounterValue aOrdinal,
WritingMode aWritingMode,
nsSubstring& aResult,
bool& aIsRTL);
virtual void GetSpokenCounterText(CounterValue aOrdinal,
WritingMode aWritingMode,
nsSubstring& aResult,
bool& aIsBullet);
// XXX This method could be removed once ::-moz-list-bullet and
// ::-moz-list-number are completely merged into ::marker.
virtual bool IsBullet() = 0;
virtual void GetNegative(NegativeType& aResult) = 0;
/**
* This method returns whether an ordinal is in the range of this
* counter style. Note that, it is possible that an ordinal in range
* is rejected by the generating algorithm.
*/
virtual bool IsOrdinalInRange(CounterValue aOrdinal) = 0;
/**
* This method returns whether an ordinal is in the default range of
* this counter style. This is the effective range when no 'range'
* descriptor is specified.
*/
virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) = 0;
virtual void GetPad(PadType& aResult) = 0;
virtual CounterStyle* GetFallback() = 0;
virtual uint8_t GetSpeakAs() = 0;
virtual bool UseNegativeSign() = 0;
virtual void CallFallbackStyle(CounterValue aOrdinal,
WritingMode aWritingMode,
nsSubstring& aResult,
bool& aIsRTL);
virtual bool GetInitialCounterText(CounterValue aOrdinal,
WritingMode aWritingMode,
nsSubstring& aResult,
bool& aIsRTL) = 0;
virtual AnonymousCounterStyle* AsAnonymous() { return nullptr; }
NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
protected:
int32_t mStyle;
};
class AnonymousCounterStyle final : public CounterStyle
{
public:
explicit AnonymousCounterStyle(const nsSubstring& aContent);
explicit AnonymousCounterStyle(const nsCSSValue::Array* aValue);
virtual void GetStyleName(nsAString& aResult) override;
virtual void GetPrefix(nsAString& aResult) override;
virtual void GetSuffix(nsAString& aResult) override;
virtual bool IsBullet() override;
virtual void GetNegative(NegativeType& aResult) override;
virtual bool IsOrdinalInRange(CounterValue aOrdinal) override;
virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) override;
virtual void GetPad(PadType& aResult) override;
virtual CounterStyle* GetFallback() override;
virtual uint8_t GetSpeakAs() override;
virtual bool UseNegativeSign() override;
virtual bool GetInitialCounterText(CounterValue aOrdinal,
WritingMode aWritingMode,
nsSubstring& aResult,
bool& aIsRTL) override;
virtual AnonymousCounterStyle* AsAnonymous() override { return this; }
bool IsSingleString() const { return mSingleString; }
uint8_t GetSystem() const { return mSystem; }
const nsTArray<nsString>& GetSymbols() const { return mSymbols; }
NS_INLINE_DECL_REFCOUNTING(AnonymousCounterStyle, override)
private:
~AnonymousCounterStyle() {}
bool mSingleString;
uint8_t mSystem;
nsTArray<nsString> mSymbols;
};
class CounterStyleManager final
{
private:
~CounterStyleManager();
public:
explicit CounterStyleManager(nsPresContext* aPresContext);
static void InitializeBuiltinCounterStyles();
void Disconnect();
bool IsInitial() const
{
// only 'none' and 'decimal'
return mCacheTable.Count() == 2;
}
CounterStyle* BuildCounterStyle(const nsSubstring& aName);
static CounterStyle* GetBuiltinStyle(int32_t aStyle);
static CounterStyle* GetNoneStyle()
{
return GetBuiltinStyle(NS_STYLE_LIST_STYLE_NONE);
}
static CounterStyle* GetDecimalStyle()
{
return GetBuiltinStyle(NS_STYLE_LIST_STYLE_DECIMAL);
}
// This method will scan all existing counter styles generated by this
// manager, and remove or mark data dirty accordingly. It returns true
// if any counter style is changed, false elsewise. This method should
// be called when any counter style may be affected.
bool NotifyRuleChanged();
nsPresContext* PresContext() const { return mPresContext; }
NS_INLINE_DECL_REFCOUNTING(CounterStyleManager)
private:
nsPresContext* mPresContext;
nsRefPtrHashtable<nsStringHashKey, CounterStyle> mCacheTable;
};
} // namespace mozilla
#endif /* !defined(mozilla_CounterStyleManager_h_) */

1988
layout/style/Declaration.cpp Normal file

File diff suppressed because it is too large Load diff

417
layout/style/Declaration.h Normal file
View file

@ -0,0 +1,417 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/*
* representation of a declaration block (or style attribute) in a CSS
* stylesheet
*/
#ifndef mozilla_css_Declaration_h
#define mozilla_css_Declaration_h
// This header is in EXPORTS because it's used in several places in content/,
// but it's not really a public interface.
#ifndef MOZILLA_INTERNAL_API
#error "This file should only be included within libxul"
#endif
#include "mozilla/Attributes.h"
#include "mozilla/DeclarationBlock.h"
#include "mozilla/MemoryReporting.h"
#include "CSSVariableDeclarations.h"
#include "nsCSSDataBlock.h"
#include "nsCSSPropertyID.h"
#include "nsCSSProps.h"
#include "nsIStyleRule.h"
#include "nsStringFwd.h"
#include "nsTArray.h"
#include <stdio.h>
// feec07b8-3fe6-491e-90d5-cc93f853e048
#define NS_CSS_DECLARATION_IMPL_CID \
{ 0xfeec07b8, 0x3fe6, 0x491e, \
{ 0x90, 0xd5, 0xcc, 0x93, 0xf8, 0x53, 0xe0, 0x48 } }
class nsHTMLCSSStyleSheet;
namespace mozilla {
namespace css {
class Rule;
class Declaration;
/**
* ImportantStyleData is the implementation of nsIStyleRule (a source of
* style data) representing the style data coming from !important rules;
* the !important declarations need a separate nsIStyleRule object since
* they fit at a different point in the cascade.
*
* ImportantStyleData is allocated only as part of a Declaration object.
*/
class ImportantStyleData final : public nsIStyleRule
{
public:
NS_DECL_ISUPPORTS
inline ::mozilla::css::Declaration* Declaration();
// nsIStyleRule interface
virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
virtual bool MightMapInheritedStyleData() override;
virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
nsCSSValue* aValue) override;
#ifdef DEBUG
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
#endif
private:
ImportantStyleData() {}
~ImportantStyleData() {}
friend class ::mozilla::css::Declaration;
};
// Declaration objects have unusual lifetime rules. Every declaration
// begins life in an invalid state which ends when InitializeEmpty or
// CompressFrom is called upon it. After that, it can be attached to
// exactly one style rule, and will be destroyed when that style rule
// is destroyed. A declaration becomes immutable (via a SetImmutable
// call) when it is matched (put in the rule tree); after that, it must
// be copied before it can be modified, which is taken care of by
// |EnsureMutable|.
class Declaration final : public DeclarationBlock
, public nsIStyleRule
{
public:
/**
* Construct an |Declaration| that is in an invalid state (null
* |mData|) and cannot be used until its |CompressFrom| method or
* |InitializeEmpty| method is called.
*/
Declaration() : DeclarationBlock(StyleBackendType::Gecko) {}
Declaration(const Declaration& aCopy);
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_DECLARATION_IMPL_CID)
NS_DECL_ISUPPORTS
private:
~Declaration();
public:
// nsIStyleRule implementation
virtual void MapRuleInfoInto(nsRuleData *aRuleData) override;
virtual bool MightMapInheritedStyleData() override;
virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
nsCSSValue* aValue) override;
#ifdef DEBUG
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
#endif
/**
* |ValueAppended| must be called to maintain this declaration's
* |mOrder| whenever a property is parsed into an expanded data block
* for this declaration. aProperty must not be a shorthand.
*/
void ValueAppended(nsCSSPropertyID aProperty);
void GetPropertyValue(const nsAString& aProperty, nsAString& aValue) const;
void GetPropertyValueByID(nsCSSPropertyID aPropID, nsAString& aValue) const;
void GetAuthoredPropertyValue(const nsAString& aProperty,
nsAString& aValue) const;
bool GetPropertyIsImportant(const nsAString& aProperty) const;
void RemoveProperty(const nsAString& aProperty);
void RemovePropertyByID(nsCSSPropertyID aProperty);
bool HasProperty(nsCSSPropertyID aProperty) const;
bool HasImportantData() const {
return mImportantData || mImportantVariables;
}
/**
* Adds a custom property declaration to this object.
*
* @param aName The variable name (i.e., without the "--" prefix).
* @param aType The type of value the variable has.
* @param aValue The value of the variable, if aType is
* CSSVariableDeclarations::eTokenStream.
* @param aIsImportant Whether the declaration is !important.
* @param aOverrideImportant When aIsImportant is false, whether an
* existing !important declaration will be overridden.
*/
void AddVariable(const nsAString& aName,
CSSVariableDeclarations::Type aType,
const nsString& aValue,
bool aIsImportant,
bool aOverrideImportant);
/**
* Removes a custom property declaration from this object.
*
* @param aName The variable name (i.e., without the "--" prefix).
*/
void RemoveVariable(const nsAString& aName);
/**
* Gets the string value for a custom property declaration of a variable
* with a given name.
*
* @param aName The variable name (i.e., without the "--" prefix).
* @param aValue Out parameter into which the variable's value will be
* stored. If the value is 'initial' or 'inherit', that exact string
* will be stored in aValue.
*/
void GetVariableValue(const nsAString& aName, nsAString& aValue) const;
/**
* Returns whether the custom property declaration for a variable with
* the given name was !important.
*/
bool GetVariableIsImportant(const nsAString& aName) const;
uint32_t Count() const {
return mOrder.Length();
}
// Returns whether we actually had a property at aIndex
bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const;
void ToString(nsAString& aString) const;
nsCSSCompressedDataBlock* GetNormalBlock() const { return mData; }
nsCSSCompressedDataBlock* GetImportantBlock() const { return mImportantData; }
void AssertNotExpanded() const {
MOZ_ASSERT(mData, "should only be called when not expanded");
}
/**
* Initialize this declaration as holding no data. Cannot fail.
*/
void InitializeEmpty();
/**
* Transfer all of the state from |aExpandedData| into this declaration.
* After calling, |aExpandedData| should be in its initial state.
* Callers must make sure mOrder is updated as necessary.
*/
void CompressFrom(nsCSSExpandedDataBlock *aExpandedData) {
MOZ_ASSERT(!mData, "oops");
MOZ_ASSERT(!mImportantData, "oops");
aExpandedData->Compress(getter_Transfers(mData),
getter_Transfers(mImportantData),
mOrder);
aExpandedData->AssertInitialState();
}
/**
* Transfer all of the state from this declaration into
* |aExpandedData| and put this declaration temporarily into an
* invalid state (ended by |CompressFrom| or |InitializeEmpty|) that
* should last only during parsing. During this time only
* |ValueAppended| should be called.
*/
void ExpandTo(nsCSSExpandedDataBlock *aExpandedData) {
AssertMutable();
aExpandedData->AssertInitialState();
MOZ_ASSERT(mData, "oops");
aExpandedData->Expand(mData.forget(), mImportantData.forget());
}
void MapImportantRuleInfoInto(nsRuleData *aRuleData) const {
AssertNotExpanded();
MOZ_ASSERT(mImportantData || mImportantVariables,
"must have important data or variables");
if (mImportantData) {
mImportantData->MapRuleInfoInto(aRuleData);
}
if (mImportantVariables) {
mImportantVariables->MapRuleInfoInto(aRuleData);
}
}
bool MapsImportantInheritedStyleData() const;
/**
* Attempt to replace the value for |aProperty| stored in this
* declaration with the matching value from |aFromBlock|.
* This method may only be called on a mutable declaration.
* It will fail (returning false) if |aProperty| is shorthand,
* is not already in this declaration, or does not have the indicated
* importance level. If it returns true, it erases the value in
* |aFromBlock|. |aChanged| is set to true if the declaration
* changed as a result of the call, and to false otherwise.
*/
bool TryReplaceValue(nsCSSPropertyID aProperty, bool aIsImportant,
nsCSSExpandedDataBlock& aFromBlock,
bool* aChanged)
{
AssertMutable();
AssertNotExpanded();
if (nsCSSProps::IsShorthand(aProperty)) {
*aChanged = false;
return false;
}
nsCSSCompressedDataBlock *block = aIsImportant ? mImportantData : mData;
// mImportantData might be null
if (!block) {
*aChanged = false;
return false;
}
#ifdef DEBUG
{
nsCSSCompressedDataBlock *other = aIsImportant ? mData : mImportantData;
MOZ_ASSERT(!other || !other->ValueFor(aProperty) ||
!block->ValueFor(aProperty),
"Property both important and not?");
}
#endif
return block->TryReplaceValue(aProperty, aFromBlock, aChanged);
}
bool HasNonImportantValueFor(nsCSSPropertyID aProperty) const {
MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty), "must be longhand");
return !!mData->ValueFor(aProperty);
}
/**
* Clear the data, in preparation for its replacement with entirely
* new data by a call to |CompressFrom|.
*/
void ClearData() {
AssertMutable();
mData = nullptr;
mImportantData = nullptr;
mVariables = nullptr;
mImportantVariables = nullptr;
mOrder.Clear();
mVariableOrder.Clear();
}
ImportantStyleData* GetImportantStyleData() {
if (HasImportantData()) {
return &mImportantStyleData;
}
return nullptr;
}
private:
Declaration& operator=(const Declaration& aCopy) = delete;
bool operator==(const Declaration& aCopy) const = delete;
void GetPropertyValueInternal(nsCSSPropertyID aProperty, nsAString& aValue,
nsCSSValue::Serialization aValueSerialization)
const;
bool GetPropertyIsImportantByID(nsCSSPropertyID aProperty) const;
static void AppendImportanceToString(bool aIsImportant, nsAString& aString);
// return whether there was a value in |aValue| (i.e., it had a non-null unit)
bool AppendValueToString(nsCSSPropertyID aProperty, nsAString& aResult) const;
bool AppendValueToString(nsCSSPropertyID aProperty, nsAString& aResult,
nsCSSValue::Serialization aValueSerialization) const;
// Helper for ToString with strange semantics regarding aValue.
void AppendPropertyAndValueToString(nsCSSPropertyID aProperty,
nsAutoString& aValue,
nsAString& aResult) const;
// helper for ToString that serializes a custom property declaration for
// a variable with the specified name
void AppendVariableAndValueToString(const nsAString& aName,
nsAString& aResult) const;
void GetImageLayerValue(nsCSSCompressedDataBlock *data,
nsAString& aValue,
nsCSSValue::Serialization aSerialization,
const nsCSSPropertyID aTable[]) const;
void GetImageLayerPositionValue(nsCSSCompressedDataBlock *data,
nsAString& aValue,
nsCSSValue::Serialization aSerialization,
const nsCSSPropertyID aTable[]) const;
public:
/**
* Returns the property at the given index in the ordered list of
* declarations. For custom properties, eCSSPropertyExtra_variable
* is returned.
*/
nsCSSPropertyID GetPropertyAt(uint32_t aIndex) const {
uint32_t value = mOrder[aIndex];
if (value >= eCSSProperty_COUNT) {
return eCSSPropertyExtra_variable;
}
return nsCSSPropertyID(value);
}
/**
* Gets the name of the custom property at the given index in the ordered
* list of declarations.
*/
void GetCustomPropertyNameAt(uint32_t aIndex, nsAString& aResult) const {
MOZ_ASSERT(mOrder[aIndex] >= eCSSProperty_COUNT);
uint32_t variableIndex = mOrder[aIndex] - eCSSProperty_COUNT;
aResult.Truncate();
aResult.AppendLiteral("--");
aResult.Append(mVariableOrder[variableIndex]);
}
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
private:
// The order of properties in this declaration. Longhand properties are
// represented by their nsCSSPropertyID value, and each custom property (--*)
// is represented by a value that begins at eCSSProperty_COUNT.
//
// Subtracting eCSSProperty_COUNT from those values that represent custom
// properties results in an index into mVariableOrder, which identifies the
// specific variable the custom property declaration is for.
AutoTArray<uint32_t, 8> mOrder;
// variable names of custom properties found in mOrder
nsTArray<nsString> mVariableOrder;
// never null, except while expanded, or before the first call to
// InitializeEmpty or CompressFrom.
nsAutoPtr<nsCSSCompressedDataBlock> mData;
// may be null
nsAutoPtr<nsCSSCompressedDataBlock> mImportantData;
// may be null
nsAutoPtr<CSSVariableDeclarations> mVariables;
// may be null
nsAutoPtr<CSSVariableDeclarations> mImportantVariables;
friend class ImportantStyleData;
ImportantStyleData mImportantStyleData;
};
inline ::mozilla::css::Declaration*
ImportantStyleData::Declaration()
{
union {
char* ch; /* for pointer arithmetic */
::mozilla::css::Declaration* declaration;
ImportantStyleData* importantData;
} u;
u.importantData = this;
u.ch -= offsetof(::mozilla::css::Declaration, mImportantStyleData);
return u.declaration;
}
NS_DEFINE_STATIC_IID_ACCESSOR(Declaration, NS_CSS_DECLARATION_IMPL_CID)
} // namespace css
} // namespace mozilla
#endif /* mozilla_css_Declaration_h */

View file

@ -0,0 +1,146 @@
/* -*- 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/. */
/*
* representation of a declaration block in a CSS stylesheet, or of
* a style attribute
*/
#ifndef mozilla_DeclarationBlock_h
#define mozilla_DeclarationBlock_h
#include "mozilla/ServoUtils.h"
#include "mozilla/StyleBackendType.h"
#include "nsCSSPropertyID.h"
class nsHTMLCSSStyleSheet;
namespace mozilla {
class ServoDeclarationBlock;
namespace css {
class Declaration;
class Rule;
} // namespace css
class DeclarationBlock
{
protected:
explicit DeclarationBlock(StyleBackendType aType)
: mImmutable(false), mType(aType) { mContainer.mRaw = 0; }
DeclarationBlock(const DeclarationBlock& aCopy)
: DeclarationBlock(aCopy.mType) {}
public:
MOZ_DECL_STYLO_METHODS(css::Declaration, ServoDeclarationBlock)
inline MozExternalRefCountType AddRef();
inline MozExternalRefCountType Release();
inline already_AddRefed<DeclarationBlock> Clone() const;
/**
* Return whether |this| may be modified.
*/
bool IsMutable() const {
return !mImmutable;
}
/**
* Crash if |this| cannot be modified.
*/
void AssertMutable() const {
MOZ_ASSERT(IsMutable(), "someone forgot to call EnsureMutable");
}
/**
* Mark this declaration as unmodifiable. It's 'const' so it can
* be called from ToString.
*/
void SetImmutable() const { mImmutable = true; }
/**
* Copy |this|, if necessary to ensure that it can be modified.
*/
inline already_AddRefed<DeclarationBlock> EnsureMutable();
void SetOwningRule(css::Rule* aRule) {
MOZ_ASSERT(!mContainer.mOwningRule || !aRule,
"should never overwrite one rule with another");
mContainer.mOwningRule = aRule;
}
css::Rule* GetOwningRule() const {
if (mContainer.mRaw & 0x1) {
return nullptr;
}
return mContainer.mOwningRule;
}
void SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet* aHTMLCSSStyleSheet) {
MOZ_ASSERT(!mContainer.mHTMLCSSStyleSheet || !aHTMLCSSStyleSheet,
"should never overwrite one sheet with another");
mContainer.mHTMLCSSStyleSheet = aHTMLCSSStyleSheet;
if (aHTMLCSSStyleSheet) {
mContainer.mRaw |= uintptr_t(1);
}
}
nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() const {
if (!(mContainer.mRaw & 0x1)) {
return nullptr;
}
auto c = mContainer;
c.mRaw &= ~uintptr_t(1);
return c.mHTMLCSSStyleSheet;
}
inline void ToString(nsAString& aString) const;
inline uint32_t Count() const;
inline bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const;
inline void GetPropertyValue(const nsAString& aProperty,
nsAString& aValue) const;
inline void GetPropertyValueByID(nsCSSPropertyID aPropID,
nsAString& aValue) const;
inline void GetAuthoredPropertyValue(const nsAString& aProperty,
nsAString& aValue) const;
inline bool GetPropertyIsImportant(const nsAString& aProperty) const;
inline void RemoveProperty(const nsAString& aProperty);
inline void RemovePropertyByID(nsCSSPropertyID aProperty);
private:
union {
// We only ever have one of these since we have an
// nsHTMLCSSStyleSheet only for style attributes, and style
// attributes never have an owning rule.
// It's an nsHTMLCSSStyleSheet if the low bit is set.
uintptr_t mRaw;
// The style rule that owns this declaration. May be null.
css::Rule* mOwningRule;
// The nsHTMLCSSStyleSheet that is responsible for this declaration.
// Only non-null for style attributes.
nsHTMLCSSStyleSheet* mHTMLCSSStyleSheet;
} mContainer;
// set when declaration put in the rule tree;
// also by ToString (hence the 'mutable').
mutable bool mImmutable;
const StyleBackendType mType;
};
} // namespace mozilla
#endif // mozilla_DeclarationBlock_h

View file

@ -0,0 +1,114 @@
/* -*- 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 mozilla_DeclarationBlockInlines_h
#define mozilla_DeclarationBlockInlines_h
#include "mozilla/css/Declaration.h"
#include "mozilla/ServoDeclarationBlock.h"
namespace mozilla {
MOZ_DEFINE_STYLO_METHODS(DeclarationBlock, css::Declaration, ServoDeclarationBlock)
MozExternalRefCountType
DeclarationBlock::AddRef()
{
MOZ_STYLO_FORWARD(AddRef, ())
}
MozExternalRefCountType
DeclarationBlock::Release()
{
MOZ_STYLO_FORWARD(Release, ())
}
already_AddRefed<DeclarationBlock>
DeclarationBlock::Clone() const
{
RefPtr<DeclarationBlock> result;
if (IsGecko()) {
result = new css::Declaration(*AsGecko());
} else {
result = new ServoDeclarationBlock(*AsServo());
}
return result.forget();
}
already_AddRefed<DeclarationBlock>
DeclarationBlock::EnsureMutable()
{
#ifdef DEBUG
if (IsGecko()) {
AsGecko()->AssertNotExpanded();
}
#endif
if (!IsMutable()) {
return Clone();
}
return do_AddRef(this);
}
void
DeclarationBlock::ToString(nsAString& aString) const
{
MOZ_STYLO_FORWARD(ToString, (aString))
}
uint32_t
DeclarationBlock::Count() const
{
MOZ_STYLO_FORWARD(Count, ())
}
bool
DeclarationBlock::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const
{
MOZ_STYLO_FORWARD(GetNthProperty, (aIndex, aReturn))
}
void
DeclarationBlock::GetPropertyValue(const nsAString& aProperty,
nsAString& aValue) const
{
MOZ_STYLO_FORWARD(GetPropertyValue, (aProperty, aValue))
}
void
DeclarationBlock::GetPropertyValueByID(nsCSSPropertyID aPropID,
nsAString& aValue) const
{
MOZ_STYLO_FORWARD(GetPropertyValueByID, (aPropID, aValue))
}
void
DeclarationBlock::GetAuthoredPropertyValue(const nsAString& aProperty,
nsAString& aValue) const
{
MOZ_STYLO_FORWARD(GetAuthoredPropertyValue, (aProperty, aValue))
}
bool
DeclarationBlock::GetPropertyIsImportant(const nsAString& aProperty) const
{
MOZ_STYLO_FORWARD(GetPropertyIsImportant, (aProperty))
}
void
DeclarationBlock::RemoveProperty(const nsAString& aProperty)
{
MOZ_STYLO_FORWARD(RemoveProperty, (aProperty))
}
void
DeclarationBlock::RemovePropertyByID(nsCSSPropertyID aProperty)
{
MOZ_STYLO_FORWARD(RemovePropertyByID, (aProperty))
}
} // namespace mozilla
#endif // mozilla_DeclarationBlockInlines_h

View file

@ -0,0 +1,382 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* diagnostic reporting for CSS style sheet parser */
#include "mozilla/css/ErrorReporter.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/css/Loader.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "nsCSSScanner.h"
#include "nsIConsoleService.h"
#include "nsIDocument.h"
#include "nsIFactory.h"
#include "nsIScriptError.h"
#include "nsIStringBundle.h"
#include "nsServiceManagerUtils.h"
#include "nsStyleUtil.h"
#include "nsThreadUtils.h"
#ifdef CSS_REPORT_PARSE_ERRORS
using namespace mozilla;
namespace {
class ShortTermURISpecCache : public Runnable {
public:
ShortTermURISpecCache() : mPending(false) {}
nsString const& GetSpec(nsIURI* aURI) {
if (mURI != aURI) {
mURI = aURI;
nsAutoCString cSpec;
nsresult rv = mURI->GetSpec(cSpec);
if (NS_FAILED(rv)) {
cSpec.AssignLiteral("[nsIURI::GetSpec failed]");
}
CopyUTF8toUTF16(cSpec, mSpec);
}
return mSpec;
}
bool IsInUse() const { return mURI != nullptr; }
bool IsPending() const { return mPending; }
void SetPending() { mPending = true; }
// When invoked as a runnable, zap the cache.
NS_IMETHOD Run() override {
mURI = nullptr;
mSpec.Truncate();
mPending = false;
return NS_OK;
}
private:
nsCOMPtr<nsIURI> mURI;
nsString mSpec;
bool mPending;
};
} // namespace
static bool sReportErrors;
static nsIConsoleService *sConsoleService;
static nsIFactory *sScriptErrorFactory;
static nsIStringBundle *sStringBundle;
static ShortTermURISpecCache *sSpecCache;
#define CSS_ERRORS_PREF "layout.css.report_errors"
static bool
InitGlobals()
{
MOZ_ASSERT(!sConsoleService && !sScriptErrorFactory && !sStringBundle,
"should not have been called");
if (NS_FAILED(Preferences::AddBoolVarCache(&sReportErrors, CSS_ERRORS_PREF,
true))) {
return false;
}
nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
if (!cs) {
return false;
}
nsCOMPtr<nsIFactory> sf = do_GetClassObject(NS_SCRIPTERROR_CONTRACTID);
if (!sf) {
return false;
}
nsCOMPtr<nsIStringBundleService> sbs = services::GetStringBundleService();
if (!sbs) {
return false;
}
nsCOMPtr<nsIStringBundle> sb;
nsresult rv = sbs->CreateBundle("chrome://global/locale/css.properties",
getter_AddRefs(sb));
if (NS_FAILED(rv) || !sb) {
return false;
}
cs.forget(&sConsoleService);
sf.forget(&sScriptErrorFactory);
sb.forget(&sStringBundle);
return true;
}
static inline bool
ShouldReportErrors()
{
if (!sConsoleService) {
if (!InitGlobals()) {
return false;
}
}
return sReportErrors;
}
namespace mozilla {
namespace css {
/* static */ void
ErrorReporter::ReleaseGlobals()
{
NS_IF_RELEASE(sConsoleService);
NS_IF_RELEASE(sScriptErrorFactory);
NS_IF_RELEASE(sStringBundle);
NS_IF_RELEASE(sSpecCache);
}
ErrorReporter::ErrorReporter(const nsCSSScanner& aScanner,
const CSSStyleSheet* aSheet,
const Loader* aLoader,
nsIURI* aURI)
: mScanner(&aScanner), mSheet(aSheet), mLoader(aLoader), mURI(aURI),
mInnerWindowID(0), mErrorLineNumber(0), mPrevErrorLineNumber(0),
mErrorColNumber(0)
{
}
ErrorReporter::~ErrorReporter()
{
// Schedule deferred cleanup for cached data. We want to strike a
// balance between performance and memory usage, so we only allow
// short-term caching.
if (sSpecCache && sSpecCache->IsInUse() && !sSpecCache->IsPending()) {
if (NS_FAILED(NS_DispatchToCurrentThread(sSpecCache))) {
// Peform the "deferred" cleanup immediately if the dispatch fails.
sSpecCache->Run();
} else {
sSpecCache->SetPending();
}
}
}
void
ErrorReporter::OutputError()
{
if (mError.IsEmpty()) {
return;
}
if (!ShouldReportErrors()) {
ClearError();
return;
}
if (mInnerWindowID == 0 && (mSheet || mLoader)) {
if (mSheet) {
mInnerWindowID = mSheet->FindOwningWindowInnerID();
}
if (mInnerWindowID == 0 && mLoader) {
nsIDocument* doc = mLoader->GetDocument();
if (doc) {
mInnerWindowID = doc->InnerWindowID();
}
}
// don't attempt this again, even if we failed
mSheet = nullptr;
mLoader = nullptr;
}
if (mFileName.IsEmpty()) {
if (mURI) {
if (!sSpecCache) {
sSpecCache = new ShortTermURISpecCache;
NS_ADDREF(sSpecCache);
}
mFileName = sSpecCache->GetSpec(mURI);
mURI = nullptr;
} else {
mFileName.AssignLiteral("from DOM");
}
}
nsresult rv;
nsCOMPtr<nsIScriptError> errorObject =
do_CreateInstance(sScriptErrorFactory, &rv);
if (NS_SUCCEEDED(rv)) {
rv = errorObject->InitWithWindowID(mError,
mFileName,
mErrorLine,
mErrorLineNumber,
mErrorColNumber,
nsIScriptError::warningFlag,
"CSS Parser",
mInnerWindowID);
if (NS_SUCCEEDED(rv)) {
sConsoleService->LogMessage(errorObject);
}
}
ClearError();
}
void
ErrorReporter::OutputError(uint32_t aLineNumber, uint32_t aLineOffset)
{
mErrorLineNumber = aLineNumber;
mErrorColNumber = aLineOffset;
OutputError();
}
void
ErrorReporter::ClearError()
{
mError.Truncate();
}
void
ErrorReporter::AddToError(const nsString &aErrorText)
{
if (!ShouldReportErrors()) return;
if (mError.IsEmpty()) {
mError = aErrorText;
mErrorLineNumber = mScanner->GetLineNumber();
mErrorColNumber = mScanner->GetColumnNumber();
// Retrieve the error line once per line, and reuse the same nsString
// for all errors on that line. That causes the text of the line to
// be shared among all the nsIScriptError objects.
if (mErrorLine.IsEmpty() || mErrorLineNumber != mPrevErrorLineNumber) {
// Be careful here: the error line might be really long and OOM
// when we try to make a copy here. If so, just leave it empty.
if (!mErrorLine.Assign(mScanner->GetCurrentLine(), fallible)) {
mErrorLine.Truncate();
}
mPrevErrorLineNumber = mErrorLineNumber;
}
} else {
mError.AppendLiteral(" ");
mError.Append(aErrorText);
}
}
void
ErrorReporter::ReportUnexpected(const char *aMessage)
{
if (!ShouldReportErrors()) return;
nsAutoString str;
sStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
getter_Copies(str));
AddToError(str);
}
void
ErrorReporter::ReportUnexpected(const char *aMessage,
const nsString &aParam)
{
if (!ShouldReportErrors()) return;
nsAutoString qparam;
nsStyleUtil::AppendEscapedCSSIdent(aParam, qparam);
const char16_t *params[1] = { qparam.get() };
nsAutoString str;
sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
params, ArrayLength(params),
getter_Copies(str));
AddToError(str);
}
void
ErrorReporter::ReportUnexpected(const char *aMessage,
const nsCSSToken &aToken)
{
if (!ShouldReportErrors()) return;
nsAutoString tokenString;
aToken.AppendToString(tokenString);
const char16_t *params[1] = { tokenString.get() };
nsAutoString str;
sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
params, ArrayLength(params),
getter_Copies(str));
AddToError(str);
}
void
ErrorReporter::ReportUnexpected(const char *aMessage,
const nsCSSToken &aToken,
char16_t aChar)
{
if (!ShouldReportErrors()) return;
nsAutoString tokenString;
aToken.AppendToString(tokenString);
const char16_t charStr[2] = { aChar, 0 };
const char16_t *params[2] = { tokenString.get(), charStr };
nsAutoString str;
sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
params, ArrayLength(params),
getter_Copies(str));
AddToError(str);
}
void
ErrorReporter::ReportUnexpected(const char *aMessage,
const nsString &aParam,
const nsString &aValue)
{
if (!ShouldReportErrors()) return;
nsAutoString qparam;
nsStyleUtil::AppendEscapedCSSIdent(aParam, qparam);
const char16_t *params[2] = { qparam.get(), aValue.get() };
nsAutoString str;
sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
params, ArrayLength(params),
getter_Copies(str));
AddToError(str);
}
void
ErrorReporter::ReportUnexpectedEOF(const char *aMessage)
{
if (!ShouldReportErrors()) return;
nsAutoString innerStr;
sStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(),
getter_Copies(innerStr));
const char16_t *params[1] = { innerStr.get() };
nsAutoString str;
sStringBundle->FormatStringFromName(u"PEUnexpEOF2",
params, ArrayLength(params),
getter_Copies(str));
AddToError(str);
}
void
ErrorReporter::ReportUnexpectedEOF(char16_t aExpected)
{
if (!ShouldReportErrors()) return;
const char16_t expectedStr[] = {
char16_t('\''), aExpected, char16_t('\''), char16_t(0)
};
const char16_t *params[1] = { expectedStr };
nsAutoString str;
sStringBundle->FormatStringFromName(u"PEUnexpEOF2",
params, ArrayLength(params),
getter_Copies(str));
AddToError(str);
}
} // namespace css
} // namespace mozilla
#endif

View file

@ -0,0 +1,113 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* diagnostic reporting for CSS style sheet parser */
#ifndef mozilla_css_ErrorReporter_h_
#define mozilla_css_ErrorReporter_h_
// XXX turn this off for minimo builds
#define CSS_REPORT_PARSE_ERRORS
#include "nsString.h"
struct nsCSSToken;
class nsCSSScanner;
class nsIURI;
namespace mozilla {
class CSSStyleSheet;
namespace css {
class Loader;
// If CSS_REPORT_PARSE_ERRORS is not defined, all of this class's
// methods become inline stubs.
class MOZ_STACK_CLASS ErrorReporter {
public:
ErrorReporter(const nsCSSScanner &aScanner,
const CSSStyleSheet *aSheet,
const Loader *aLoader,
nsIURI *aURI);
~ErrorReporter();
static void ReleaseGlobals();
void OutputError();
void OutputError(uint32_t aLineNumber, uint32_t aLineOffset);
void ClearError();
// In all overloads of ReportUnexpected, aMessage is a stringbundle
// name, which will be processed as a format string with the
// indicated number of parameters.
// no parameters
void ReportUnexpected(const char *aMessage);
// one parameter, a string
void ReportUnexpected(const char *aMessage, const nsString& aParam);
// one parameter, a token
void ReportUnexpected(const char *aMessage, const nsCSSToken& aToken);
// two parameters, a token and a character, in that order
void ReportUnexpected(const char *aMessage, const nsCSSToken& aToken,
char16_t aChar);
// two parameters, a param and a value
void ReportUnexpected(const char *aMessage, const nsString& aParam,
const nsString& aValue);
// for ReportUnexpectedEOF, aExpected can be either a stringbundle
// name or a single character. In the former case there may not be
// any format parameters.
void ReportUnexpectedEOF(const char *aExpected);
void ReportUnexpectedEOF(char16_t aExpected);
private:
void AddToError(const nsString &aErrorText);
#ifdef CSS_REPORT_PARSE_ERRORS
nsAutoString mError;
nsString mErrorLine;
nsString mFileName;
const nsCSSScanner *mScanner;
const CSSStyleSheet *mSheet;
const Loader *mLoader;
nsIURI *mURI;
uint64_t mInnerWindowID;
uint32_t mErrorLineNumber;
uint32_t mPrevErrorLineNumber;
uint32_t mErrorColNumber;
#endif
};
#ifndef CSS_REPORT_PARSE_ERRORS
inline ErrorReporter::ErrorReporter(const nsCSSScanner&,
const CSSStyleSheet*,
const Loader*,
nsIURI*) {}
inline ErrorReporter::~ErrorReporter() {}
inline void ErrorReporter::ReleaseGlobals() {}
inline void ErrorReporter::OutputError() {}
inline void ErrorReporter::ClearError() {}
inline void ErrorReporter::ReportUnexpected(const char *) {}
inline void ErrorReporter::ReportUnexpected(const char *, const nsString &) {}
inline void ErrorReporter::ReportUnexpected(const char *, const nsCSSToken &) {}
inline void ErrorReporter::ReportUnexpected(const char *, const nsCSSToken &,
char16_t) {}
inline void ErrorReporter::ReportUnexpected(const char *, const nsString &,
const nsString &) {}
inline void ErrorReporter::ReportUnexpectedEOF(const char *) {}
inline void ErrorReporter::ReportUnexpectedEOF(char16_t) {}
inline void ErrorReporter::AddToError(const nsString &) {}
#endif
} // namespace css
} // namespace mozilla
#endif // mozilla_css_ErrorReporter_h_

808
layout/style/FontFace.cpp Normal file
View file

@ -0,0 +1,808 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/dom/FontFace.h"
#include <algorithm>
#include "mozilla/dom/FontFaceBinding.h"
#include "mozilla/dom/FontFaceSet.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/dom/UnionTypes.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "nsCSSParser.h"
#include "nsCSSRules.h"
#include "nsIDocument.h"
#include "nsStyleUtil.h"
namespace mozilla {
namespace dom {
// -- FontFaceBufferSource ---------------------------------------------------
/**
* An object that wraps a FontFace object and exposes its ArrayBuffer
* or ArrayBufferView data in a form the user font set can consume.
*/
class FontFaceBufferSource : public gfxFontFaceBufferSource
{
public:
explicit FontFaceBufferSource(FontFace* aFontFace)
: mFontFace(aFontFace) {}
virtual void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength);
private:
RefPtr<FontFace> mFontFace;
};
void
FontFaceBufferSource::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength)
{
MOZ_ASSERT(mFontFace, "only call TakeBuffer once on a given "
"FontFaceBufferSource object");
mFontFace->TakeBuffer(aBuffer, aLength);
mFontFace = nullptr;
}
// -- Utility functions ------------------------------------------------------
template<typename T>
static void
GetDataFrom(const T& aObject, uint8_t*& aBuffer, uint32_t& aLength)
{
MOZ_ASSERT(!aBuffer);
aObject.ComputeLengthAndData();
// We use malloc here rather than a FallibleTArray or fallible
// operator new[] since the gfxUserFontEntry will be calling free
// on it.
aBuffer = (uint8_t*) malloc(aObject.Length());
if (!aBuffer) {
return;
}
memcpy((void*) aBuffer, aObject.Data(), aObject.Length());
aLength = aObject.Length();
}
// -- FontFace ---------------------------------------------------------------
NS_IMPL_CYCLE_COLLECTION_CLASS(FontFace)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FontFace)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoaded)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRule)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOtherFontFaceSets)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FontFace)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoaded)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRule)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOtherFontFaceSets)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(FontFace)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFace)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(FontFace)
NS_IMPL_CYCLE_COLLECTING_RELEASE(FontFace)
FontFace::FontFace(nsISupports* aParent, FontFaceSet* aFontFaceSet)
: mParent(aParent)
, mLoadedRejection(NS_OK)
, mStatus(FontFaceLoadStatus::Unloaded)
, mSourceType(SourceType(0))
, mSourceBuffer(nullptr)
, mSourceBufferLength(0)
, mFontFaceSet(aFontFaceSet)
, mInFontFaceSet(false)
{
MOZ_COUNT_CTOR(FontFace);
}
FontFace::~FontFace()
{
MOZ_COUNT_DTOR(FontFace);
SetUserFontEntry(nullptr);
if (mSourceBuffer) {
free(mSourceBuffer);
}
}
JSObject*
FontFace::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return FontFaceBinding::Wrap(aCx, this, aGivenProto);
}
static FontFaceLoadStatus
LoadStateToStatus(gfxUserFontEntry::UserFontLoadState aLoadState)
{
switch (aLoadState) {
case gfxUserFontEntry::UserFontLoadState::STATUS_NOT_LOADED:
return FontFaceLoadStatus::Unloaded;
case gfxUserFontEntry::UserFontLoadState::STATUS_LOADING:
return FontFaceLoadStatus::Loading;
case gfxUserFontEntry::UserFontLoadState::STATUS_LOADED:
return FontFaceLoadStatus::Loaded;
case gfxUserFontEntry::UserFontLoadState::STATUS_FAILED:
return FontFaceLoadStatus::Error;
}
NS_NOTREACHED("invalid aLoadState value");
return FontFaceLoadStatus::Error;
}
already_AddRefed<FontFace>
FontFace::CreateForRule(nsISupports* aGlobal,
FontFaceSet* aFontFaceSet,
nsCSSFontFaceRule* aRule)
{
RefPtr<FontFace> obj = new FontFace(aGlobal, aFontFaceSet);
obj->mRule = aRule;
obj->mSourceType = eSourceType_FontFaceRule;
obj->mInFontFaceSet = true;
return obj.forget();
}
already_AddRefed<FontFace>
FontFace::Constructor(const GlobalObject& aGlobal,
const nsAString& aFamily,
const StringOrArrayBufferOrArrayBufferView& aSource,
const FontFaceDescriptors& aDescriptors,
ErrorResult& aRv)
{
nsISupports* global = aGlobal.GetAsSupports();
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
nsIDocument* doc = window->GetDoc();
if (!doc) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<FontFace> obj = new FontFace(global, doc->Fonts());
if (!obj->SetDescriptors(aFamily, aDescriptors)) {
return obj.forget();
}
obj->InitializeSource(aSource);
return obj.forget();
}
void
FontFace::InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource)
{
if (aSource.IsString()) {
if (!ParseDescriptor(eCSSFontDesc_Src,
aSource.GetAsString(),
mDescriptors->mSrc)) {
Reject(NS_ERROR_DOM_SYNTAX_ERR);
SetStatus(FontFaceLoadStatus::Error);
return;
}
mSourceType = eSourceType_URLs;
return;
}
mSourceType = FontFace::eSourceType_Buffer;
if (aSource.IsArrayBuffer()) {
GetDataFrom(aSource.GetAsArrayBuffer(),
mSourceBuffer, mSourceBufferLength);
} else {
MOZ_ASSERT(aSource.IsArrayBufferView());
GetDataFrom(aSource.GetAsArrayBufferView(),
mSourceBuffer, mSourceBufferLength);
}
SetStatus(FontFaceLoadStatus::Loading);
DoLoad();
}
void
FontFace::GetFamily(nsString& aResult)
{
mFontFaceSet->FlushUserFontSet();
// Serialize the same way as in nsCSSFontFaceStyleDecl::GetPropertyValue.
nsCSSValue value;
GetDesc(eCSSFontDesc_Family, value);
aResult.Truncate();
if (value.GetUnit() == eCSSUnit_Null) {
return;
}
nsDependentString family(value.GetStringBufferValue());
if (!family.IsEmpty()) {
// The string length can be zero when the author passed an invalid
// family name or an invalid descriptor to the JS FontFace constructor.
nsStyleUtil::AppendEscapedCSSString(family, aResult);
}
}
void
FontFace::SetFamily(const nsAString& aValue, ErrorResult& aRv)
{
mFontFaceSet->FlushUserFontSet();
SetDescriptor(eCSSFontDesc_Family, aValue, aRv);
}
void
FontFace::GetStyle(nsString& aResult)
{
mFontFaceSet->FlushUserFontSet();
GetDesc(eCSSFontDesc_Style, eCSSProperty_font_style, aResult);
}
void
FontFace::SetStyle(const nsAString& aValue, ErrorResult& aRv)
{
mFontFaceSet->FlushUserFontSet();
SetDescriptor(eCSSFontDesc_Style, aValue, aRv);
}
void
FontFace::GetWeight(nsString& aResult)
{
mFontFaceSet->FlushUserFontSet();
GetDesc(eCSSFontDesc_Weight, eCSSProperty_font_weight, aResult);
}
void
FontFace::SetWeight(const nsAString& aValue, ErrorResult& aRv)
{
mFontFaceSet->FlushUserFontSet();
SetDescriptor(eCSSFontDesc_Weight, aValue, aRv);
}
void
FontFace::GetStretch(nsString& aResult)
{
mFontFaceSet->FlushUserFontSet();
GetDesc(eCSSFontDesc_Stretch, eCSSProperty_font_stretch, aResult);
}
void
FontFace::SetStretch(const nsAString& aValue, ErrorResult& aRv)
{
mFontFaceSet->FlushUserFontSet();
SetDescriptor(eCSSFontDesc_Stretch, aValue, aRv);
}
void
FontFace::GetUnicodeRange(nsString& aResult)
{
mFontFaceSet->FlushUserFontSet();
// There is no eCSSProperty_unicode_range for us to pass in to GetDesc
// to get a serialized (possibly defaulted) value, but that function
// doesn't use the property ID for this descriptor anyway.
GetDesc(eCSSFontDesc_UnicodeRange, eCSSProperty_UNKNOWN, aResult);
}
void
FontFace::SetUnicodeRange(const nsAString& aValue, ErrorResult& aRv)
{
mFontFaceSet->FlushUserFontSet();
SetDescriptor(eCSSFontDesc_UnicodeRange, aValue, aRv);
}
void
FontFace::GetVariant(nsString& aResult)
{
mFontFaceSet->FlushUserFontSet();
// XXX Just expose the font-variant descriptor as "normal" until we
// support it properly (bug 1055385).
aResult.AssignLiteral("normal");
}
void
FontFace::SetVariant(const nsAString& aValue, ErrorResult& aRv)
{
mFontFaceSet->FlushUserFontSet();
// XXX Ignore assignments to variant until we support font-variant
// descriptors (bug 1055385).
}
void
FontFace::GetFeatureSettings(nsString& aResult)
{
mFontFaceSet->FlushUserFontSet();
GetDesc(eCSSFontDesc_FontFeatureSettings, eCSSProperty_font_feature_settings,
aResult);
}
void
FontFace::SetFeatureSettings(const nsAString& aValue, ErrorResult& aRv)
{
mFontFaceSet->FlushUserFontSet();
SetDescriptor(eCSSFontDesc_FontFeatureSettings, aValue, aRv);
}
void
FontFace::GetDisplay(nsString& aResult)
{
mFontFaceSet->FlushUserFontSet();
GetDesc(eCSSFontDesc_Display, eCSSProperty_UNKNOWN, aResult);
}
void
FontFace::SetDisplay(const nsAString& aValue, ErrorResult& aRv)
{
mFontFaceSet->FlushUserFontSet();
SetDescriptor(eCSSFontDesc_Display, aValue, aRv);
}
FontFaceLoadStatus
FontFace::Status()
{
return mStatus;
}
Promise*
FontFace::Load(ErrorResult& aRv)
{
mFontFaceSet->FlushUserFontSet();
EnsurePromise();
if (!mLoaded) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
// Calling Load on a FontFace constructed with an ArrayBuffer data source,
// or on one that is already loading (or has finished loading), has no
// effect.
if (mSourceType == eSourceType_Buffer ||
mStatus != FontFaceLoadStatus::Unloaded) {
return mLoaded;
}
// Calling the user font entry's Load method will end up setting our
// status to Loading, but the spec requires us to set it to Loading
// here.
SetStatus(FontFaceLoadStatus::Loading);
DoLoad();
return mLoaded;
}
gfxUserFontEntry*
FontFace::CreateUserFontEntry()
{
if (!mUserFontEntry) {
MOZ_ASSERT(!HasRule(),
"Rule backed FontFace objects should already have a user font "
"entry by the time Load() can be called on them");
RefPtr<gfxUserFontEntry> newEntry =
mFontFaceSet->FindOrCreateUserFontEntryFromFontFace(this);
if (newEntry) {
SetUserFontEntry(newEntry);
}
}
return mUserFontEntry;
}
void
FontFace::DoLoad()
{
if (!CreateUserFontEntry()) {
return;
}
mUserFontEntry->Load();
}
Promise*
FontFace::GetLoaded(ErrorResult& aRv)
{
mFontFaceSet->FlushUserFontSet();
EnsurePromise();
if (!mLoaded) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
return mLoaded;
}
void
FontFace::SetStatus(FontFaceLoadStatus aStatus)
{
if (mStatus == aStatus) {
return;
}
if (aStatus < mStatus) {
// We're being asked to go backwards in status! Normally, this shouldn't
// happen. But it can if the FontFace had a user font entry that had
// loaded, but then was given a new one by FontFaceSet::InsertRuleFontFace
// if we used a local() rule. For now, just ignore the request to
// go backwards in status.
return;
}
mStatus = aStatus;
if (mInFontFaceSet) {
mFontFaceSet->OnFontFaceStatusChanged(this);
}
for (FontFaceSet* otherSet : mOtherFontFaceSets) {
otherSet->OnFontFaceStatusChanged(this);
}
if (mStatus == FontFaceLoadStatus::Loaded) {
if (mLoaded) {
mLoaded->MaybeResolve(this);
}
} else if (mStatus == FontFaceLoadStatus::Error) {
if (mSourceType == eSourceType_Buffer) {
Reject(NS_ERROR_DOM_SYNTAX_ERR);
} else {
Reject(NS_ERROR_DOM_NETWORK_ERR);
}
}
}
bool
FontFace::ParseDescriptor(nsCSSFontDesc aDescID,
const nsAString& aString,
nsCSSValue& aResult)
{
nsCSSParser parser;
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent);
nsCOMPtr<nsIURI> docURI = window->GetDocumentURI();
nsCOMPtr<nsIURI> base = window->GetDocBaseURI();
if (!parser.ParseFontFaceDescriptor(aDescID, aString,
docURI, // aSheetURL
base,
principal,
aResult)) {
aResult.Reset();
return false;
}
return true;
}
void
FontFace::SetDescriptor(nsCSSFontDesc aFontDesc,
const nsAString& aValue,
ErrorResult& aRv)
{
NS_ASSERTION(!HasRule(),
"we don't handle rule backed FontFace objects yet");
if (HasRule()) {
return;
}
nsCSSValue parsedValue;
if (!ParseDescriptor(aFontDesc, aValue, parsedValue)) {
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return;
}
mDescriptors->Get(aFontDesc) = parsedValue;
// XXX Setting descriptors doesn't actually have any effect on FontFace
// objects that have started loading or have already been loaded.
}
bool
FontFace::SetDescriptors(const nsAString& aFamily,
const FontFaceDescriptors& aDescriptors)
{
MOZ_ASSERT(!HasRule());
MOZ_ASSERT(!mDescriptors);
mDescriptors = new CSSFontFaceDescriptors;
// Parse all of the mDescriptors in aInitializer, which are the values
// we got from the JS constructor.
if (!ParseDescriptor(eCSSFontDesc_Family,
aFamily,
mDescriptors->mFamily) ||
*mDescriptors->mFamily.GetStringBufferValue() == 0 ||
!ParseDescriptor(eCSSFontDesc_Style,
aDescriptors.mStyle,
mDescriptors->mStyle) ||
!ParseDescriptor(eCSSFontDesc_Weight,
aDescriptors.mWeight,
mDescriptors->mWeight) ||
!ParseDescriptor(eCSSFontDesc_Stretch,
aDescriptors.mStretch,
mDescriptors->mStretch) ||
!ParseDescriptor(eCSSFontDesc_UnicodeRange,
aDescriptors.mUnicodeRange,
mDescriptors->mUnicodeRange) ||
!ParseDescriptor(eCSSFontDesc_FontFeatureSettings,
aDescriptors.mFeatureSettings,
mDescriptors->mFontFeatureSettings) ||
!ParseDescriptor(eCSSFontDesc_Display,
aDescriptors.mDisplay,
mDescriptors->mDisplay)) {
// XXX Handle font-variant once we support it (bug 1055385).
// If any of the descriptors failed to parse, none of them should be set
// on the FontFace.
mDescriptors = new CSSFontFaceDescriptors;
Reject(NS_ERROR_DOM_SYNTAX_ERR);
SetStatus(FontFaceLoadStatus::Error);
return false;
}
return true;
}
void
FontFace::GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const
{
if (HasRule()) {
MOZ_ASSERT(mRule);
MOZ_ASSERT(!mDescriptors);
mRule->GetDesc(aDescID, aResult);
} else {
aResult = mDescriptors->Get(aDescID);
}
}
void
FontFace::GetDesc(nsCSSFontDesc aDescID,
nsCSSPropertyID aPropID,
nsString& aResult) const
{
MOZ_ASSERT(aDescID == eCSSFontDesc_UnicodeRange ||
aDescID == eCSSFontDesc_Display ||
aPropID != eCSSProperty_UNKNOWN,
"only pass eCSSProperty_UNKNOWN for eCSSFontDesc_UnicodeRange");
nsCSSValue value;
GetDesc(aDescID, value);
aResult.Truncate();
// Fill in a default value for missing descriptors.
if (value.GetUnit() == eCSSUnit_Null) {
if (aDescID == eCSSFontDesc_UnicodeRange) {
aResult.AssignLiteral("U+0-10FFFF");
} else if (aDescID == eCSSFontDesc_Display) {
aResult.AssignLiteral("auto");
} else if (aDescID != eCSSFontDesc_Family &&
aDescID != eCSSFontDesc_Src) {
aResult.AssignLiteral("normal");
}
return;
}
if (aDescID == eCSSFontDesc_UnicodeRange) {
// Since there's no unicode-range property, we can't use
// nsCSSValue::AppendToString to serialize this descriptor.
nsStyleUtil::AppendUnicodeRange(value, aResult);
} else if (aDescID == eCSSFontDesc_Display) {
AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(value.GetIntValue(),
nsCSSProps::kFontDisplayKTable),
aResult);
} else {
value.AppendToString(aPropID, aResult, nsCSSValue::eNormalized);
}
}
void
FontFace::SetUserFontEntry(gfxUserFontEntry* aEntry)
{
if (mUserFontEntry) {
mUserFontEntry->mFontFaces.RemoveElement(this);
}
mUserFontEntry = static_cast<Entry*>(aEntry);
if (mUserFontEntry) {
mUserFontEntry->mFontFaces.AppendElement(this);
MOZ_ASSERT(mUserFontEntry->GetUserFontSet() ==
mFontFaceSet->GetUserFontSet(),
"user font entry must be associated with the same user font set "
"as the FontFace");
// Our newly assigned user font entry might be in the process of or
// finished loading, so set our status accordingly. But only do so
// if we're not going "backwards" in status, which could otherwise
// happen in this case:
//
// new FontFace("ABC", "url(x)").load();
//
// where the SetUserFontEntry call (from the after-initialization
// DoLoad call) comes after the author's call to load(), which set mStatus
// to Loading.
FontFaceLoadStatus newStatus =
LoadStateToStatus(mUserFontEntry->LoadState());
if (newStatus > mStatus) {
SetStatus(newStatus);
}
}
}
bool
FontFace::GetFamilyName(nsString& aResult)
{
nsCSSValue value;
GetDesc(eCSSFontDesc_Family, value);
if (value.GetUnit() == eCSSUnit_String) {
nsString familyname;
value.GetStringValue(familyname);
aResult.Append(familyname);
}
return !aResult.IsEmpty();
}
void
FontFace::DisconnectFromRule()
{
MOZ_ASSERT(HasRule());
// Make a copy of the descriptors.
mDescriptors = new CSSFontFaceDescriptors;
mRule->GetDescriptors(*mDescriptors);
mRule = nullptr;
mInFontFaceSet = false;
}
bool
FontFace::HasFontData() const
{
return mSourceType == eSourceType_Buffer && mSourceBuffer;
}
void
FontFace::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength)
{
MOZ_ASSERT(HasFontData());
aBuffer = mSourceBuffer;
aLength = mSourceBufferLength;
mSourceBuffer = nullptr;
mSourceBufferLength = 0;
}
already_AddRefed<gfxFontFaceBufferSource>
FontFace::CreateBufferSource()
{
RefPtr<FontFaceBufferSource> bufferSource = new FontFaceBufferSource(this);
return bufferSource.forget();
}
bool
FontFace::IsInFontFaceSet(FontFaceSet* aFontFaceSet) const
{
if (mFontFaceSet == aFontFaceSet) {
return mInFontFaceSet;
}
return mOtherFontFaceSets.Contains(aFontFaceSet);
}
void
FontFace::AddFontFaceSet(FontFaceSet* aFontFaceSet)
{
MOZ_ASSERT(!IsInFontFaceSet(aFontFaceSet));
if (mFontFaceSet == aFontFaceSet) {
mInFontFaceSet = true;
} else {
mOtherFontFaceSets.AppendElement(aFontFaceSet);
}
}
void
FontFace::RemoveFontFaceSet(FontFaceSet* aFontFaceSet)
{
MOZ_ASSERT(IsInFontFaceSet(aFontFaceSet));
if (mFontFaceSet == aFontFaceSet) {
mInFontFaceSet = false;
} else {
mOtherFontFaceSets.RemoveElement(aFontFaceSet);
}
}
void
FontFace::Reject(nsresult aResult)
{
if (mLoaded) {
mLoaded->MaybeReject(aResult);
} else if (mLoadedRejection == NS_OK) {
mLoadedRejection = aResult;
}
}
void
FontFace::EnsurePromise()
{
if (mLoaded) {
return;
}
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
// If the pref is not set, don't create the Promise (which the page wouldn't
// be able to get to anyway) as it causes the window.FontFace constructor
// to be created.
if (global && FontFaceSet::PrefEnabled()) {
ErrorResult rv;
mLoaded = Promise::Create(global, rv);
if (mStatus == FontFaceLoadStatus::Loaded) {
mLoaded->MaybeResolve(this);
} else if (mLoadedRejection != NS_OK) {
mLoaded->MaybeReject(mLoadedRejection);
}
}
}
// -- FontFace::Entry --------------------------------------------------------
/* virtual */ void
FontFace::Entry::SetLoadState(UserFontLoadState aLoadState)
{
gfxUserFontEntry::SetLoadState(aLoadState);
for (size_t i = 0; i < mFontFaces.Length(); i++) {
mFontFaces[i]->SetStatus(LoadStateToStatus(aLoadState));
}
}
/* virtual */ void
FontFace::Entry::GetUserFontSets(nsTArray<gfxUserFontSet*>& aResult)
{
aResult.Clear();
for (FontFace* f : mFontFaces) {
if (f->mInFontFaceSet) {
aResult.AppendElement(f->mFontFaceSet->GetUserFontSet());
}
for (FontFaceSet* s : f->mOtherFontFaceSets) {
aResult.AppendElement(s->GetUserFontSet());
}
}
// Remove duplicates.
aResult.Sort();
auto it = std::unique(aResult.begin(), aResult.end());
aResult.TruncateLength(it - aResult.begin());
}
} // namespace dom
} // namespace mozilla

271
layout/style/FontFace.h Normal file
View file

@ -0,0 +1,271 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 mozilla_dom_FontFace_h
#define mozilla_dom_FontFace_h
#include "mozilla/dom/FontFaceBinding.h"
#include "gfxUserFontSet.h"
#include "nsAutoPtr.h"
#include "nsCSSPropertyID.h"
#include "nsCSSValue.h"
#include "nsWrapperCache.h"
class gfxFontFaceBufferSource;
class nsCSSFontFaceRule;
namespace mozilla {
struct CSSFontFaceDescriptors;
namespace dom {
class FontFaceBufferSource;
struct FontFaceDescriptors;
class FontFaceSet;
class Promise;
class StringOrArrayBufferOrArrayBufferView;
} // namespace dom
} // namespace mozilla
namespace mozilla {
namespace dom {
class FontFace final : public nsISupports,
public nsWrapperCache
{
friend class mozilla::dom::FontFaceBufferSource;
friend class Entry;
public:
class Entry final : public gfxUserFontEntry {
friend class FontFace;
public:
Entry(gfxUserFontSet* aFontSet,
const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
uint32_t aWeight,
int32_t aStretch,
uint8_t aStyle,
const nsTArray<gfxFontFeature>& aFeatureSettings,
uint32_t aLanguageOverride,
gfxSparseBitSet* aUnicodeRanges,
uint8_t aFontDisplay)
: gfxUserFontEntry(aFontSet, aFontFaceSrcList, aWeight, aStretch,
aStyle, aFeatureSettings, aLanguageOverride,
aUnicodeRanges, aFontDisplay) {}
virtual void SetLoadState(UserFontLoadState aLoadState) override;
virtual void GetUserFontSets(nsTArray<gfxUserFontSet*>& aResult) override;
const AutoTArray<FontFace*,1>& GetFontFaces() { return mFontFaces; }
protected:
// The FontFace objects that use this user font entry. We need to store
// an array of these, not just a single pointer, since the user font
// cache can return the same entry for different FontFaces that have
// the same descriptor values and come from the same origin.
AutoTArray<FontFace*,1> mFontFaces;
};
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FontFace)
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
static already_AddRefed<FontFace>
CreateForRule(nsISupports* aGlobal, FontFaceSet* aFontFaceSet,
nsCSSFontFaceRule* aRule);
nsCSSFontFaceRule* GetRule() { return mRule; }
void GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const;
gfxUserFontEntry* CreateUserFontEntry();
gfxUserFontEntry* GetUserFontEntry() const { return mUserFontEntry; }
void SetUserFontEntry(gfxUserFontEntry* aEntry);
/**
* Returns whether this object is in the specified FontFaceSet.
*/
bool IsInFontFaceSet(FontFaceSet* aFontFaceSet) const;
void AddFontFaceSet(FontFaceSet* aFontFaceSet);
void RemoveFontFaceSet(FontFaceSet* aFontFaceSet);
FontFaceSet* GetPrimaryFontFaceSet() const { return mFontFaceSet; }
/**
* Gets the family name of the FontFace as a raw string (such as 'Times', as
* opposed to GetFamily, which returns a CSS-escaped string, such as
* '"Times"'). Returns whether a valid family name was available.
*/
bool GetFamilyName(nsString& aResult);
/**
* Returns whether this object is CSS-connected, i.e. reflecting an
* @font-face rule.
*/
bool HasRule() const { return mRule; }
/**
* Breaks the connection between this FontFace and its @font-face rule.
*/
void DisconnectFromRule();
/**
* Returns whether there is an ArrayBuffer or ArrayBufferView of font
* data.
*/
bool HasFontData() const;
/**
* Creates a gfxFontFaceBufferSource to represent the font data
* in this object.
*/
already_AddRefed<gfxFontFaceBufferSource> CreateBufferSource();
/**
* Gets a pointer to and the length of the font data stored in the
* ArrayBuffer or ArrayBufferView.
*/
bool GetData(uint8_t*& aBuffer, uint32_t& aLength);
// Web IDL
static already_AddRefed<FontFace>
Constructor(const GlobalObject& aGlobal,
const nsAString& aFamily,
const mozilla::dom::StringOrArrayBufferOrArrayBufferView& aSource,
const mozilla::dom::FontFaceDescriptors& aDescriptors,
ErrorResult& aRV);
void GetFamily(nsString& aResult);
void SetFamily(const nsAString& aValue, mozilla::ErrorResult& aRv);
void GetStyle(nsString& aResult);
void SetStyle(const nsAString& aValue, mozilla::ErrorResult& aRv);
void GetWeight(nsString& aResult);
void SetWeight(const nsAString& aValue, mozilla::ErrorResult& aRv);
void GetStretch(nsString& aResult);
void SetStretch(const nsAString& aValue, mozilla::ErrorResult& aRv);
void GetUnicodeRange(nsString& aResult);
void SetUnicodeRange(const nsAString& aValue, mozilla::ErrorResult& aRv);
void GetVariant(nsString& aResult);
void SetVariant(const nsAString& aValue, mozilla::ErrorResult& aRv);
void GetFeatureSettings(nsString& aResult);
void SetFeatureSettings(const nsAString& aValue, mozilla::ErrorResult& aRv);
void GetDisplay(nsString& aResult);
void SetDisplay(const nsAString& aValue, mozilla::ErrorResult& aRv);
mozilla::dom::FontFaceLoadStatus Status();
mozilla::dom::Promise* Load(mozilla::ErrorResult& aRv);
mozilla::dom::Promise* GetLoaded(mozilla::ErrorResult& aRv);
private:
FontFace(nsISupports* aParent, FontFaceSet* aFontFaceSet);
~FontFace();
void InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource);
// Helper function for Load.
void DoLoad();
/**
* Parses a @font-face descriptor value, storing the result in aResult.
* Returns whether the parsing was successful.
*/
bool ParseDescriptor(nsCSSFontDesc aDescID, const nsAString& aString,
nsCSSValue& aResult);
// Helper function for the descriptor setter methods.
void SetDescriptor(nsCSSFontDesc aFontDesc,
const nsAString& aValue,
mozilla::ErrorResult& aRv);
/**
* Sets all of the descriptor values in mDescriptors using values passed
* to the JS constructor.
*/
bool SetDescriptors(const nsAString& aFamily,
const FontFaceDescriptors& aDescriptors);
/**
* Sets the current loading status.
*/
void SetStatus(mozilla::dom::FontFaceLoadStatus aStatus);
void GetDesc(nsCSSFontDesc aDescID,
nsCSSPropertyID aPropID,
nsString& aResult) const;
/**
* Returns and takes ownership of the buffer storing the font data.
*/
void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength);
// Acts like mLoaded->MaybeReject(aResult), except it doesn't create mLoaded
// if it doesn't already exist.
void Reject(nsresult aResult);
// Creates mLoaded if it doesn't already exist. It may immediately resolve or
// reject mLoaded based on mStatus and mLoadedRejection.
void EnsurePromise();
nsCOMPtr<nsISupports> mParent;
// A Promise that is fulfilled once the font represented by this FontFace is
// loaded, and is rejected if the load fails. This promise is created lazily
// when JS asks for it.
RefPtr<mozilla::dom::Promise> mLoaded;
// Saves the rejection code for mLoaded if mLoaded hasn't been created yet.
nsresult mLoadedRejection;
// The @font-face rule this FontFace object is reflecting, if it is a
// rule backed FontFace.
RefPtr<nsCSSFontFaceRule> mRule;
// The FontFace object's user font entry. This is initially null, but is set
// during FontFaceSet::UpdateRules and when a FontFace is explicitly loaded.
RefPtr<Entry> mUserFontEntry;
// The current load status of the font represented by this FontFace.
// Note that we can't just reflect the value of the gfxUserFontEntry's
// status, since the spec sometimes requires us to go through the event
// loop before updating the status, rather than doing it immediately.
mozilla::dom::FontFaceLoadStatus mStatus;
// Represents where a FontFace's data is coming from.
enum SourceType {
eSourceType_FontFaceRule = 1,
eSourceType_URLs,
eSourceType_Buffer
};
// Where the font data for this FontFace is coming from.
SourceType mSourceType;
// If the FontFace was constructed with an ArrayBuffer(View), this is a
// copy of the data from it.
uint8_t* mSourceBuffer;
uint32_t mSourceBufferLength;
// The values corresponding to the font face descriptors, if we are not
// a rule backed FontFace object. For rule backed objects, we use
// the descriptors stored in mRule.
nsAutoPtr<mozilla::CSSFontFaceDescriptors> mDescriptors;
// The primary FontFaceSet this FontFace is associated with,
// regardless of whether it is currently "in" the set.
RefPtr<FontFaceSet> mFontFaceSet;
// Other FontFaceSets (apart from mFontFaceSet) that this FontFace
// appears in.
nsTArray<RefPtr<FontFaceSet>> mOtherFontFaceSets;
// Whether this FontFace appears in mFontFaceSet.
bool mInFontFaceSet;
};
} // namespace dom
} // namespace mozilla
#endif // !defined(mozilla_dom_FontFace_h)

1848
layout/style/FontFaceSet.cpp Normal file

File diff suppressed because it is too large Load diff

356
layout/style/FontFaceSet.h Normal file
View file

@ -0,0 +1,356 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 mozilla_dom_FontFaceSet_h
#define mozilla_dom_FontFaceSet_h
#include "mozilla/dom/FontFace.h"
#include "mozilla/dom/FontFaceSetBinding.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "gfxUserFontSet.h"
#include "nsCSSRules.h"
#include "nsICSSLoaderObserver.h"
struct gfxFontFaceSrc;
class gfxUserFontEntry;
class nsFontFaceLoader;
class nsIPrincipal;
class nsPIDOMWindowInner;
namespace mozilla {
namespace css {
class FontFamilyListRefCnt;
} // namespace css
namespace dom {
class FontFace;
class Promise;
} // namespace dom
} // namespace mozilla
namespace mozilla {
namespace dom {
class FontFaceSet final : public DOMEventTargetHelper
, public nsIDOMEventListener
, public nsICSSLoaderObserver
{
friend class UserFontSet;
public:
/**
* A gfxUserFontSet that integrates with the layout and style systems to
* manage @font-face rules and handle network requests for font loading.
*
* We would combine this class and FontFaceSet into the one class if it were
* possible; it's not because FontFaceSet is cycle collected and
* gfxUserFontSet isn't (and can't be, as gfx classes don't use the cycle
* collector). So UserFontSet exists just to override the needed virtual
* methods from gfxUserFontSet and to forward them on FontFaceSet.
*/
class UserFontSet final : public gfxUserFontSet
{
friend class FontFaceSet;
public:
explicit UserFontSet(FontFaceSet* aFontFaceSet)
: mFontFaceSet(aFontFaceSet)
{
}
FontFaceSet* GetFontFaceSet() { return mFontFaceSet; }
virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
nsIPrincipal** aPrincipal,
bool* aBypassCache) override;
virtual bool IsFontLoadAllowed(nsIURI* aFontLocation,
nsIPrincipal* aPrincipal) override;
virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
const gfxFontFaceSrc* aFontFaceSrc) override;
void RecordFontLoadDone(uint32_t aFontSize,
mozilla::TimeStamp aDoneTime) override;
protected:
virtual bool GetPrivateBrowsing() override;
virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
const gfxFontFaceSrc* aFontFaceSrc,
uint8_t*& aBuffer,
uint32_t& aBufferLength) override;
virtual nsresult LogMessage(gfxUserFontEntry* aUserFontEntry,
const char* aMessage,
uint32_t aFlags = nsIScriptError::errorFlag,
nsresult aStatus = NS_OK) override;
virtual void DoRebuildUserFontSet() override;
virtual already_AddRefed<gfxUserFontEntry> CreateUserFontEntry(
const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
uint32_t aWeight,
int32_t aStretch,
uint8_t aStyle,
const nsTArray<gfxFontFeature>& aFeatureSettings,
uint32_t aLanguageOverride,
gfxSparseBitSet* aUnicodeRanges,
uint8_t aFontDisplay) override;
private:
RefPtr<FontFaceSet> mFontFaceSet;
};
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FontFaceSet, DOMEventTargetHelper)
NS_DECL_NSIDOMEVENTLISTENER
FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
UserFontSet* GetUserFontSet() { return mUserFontSet; }
// Called by nsFontFaceLoader when the loader has completed normally.
// It's removed from the mLoaders set.
void RemoveLoader(nsFontFaceLoader* aLoader);
bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules);
nsPresContext* GetPresContext();
// search for @font-face rule that matches a platform font entry
nsCSSFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry);
void IncrementGeneration(bool aIsRebuild = false);
/**
* Finds an existing entry in the user font cache or creates a new user
* font entry for the given FontFace object.
*/
static already_AddRefed<gfxUserFontEntry>
FindOrCreateUserFontEntryFromFontFace(FontFace* aFontFace);
/**
* Notification method called by a FontFace to indicate that its loading
* status has changed.
*/
void OnFontFaceStatusChanged(FontFace* aFontFace);
/**
* Notification method called by the nsPresContext to indicate that the
* refresh driver ticked and flushed style and layout.
* were just flushed.
*/
void DidRefresh();
/**
* Returns whether the "layout.css.font-loading-api.enabled" pref is true.
*/
static bool PrefEnabled();
// nsICSSLoaderObserver
NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet,
bool aWasAlternate,
nsresult aStatus) override;
FontFace* GetFontFaceAt(uint32_t aIndex);
void FlushUserFontSet();
static nsPresContext* GetPresContextFor(gfxUserFontSet* aUserFontSet)
{
FontFaceSet* set = static_cast<UserFontSet*>(aUserFontSet)->mFontFaceSet;
return set ? set->GetPresContext() : nullptr;
}
// -- Web IDL --------------------------------------------------------------
IMPL_EVENT_HANDLER(loading)
IMPL_EVENT_HANDLER(loadingdone)
IMPL_EVENT_HANDLER(loadingerror)
already_AddRefed<mozilla::dom::Promise> Load(JSContext* aCx,
const nsAString& aFont,
const nsAString& aText,
mozilla::ErrorResult& aRv);
bool Check(const nsAString& aFont,
const nsAString& aText,
mozilla::ErrorResult& aRv);
mozilla::dom::Promise* GetReady(mozilla::ErrorResult& aRv);
mozilla::dom::FontFaceSetLoadStatus Status();
FontFaceSet* Add(FontFace& aFontFace, mozilla::ErrorResult& aRv);
void Clear();
bool Delete(FontFace& aFontFace);
bool Has(FontFace& aFontFace);
uint32_t Size();
already_AddRefed<mozilla::dom::FontFaceSetIterator> Entries();
already_AddRefed<mozilla::dom::FontFaceSetIterator> Values();
void ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback,
JS::Handle<JS::Value> aThisArg,
mozilla::ErrorResult& aRv);
private:
~FontFaceSet();
/**
* Returns whether the given FontFace is currently "in" the FontFaceSet.
*/
bool HasAvailableFontFace(FontFace* aFontFace);
/**
* Removes any listeners and observers.
*/
void Disconnect();
void RemoveDOMContentLoadedListener();
/**
* Returns whether there might be any pending font loads, which should cause
* the mReady Promise not to be resolved yet.
*/
bool MightHavePendingFontLoads();
/**
* Checks to see whether it is time to replace mReady and dispatch a
* "loading" event.
*/
void CheckLoadingStarted();
/**
* Checks to see whether it is time to resolve mReady and dispatch any
* "loadingdone" and "loadingerror" events.
*/
void CheckLoadingFinished();
/**
* Callback for invoking CheckLoadingFinished after going through the
* event loop. See OnFontFaceStatusChanged.
*/
void CheckLoadingFinishedAfterDelay();
/**
* Dispatches a FontFaceSetLoadEvent to this object.
*/
void DispatchLoadingFinishedEvent(
const nsAString& aType,
const nsTArray<FontFace*>& aFontFaces);
// Note: if you add new cycle collected objects to FontFaceRecord,
// make sure to update FontFaceSet's cycle collection macros
// accordingly.
struct FontFaceRecord {
RefPtr<FontFace> mFontFace;
SheetType mSheetType; // only relevant for mRuleFaces entries
// When true, indicates that when finished loading, the FontFace should be
// included in the subsequent loadingdone/loadingerror event fired at the
// FontFaceSet.
bool mLoadEventShouldFire;
};
static already_AddRefed<gfxUserFontEntry> FindOrCreateUserFontEntryFromFontFace(
const nsAString& aFamilyName,
FontFace* aFontFace,
SheetType aSheetType);
// search for @font-face rule that matches a userfont font entry
nsCSSFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry);
nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
const gfxFontFaceSrc* aFontFaceSrc);
nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
nsIPrincipal** aPrincipal,
bool* aBypassCache);
bool IsFontLoadAllowed(nsIURI* aFontLocation, nsIPrincipal* aPrincipal);
bool GetPrivateBrowsing();
nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
const gfxFontFaceSrc* aFontFaceSrc,
uint8_t*& aBuffer,
uint32_t& aBufferLength);
nsresult LogMessage(gfxUserFontEntry* aUserFontEntry,
const char* aMessage,
uint32_t aFlags,
nsresult aStatus);
void RebuildUserFontSet();
void InsertRuleFontFace(FontFace* aFontFace, SheetType aSheetType,
nsTArray<FontFaceRecord>& aOldRecords,
bool& aFontSetModified);
void InsertNonRuleFontFace(FontFace* aFontFace, bool& aFontSetModified);
#ifdef DEBUG
bool HasRuleFontFace(FontFace* aFontFace);
#endif
/**
* Returns whether we have any loading FontFace objects in the FontFaceSet.
*/
bool HasLoadingFontFaces();
// Helper function for HasLoadingFontFaces.
void UpdateHasLoadingFontFaces();
void ParseFontShorthandForMatching(
const nsAString& aFont,
RefPtr<mozilla::css::FontFamilyListRefCnt>& aFamilyList,
uint32_t& aWeight,
int32_t& aStretch,
uint8_t& aStyle,
ErrorResult& aRv);
void FindMatchingFontFaces(const nsAString& aFont,
const nsAString& aText,
nsTArray<FontFace*>& aFontFaces,
mozilla::ErrorResult& aRv);
TimeStamp GetNavigationStartTimeStamp();
RefPtr<UserFontSet> mUserFontSet;
// The document this is a FontFaceSet for.
nsCOMPtr<nsIDocument> mDocument;
// A Promise that is fulfilled once all of the FontFace objects
// in mRuleFaces and mNonRuleFaces that started or were loading at the
// time the Promise was created have finished loading. It is rejected if
// any of those fonts failed to load. mReady is replaced with
// a new Promise object whenever mReady is settled and another
// FontFace in mRuleFaces or mNonRuleFaces starts to load.
// Note that mReady is created lazily when GetReady() is called.
RefPtr<mozilla::dom::Promise> mReady;
// Whether the ready promise must be resolved when it's created.
bool mResolveLazilyCreatedReadyPromise;
// Set of all loaders pointing to us. These are not strong pointers,
// but that's OK because nsFontFaceLoader always calls RemoveLoader on
// us before it dies (unless we die first).
nsTHashtable< nsPtrHashKey<nsFontFaceLoader> > mLoaders;
// The @font-face rule backed FontFace objects in the FontFaceSet.
nsTArray<FontFaceRecord> mRuleFaces;
// The non rule backed FontFace objects that have been added to this
// FontFaceSet.
nsTArray<FontFaceRecord> mNonRuleFaces;
// The overall status of the loading or loaded fonts in the FontFaceSet.
mozilla::dom::FontFaceSetLoadStatus mStatus;
// Whether mNonRuleFaces has changed since last time UpdateRules ran.
bool mNonRuleFacesDirty;
// Whether any FontFace objects in mRuleFaces or mNonRuleFaces are
// loading. Only valid when mHasLoadingFontFacesIsDirty is false. Don't use
// this variable directly; call the HasLoadingFontFaces method instead.
bool mHasLoadingFontFaces;
// This variable is only valid when mLoadingDirty is false.
bool mHasLoadingFontFacesIsDirty;
// Whether CheckLoadingFinished calls should be ignored. See comment in
// OnFontFaceStatusChanged.
bool mDelayedLoadCheck;
};
} // namespace dom
} // namespace mozilla
#endif // !defined(mozilla_dom_FontFaceSet_h)

View file

@ -0,0 +1,80 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/dom/FontFaceSetIterator.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION(FontFaceSetIterator, mFontFaceSet)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(FontFaceSetIterator, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(FontFaceSetIterator, Release)
FontFaceSetIterator::FontFaceSetIterator(FontFaceSet* aFontFaceSet,
bool aIsKeyAndValue)
: mFontFaceSet(aFontFaceSet)
, mNextIndex(0)
, mIsKeyAndValue(aIsKeyAndValue)
{
MOZ_COUNT_CTOR(FontFaceSetIterator);
}
FontFaceSetIterator::~FontFaceSetIterator()
{
MOZ_COUNT_DTOR(FontFaceSetIterator);
}
bool
FontFaceSetIterator::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector)
{
return FontFaceSetIteratorBinding::Wrap(aCx, this, aGivenProto, aReflector);
}
void
FontFaceSetIterator::Next(JSContext* aCx, FontFaceSetIteratorResult& aResult,
ErrorResult& aRv)
{
if (!mFontFaceSet) {
aResult.mDone = true;
return;
}
FontFace* face = mFontFaceSet->GetFontFaceAt(mNextIndex++);
if (!face) {
aResult.mValue.setUndefined();
aResult.mDone = true;
mFontFaceSet = nullptr;
return;
}
JS::Rooted<JS::Value> value(aCx);
if (!ToJSValue(aCx, face, &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (mIsKeyAndValue) {
JS::AutoValueArray<2> values(aCx);
values[0].set(value);
values[1].set(value);
JS::Rooted<JSObject*> array(aCx);
array = JS_NewArrayObject(aCx, values);
if (array) {
aResult.mValue.setObject(*array);
}
} else {
aResult.mValue = value;
}
aResult.mDone = false;
}
} // namespace dom
} // namespace mozilla

View file

@ -0,0 +1,44 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 mozilla_dom_FontFaceSetIterator_h
#define mozilla_dom_FontFaceSetIterator_h
#include "mozilla/dom/FontFaceSet.h"
#include "mozilla/dom/FontFaceSetBinding.h"
#include "mozilla/dom/NonRefcountedDOMObject.h"
namespace mozilla {
namespace dom {
class FontFaceSetIterator final
{
public:
FontFaceSetIterator(mozilla::dom::FontFaceSet* aFontFaceSet,
bool aIsKeyAndValue);
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(FontFaceSetIterator)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(FontFaceSetIterator)
bool WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
// WebIDL
void Next(JSContext* aCx, FontFaceSetIteratorResult& aResult,
mozilla::ErrorResult& aRv);
private:
~FontFaceSetIterator();
RefPtr<FontFaceSet> mFontFaceSet;
uint32_t mNextIndex;
bool mIsKeyAndValue;
};
} // namespace dom
} // namespace mozilla
#endif // !defined(mozilla_dom_FontFaceSetIterator_h)

View file

@ -0,0 +1,104 @@
# 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/.
import sys
import string
import argparse
import subprocess
import buildconfig
from mozbuild import shellutil
def get_properties(preprocessorHeader):
cpp = list(buildconfig.substs['CPP'])
cpp += shellutil.split(buildconfig.substs['ACDEFINES'])
cpp.append(preprocessorHeader)
preprocessed = subprocess.check_output(cpp)
properties = [{"name":p[0], "prop":p[1], "id":p[2],
"flags":p[3], "pref":p[4], "proptype":p[5]}
for (i, p) in enumerate(eval(preprocessed))]
# Sort the list so that longhand and logical properties are intermingled
# first, shorthand properties follow, then aliases appear last. This matches
# the order of the nsCSSPropertyID enum.
def property_compare(x, y):
property_order = {"longhand": 0, "logical": 0, "shorthand": 1, "alias": 2}
return property_order[x["proptype"]] - property_order[y["proptype"]]
properties = sorted(properties, cmp=property_compare)
for i, p in enumerate(properties):
p["index"] = i
# Record each property's IDL name.
for p in properties:
if "CSS_PROPERTY_INTERNAL" in p["flags"]:
p["idlname"] = None
else:
idl_name = p["prop"]
if not idl_name.startswith("Moz"):
idl_name = idl_name[0].lower() + idl_name[1:]
p["idlname"] = idl_name
return properties
def generate_idl_names(properties):
names = []
for p in properties:
if p["proptype"] is "alias":
continue
if p["idlname"] is None:
names.append(" nullptr, // %s" % p["name"])
else:
names.append(' "%s",' % p["idlname"])
return "\n".join(names)
def generate_assertions(properties):
def enum(p):
if p["proptype"] is "alias":
return "eCSSPropertyAlias_%s" % p["prop"]
else:
return "eCSSProperty_%s" % p["id"]
msg = ('static_assert(%s == %d, "GenerateCSSPropsGenerated.py did not list '
'properties in nsCSSPropertyID order");')
return "\n".join(map(lambda p: msg % (enum(p), p["index"]), properties))
def generate_idl_name_positions(properties):
# Skip aliases.
ps = filter(lambda p: p["proptype"] is not "alias", properties)
# Sort alphabetically by IDL name.
ps = sorted(ps, key=lambda p: p["idlname"])
# Annotate entries with the sorted position.
ps = [(p, position) for position, p in enumerate(ps)]
# Sort back to nsCSSPropertyID order.
ps = sorted(ps, key=lambda (p, position): p["index"])
return ",\n".join(map(lambda (p, position): " %d" % position, ps))
def generate(output, cppTemplate, preprocessorHeader):
cppFile = open(cppTemplate, "r")
cppTemplate = cppFile.read()
cppFile.close()
properties = get_properties(preprocessorHeader)
substitutions = {
"idl_names": generate_idl_names(properties),
"assertions": generate_assertions(properties),
"idl_name_positions": generate_idl_name_positions(properties),
}
output.write("/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */\n\n" +
string.Template(cppTemplate).substitute(substitutions) + "\n")
def main():
parser = argparse.ArgumentParser()
parser.add_argument('cppTemplate', help='CSS property file template')
parser.add_argument('preprocessorHeader', help='Header file to pass through the preprocessor')
args = parser.parse_args()
generate(sys.stdout, args.cppTemplate, args.preprocessorHeader)
if __name__ == '__main__':
main()

101
layout/style/GroupRule.h Normal file
View file

@ -0,0 +1,101 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/*
* internal interface representing CSS style rules that contain other
* rules, such as @media rules
*/
#ifndef mozilla_css_GroupRule_h__
#define mozilla_css_GroupRule_h__
#include "mozilla/Attributes.h"
#include "mozilla/IncrementalClearCOMRuleArray.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/Rule.h"
#include "nsCycleCollectionParticipant.h"
class nsPresContext;
class nsMediaQueryResultCacheKey;
namespace mozilla {
class CSSStyleSheet;
namespace css {
class GroupRuleRuleList;
// inherits from Rule so it can be shared between
// MediaRule and DocumentRule
class GroupRule : public Rule
{
protected:
GroupRule(uint32_t aLineNumber, uint32_t aColumnNumber);
GroupRule(const GroupRule& aCopy);
virtual ~GroupRule();
public:
NS_DECL_CYCLE_COLLECTION_CLASS(GroupRule)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
// implement part of Rule
DECL_STYLE_RULE_INHERIT_NO_DOMRULE
#ifdef DEBUG
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
#endif
virtual void SetStyleSheet(CSSStyleSheet* aSheet) override;
public:
void AppendStyleRule(Rule* aRule);
int32_t StyleRuleCount() const { return mRules.Count(); }
Rule* GetStyleRuleAt(int32_t aIndex) const;
typedef IncrementalClearCOMRuleArray::nsCOMArrayEnumFunc RuleEnumFunc;
bool EnumerateRulesForwards(RuleEnumFunc aFunc, void * aData) const;
/*
* The next three methods should never be called unless you have first
* called WillDirty() on the parent stylesheet. After they are
* called, DidDirty() needs to be called on the sheet.
*/
nsresult DeleteStyleRuleAt(uint32_t aIndex);
nsresult InsertStyleRuleAt(uint32_t aIndex, Rule* aRule);
virtual bool UseForPresentation(nsPresContext* aPresContext,
nsMediaQueryResultCacheKey& aKey) = 0;
// non-virtual -- it is only called by subclasses
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override = 0;
static bool
CloneRuleInto(Rule* aRule, void* aArray)
{
RefPtr<Rule> clone = aRule->Clone();
static_cast<IncrementalClearCOMRuleArray*>(aArray)->AppendObject(clone);
return true;
}
protected:
// to help implement nsIDOMCSSRule
void AppendRulesToCssText(nsAString& aCssText);
// to implement common methods on nsIDOMCSSMediaRule and
// nsIDOMCSSMozDocumentRule
nsresult GetCssRules(nsIDOMCSSRuleList* *aRuleList);
nsresult InsertRule(const nsAString & aRule, uint32_t aIndex,
uint32_t* _retval);
nsresult DeleteRule(uint32_t aIndex);
IncrementalClearCOMRuleArray mRules;
RefPtr<GroupRuleRuleList> mRuleCollection; // lazily constructed
};
} // namespace css
} // namespace mozilla
#endif /* mozilla_css_GroupRule_h__ */

133
layout/style/HandleRefPtr.h Normal file
View file

@ -0,0 +1,133 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=2 sw=2 et tw=78:
* 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/.
*/
/* smart pointer for strong references to objects through pointer-like
* "handle" objects */
#include <algorithm>
#include "mozilla/Assertions.h"
#ifndef mozilla_HandleRefPtr_h
#define mozilla_HandleRefPtr_h
namespace mozilla {
/**
* A class for holding strong references to handle-managed objects.
*
* This is intended for use with objects like RestyleManagerHandle,
* where the handle type is not a pointer but which can still have
* ->AddRef() and ->Release() called on it.
*/
template<typename T>
class HandleRefPtr
{
public:
HandleRefPtr() {}
HandleRefPtr(HandleRefPtr<T>& aRhs)
{
assign(aRhs.mHandle);
}
HandleRefPtr(HandleRefPtr<T>&& aRhs)
{
std::swap(mHandle, aRhs.mHandle);
}
MOZ_IMPLICIT HandleRefPtr(T aRhs)
{
assign(aRhs);
}
HandleRefPtr<T>& operator=(HandleRefPtr<T>& aRhs)
{
assign(aRhs.mHandle);
return *this;
}
HandleRefPtr<T>& operator=(T aRhs)
{
assign(aRhs);
return *this;
}
~HandleRefPtr() { assign(nullptr); }
explicit operator bool() const { return !!mHandle; }
bool operator!() const { return !mHandle; }
operator T() const { return mHandle; }
T operator->() const { return mHandle; }
void swap(HandleRefPtr<T>& aOther)
{
std::swap(mHandle, aOther.mHandle);
}
private:
void assign(T aPtr)
{
// AddRef early so |aPtr| can't disappear underneath us.
if (aPtr) {
aPtr->AddRef();
}
// Don't release |mHandle| yet: if |mHandle| indirectly owns |this|,
// releasing would invalidate |this|. Swap |aPtr| for |mHandle| so that
// |aPtr| lives as long as |this|, then release the new |aPtr| (really the
// original |mHandle|) so that if |this| is invalidated, we're not using it
// any more.
std::swap(mHandle, aPtr);
if (aPtr) {
aPtr->Release();
}
}
T mHandle;
};
template<typename T>
inline bool operator==(const HandleRefPtr<T>& aLHS, const HandleRefPtr<T>& aRHS)
{
return static_cast<T>(aLHS) == static_cast<T>(aRHS);
}
template<typename T>
inline bool operator==(const HandleRefPtr<T>& aLHS, T aRHS)
{
return static_cast<T>(aLHS) == aRHS;
}
template<typename T>
inline bool operator==(T aLHS, const HandleRefPtr<T>& aRHS)
{
return aLHS == static_cast<T>(aRHS);
}
template<typename T>
inline bool operator!=(const HandleRefPtr<T>& aLHS, const HandleRefPtr<T>& aRHS)
{
return !(aLHS == aRHS);
}
template<typename T>
inline bool operator!=(const HandleRefPtr<T>& aLHS, T aRHS)
{
return !(aLHS == aRHS);
}
template<typename T>
inline bool operator!=(T aLHS, const HandleRefPtr<T>& aRHS)
{
return !(aLHS == aRHS);
}
} // namespace mozilla
#endif // mozilla_HandleRefPtr_h

View file

@ -0,0 +1,31 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/*
* This CSS stylesheet defines the rules to be applied to any ImageDocuments,
* including those in frames.
*/
@media not print {
.overflowingVertical, .overflowingHorizontalOnly {
cursor: zoom-out;
}
.shrinkToFit {
cursor: zoom-in;
}
}
@media print {
/* We must declare the image as a block element. If we stay as
an inline element, our parent LineBox will be inline too and
ignore the available height during reflow.
This is bad during printing, it means tall image frames won't know
the size of the paper and cannot break into continuations along
multiple pages. */
img {
display: block;
}
}

View file

@ -0,0 +1,529 @@
/* 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 class that handles style system image loads (other image loads are handled
* by the nodes in the content tree).
*/
#include "mozilla/css/ImageLoader.h"
#include "nsAutoPtr.h"
#include "nsContentUtils.h"
#include "nsLayoutUtils.h"
#include "nsError.h"
#include "nsDisplayList.h"
#include "FrameLayerBuilder.h"
#include "nsSVGEffects.h"
#include "imgIContainer.h"
#include "Image.h"
namespace mozilla {
namespace css {
void
ImageLoader::DropDocumentReference()
{
// It's okay if GetPresContext returns null here (due to the presshell pointer
// on the document being null) as that means the presshell has already
// been destroyed, and it also calls ClearFrames when it is destroyed.
ClearFrames(GetPresContext());
for (auto it = mImages.Iter(); !it.Done(); it.Next()) {
ImageLoader::Image* image = it.Get()->GetKey();
imgIRequest* request = image->mRequests.GetWeak(mDocument);
if (request) {
request->CancelAndForgetObserver(NS_BINDING_ABORTED);
}
image->mRequests.Remove(mDocument);
}
mImages.Clear();
mDocument = nullptr;
}
void
ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
nsIFrame* aFrame)
{
nsCOMPtr<imgINotificationObserver> observer;
aRequest->GetNotificationObserver(getter_AddRefs(observer));
if (!observer) {
// The request has already been canceled, so ignore it. This is ok because
// we're not going to get any more notifications from a canceled request.
return;
}
MOZ_ASSERT(observer == this);
FrameSet* frameSet = nullptr;
if (mRequestToFrameMap.Get(aRequest, &frameSet)) {
NS_ASSERTION(frameSet, "This should never be null!");
}
if (!frameSet) {
nsAutoPtr<FrameSet> newFrameSet(new FrameSet());
mRequestToFrameMap.Put(aRequest, newFrameSet);
frameSet = newFrameSet.forget();
nsPresContext* presContext = GetPresContext();
if (presContext) {
nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
aRequest,
nullptr);
}
}
RequestSet* requestSet = nullptr;
if (mFrameToRequestMap.Get(aFrame, &requestSet)) {
NS_ASSERTION(requestSet, "This should never be null");
}
if (!requestSet) {
nsAutoPtr<RequestSet> newRequestSet(new RequestSet());
mFrameToRequestMap.Put(aFrame, newRequestSet);
requestSet = newRequestSet.forget();
}
// Add these to the sets, but only if they're not already there.
uint32_t i = frameSet->IndexOfFirstElementGt(aFrame);
if (i == 0 || aFrame != frameSet->ElementAt(i-1)) {
frameSet->InsertElementAt(i, aFrame);
}
i = requestSet->IndexOfFirstElementGt(aRequest);
if (i == 0 || aRequest != requestSet->ElementAt(i-1)) {
requestSet->InsertElementAt(i, aRequest);
}
}
void
ImageLoader::MaybeRegisterCSSImage(ImageLoader::Image* aImage)
{
NS_ASSERTION(aImage, "This should never be null!");
bool found = false;
aImage->mRequests.GetWeak(mDocument, &found);
if (found) {
// This document already has a request.
return;
}
imgRequestProxy* canonicalRequest = aImage->mRequests.GetWeak(nullptr);
if (!canonicalRequest) {
// The image was blocked or something.
return;
}
RefPtr<imgRequestProxy> request;
// Ignore errors here. If cloning fails for some reason we'll put a null
// entry in the hash and we won't keep trying to clone.
mInClone = true;
canonicalRequest->Clone(this, getter_AddRefs(request));
mInClone = false;
aImage->mRequests.Put(mDocument, request);
AddImage(aImage);
}
void
ImageLoader::DeregisterCSSImage(ImageLoader::Image* aImage)
{
RemoveImage(aImage);
}
void
ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest,
nsIFrame* aFrame)
{
FrameSet* frameSet = nullptr;
RequestSet* requestSet = nullptr;
#ifdef DEBUG
{
nsCOMPtr<imgINotificationObserver> observer;
aRequest->GetNotificationObserver(getter_AddRefs(observer));
MOZ_ASSERT(!observer || observer == this);
}
#endif
mRequestToFrameMap.Get(aRequest, &frameSet);
mFrameToRequestMap.Get(aFrame, &requestSet);
if (frameSet) {
frameSet->RemoveElementSorted(aFrame);
}
if (requestSet) {
requestSet->RemoveElementSorted(aRequest);
}
if (frameSet && !frameSet->Length()) {
mRequestToFrameMap.Remove(aRequest);
nsPresContext* presContext = GetPresContext();
if (presContext) {
nsLayoutUtils::DeregisterImageRequest(presContext,
aRequest,
nullptr);
}
}
if (requestSet && !requestSet->Length()) {
mFrameToRequestMap.Remove(aFrame);
}
}
void
ImageLoader::DropRequestsForFrame(nsIFrame* aFrame)
{
RequestSet* requestSet = nullptr;
if (!mFrameToRequestMap.Get(aFrame, &requestSet)) {
return;
}
NS_ASSERTION(requestSet, "This should never be null");
RequestSet frozenRequestSet(*requestSet);
for (RequestSet::size_type i = frozenRequestSet.Length(); i != 0; --i) {
imgIRequest* request = frozenRequestSet.ElementAt(i - 1);
DisassociateRequestFromFrame(request, aFrame);
}
}
void
ImageLoader::SetAnimationMode(uint16_t aMode)
{
NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
aMode == imgIContainer::kDontAnimMode ||
aMode == imgIContainer::kLoopOnceAnimMode,
"Wrong Animation Mode is being set!");
for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) {
auto request = static_cast<imgIRequest*>(iter.Key());
#ifdef DEBUG
{
nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request);
NS_ASSERTION(debugRequest == request, "This is bad");
}
#endif
nsCOMPtr<imgIContainer> container;
request->GetImage(getter_AddRefs(container));
if (!container) {
continue;
}
// This can fail if the image is in error, and we don't care.
container->SetAnimationMode(aMode);
}
}
void
ImageLoader::ClearFrames(nsPresContext* aPresContext)
{
for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) {
auto request = static_cast<imgIRequest*>(iter.Key());
#ifdef DEBUG
{
nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request);
NS_ASSERTION(debugRequest == request, "This is bad");
}
#endif
if (aPresContext) {
nsLayoutUtils::DeregisterImageRequest(aPresContext,
request,
nullptr);
}
}
mRequestToFrameMap.Clear();
mFrameToRequestMap.Clear();
}
void
ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
nsIURI* aReferrer, ImageLoader::Image* aImage)
{
NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?");
aImage->mRequests.Put(nullptr, nullptr);
if (!aURI) {
return;
}
RefPtr<imgRequestProxy> request;
nsresult rv = nsContentUtils::LoadImage(aURI, mDocument, mDocument,
aOriginPrincipal, aReferrer,
mDocument->GetReferrerPolicy(),
nullptr, nsIRequest::LOAD_NORMAL,
NS_LITERAL_STRING("css"),
getter_AddRefs(request));
if (NS_FAILED(rv) || !request) {
return;
}
RefPtr<imgRequestProxy> clonedRequest;
mInClone = true;
rv = request->Clone(this, getter_AddRefs(clonedRequest));
mInClone = false;
if (NS_FAILED(rv)) {
return;
}
aImage->mRequests.Put(nullptr, request);
aImage->mRequests.Put(mDocument, clonedRequest);
AddImage(aImage);
}
void
ImageLoader::AddImage(ImageLoader::Image* aImage)
{
NS_ASSERTION(!mImages.Contains(aImage), "Huh?");
mImages.PutEntry(aImage);
}
void
ImageLoader::RemoveImage(ImageLoader::Image* aImage)
{
NS_ASSERTION(mImages.Contains(aImage), "Huh?");
mImages.RemoveEntry(aImage);
}
nsPresContext*
ImageLoader::GetPresContext()
{
if (!mDocument) {
return nullptr;
}
nsIPresShell* shell = mDocument->GetShell();
if (!shell) {
return nullptr;
}
return shell->GetPresContext();
}
void InvalidateImagesCallback(nsIFrame* aFrame,
FrameLayerBuilder::DisplayItemData* aItem)
{
nsDisplayItem::Type type = nsDisplayItem::GetDisplayItemTypeFromKey(aItem->GetDisplayItemKey());
uint8_t flags = nsDisplayItem::GetDisplayItemFlagsForType(type);
if (flags & nsDisplayItem::TYPE_RENDERS_NO_IMAGES) {
return;
}
if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
printf_stderr("Invalidating display item(type=%d) based on frame %p \
because it might contain an invalidated image\n", type, aFrame);
}
aItem->Invalidate();
aFrame->SchedulePaint();
}
void
ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint)
{
NS_ASSERTION(aFrameSet, "Must have a frame set");
NS_ASSERTION(mDocument, "Should have returned earlier!");
FrameSet::size_type length = aFrameSet->Length();
for (FrameSet::size_type i = 0; i < length; i++) {
nsIFrame* frame = aFrameSet->ElementAt(i);
if (frame->StyleVisibility()->IsVisible()) {
if (frame->IsFrameOfType(nsIFrame::eTablePart)) {
// Tables don't necessarily build border/background display items
// for the individual table part frames, so IterateRetainedDataFor
// might not find the right display item.
frame->InvalidateFrame();
} else {
FrameLayerBuilder::IterateRetainedDataFor(frame, InvalidateImagesCallback);
// Update ancestor rendering observers (-moz-element etc)
nsIFrame *f = frame;
while (f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
nsSVGEffects::InvalidateDirectRenderingObservers(f);
f = nsLayoutUtils::GetCrossDocParentFrame(f);
}
if (aForcePaint) {
frame->SchedulePaint();
}
}
}
}
}
NS_IMPL_ADDREF(ImageLoader)
NS_IMPL_RELEASE(ImageLoader)
NS_INTERFACE_MAP_BEGIN(ImageLoader)
NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
NS_INTERFACE_MAP_ENTRY(imgIOnloadBlocker)
NS_INTERFACE_MAP_END
NS_IMETHODIMP
ImageLoader::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
{
if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
nsCOMPtr<imgIContainer> image;
aRequest->GetImage(getter_AddRefs(image));
return OnSizeAvailable(aRequest, image);
}
if (aType == imgINotificationObserver::IS_ANIMATED) {
return OnImageIsAnimated(aRequest);
}
if (aType == imgINotificationObserver::FRAME_COMPLETE) {
return OnFrameComplete(aRequest);
}
if (aType == imgINotificationObserver::FRAME_UPDATE) {
return OnFrameUpdate(aRequest);
}
if (aType == imgINotificationObserver::DECODE_COMPLETE) {
nsCOMPtr<imgIContainer> image;
aRequest->GetImage(getter_AddRefs(image));
if (image && mDocument) {
image->PropagateUseCounters(mDocument);
}
}
return NS_OK;
}
nsresult
ImageLoader::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
{
nsPresContext* presContext = GetPresContext();
if (!presContext) {
return NS_OK;
}
aImage->SetAnimationMode(presContext->ImageAnimationMode());
return NS_OK;
}
nsresult
ImageLoader::OnImageIsAnimated(imgIRequest* aRequest)
{
if (!mDocument) {
return NS_OK;
}
FrameSet* frameSet = nullptr;
if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
return NS_OK;
}
// Register with the refresh driver now that we are aware that
// we are animated.
nsPresContext* presContext = GetPresContext();
if (presContext) {
nsLayoutUtils::RegisterImageRequest(presContext,
aRequest,
nullptr);
}
return NS_OK;
}
nsresult
ImageLoader::OnFrameComplete(imgIRequest* aRequest)
{
if (!mDocument || mInClone) {
return NS_OK;
}
FrameSet* frameSet = nullptr;
if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
return NS_OK;
}
NS_ASSERTION(frameSet, "This should never be null!");
// Since we just finished decoding a frame, we always want to paint, in case
// we're now able to paint an image that we couldn't paint before (and hence
// that we don't have retained data for).
DoRedraw(frameSet, /* aForcePaint = */ true);
return NS_OK;
}
nsresult
ImageLoader::OnFrameUpdate(imgIRequest* aRequest)
{
if (!mDocument || mInClone) {
return NS_OK;
}
FrameSet* frameSet = nullptr;
if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
return NS_OK;
}
NS_ASSERTION(frameSet, "This should never be null!");
DoRedraw(frameSet, /* aForcePaint = */ false);
return NS_OK;
}
NS_IMETHODIMP
ImageLoader::BlockOnload(imgIRequest* aRequest)
{
if (!mDocument) {
return NS_OK;
}
mDocument->BlockOnload();
return NS_OK;
}
NS_IMETHODIMP
ImageLoader::UnblockOnload(imgIRequest* aRequest)
{
if (!mDocument) {
return NS_OK;
}
mDocument->UnblockOnload(false);
return NS_OK;
}
void
ImageLoader::FlushUseCounters()
{
for (auto iter = mImages.Iter(); !iter.Done(); iter.Next()) {
nsPtrHashKey<Image>* key = iter.Get();
ImageLoader::Image* image = key->GetKey();
imgIRequest* request = image->mRequests.GetWeak(mDocument);
nsCOMPtr<imgIContainer> container;
request->GetImage(getter_AddRefs(container));
if (container) {
static_cast<image::Image*>(container.get())->ReportUseCounters();
}
}
}
} // namespace css
} // namespace mozilla

124
layout/style/ImageLoader.h Normal file
View file

@ -0,0 +1,124 @@
/* 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 class that handles style system image loads (other image loads are handled
// by the nodes in the content tree).
#ifndef mozilla_css_ImageLoader_h___
#define mozilla_css_ImageLoader_h___
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
#include "imgIRequest.h"
#include "imgIOnloadBlocker.h"
#include "imgINotificationObserver.h"
#include "mozilla/Attributes.h"
class imgIContainer;
class nsIFrame;
class nsIDocument;
class nsPresContext;
class nsIURI;
class nsIPrincipal;
namespace mozilla {
namespace css {
struct ImageValue;
class ImageLoader final : public imgINotificationObserver,
public imgIOnloadBlocker
{
public:
typedef mozilla::css::ImageValue Image;
explicit ImageLoader(nsIDocument* aDocument)
: mDocument(aDocument),
mInClone(false)
{
MOZ_ASSERT(mDocument);
}
NS_DECL_ISUPPORTS
NS_DECL_IMGIONLOADBLOCKER
NS_DECL_IMGINOTIFICATIONOBSERVER
void DropDocumentReference();
void MaybeRegisterCSSImage(Image* aImage);
void DeregisterCSSImage(Image* aImage);
void AssociateRequestToFrame(imgIRequest* aRequest,
nsIFrame* aFrame);
void DisassociateRequestFromFrame(imgIRequest* aRequest,
nsIFrame* aFrame);
void DropRequestsForFrame(nsIFrame* aFrame);
void SetAnimationMode(uint16_t aMode);
// The prescontext for this ImageLoader's document. We need it to be passed
// in because this can be called during presentation destruction after the
// presshell pointer on the document has been cleared.
void ClearFrames(nsPresContext* aPresContext);
void LoadImage(nsIURI* aURI, nsIPrincipal* aPrincipal, nsIURI* aReferrer,
Image* aCSSValue);
void DestroyRequest(imgIRequest* aRequest);
void FlushUseCounters();
private:
~ImageLoader() {}
// We need to be able to look up the frames associated with a request (for
// delivering notifications) and the requests associated with a frame (when
// the frame goes away). Thus we maintain hashtables going both ways. These
// should always be in sync.
typedef nsTArray<nsIFrame*> FrameSet;
typedef nsTArray<nsCOMPtr<imgIRequest> > RequestSet;
typedef nsTHashtable<nsPtrHashKey<Image> > ImageHashSet;
typedef nsClassHashtable<nsISupportsHashKey,
FrameSet> RequestToFrameMap;
typedef nsClassHashtable<nsPtrHashKey<nsIFrame>,
RequestSet> FrameToRequestMap;
void AddImage(Image* aCSSImage);
void RemoveImage(Image* aCSSImage);
nsPresContext* GetPresContext();
void DoRedraw(FrameSet* aFrameSet, bool aForcePaint);
nsresult OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage);
nsresult OnFrameComplete(imgIRequest* aRequest);
nsresult OnImageIsAnimated(imgIRequest* aRequest);
nsresult OnFrameUpdate(imgIRequest* aRequest);
// A map of imgIRequests to the nsIFrames that are using them.
RequestToFrameMap mRequestToFrameMap;
// A map of nsIFrames to the imgIRequests they use.
FrameToRequestMap mFrameToRequestMap;
// A weak pointer to our document. Nulled out by DropDocumentReference.
nsIDocument* mDocument;
// The set of all nsCSSValue::Images (whether they're associated a frame or
// not). We'll need this when we go away to remove any requests associated
// with our document from those Images.
ImageHashSet mImages;
// Are we cloning? If so, ignore any notifications we get.
bool mInClone;
};
} // namespace css
} // namespace mozilla
#endif /* mozilla_css_ImageLoader_h___ */

72
layout/style/ImportRule.h Normal file
View file

@ -0,0 +1,72 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* class for CSS @import rules */
#ifndef mozilla_css_ImportRule_h__
#define mozilla_css_ImportRule_h__
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/Rule.h"
#include "nsIDOMCSSImportRule.h"
class nsMediaList;
class nsString;
namespace mozilla {
class CSSStyleSheet;
namespace css {
class ImportRule final : public Rule,
public nsIDOMCSSImportRule
{
public:
ImportRule(nsMediaList* aMedia, const nsString& aURLSpec,
uint32_t aLineNumber, uint32_t aColumnNumber);
private:
// for |Clone|
ImportRule(const ImportRule& aCopy);
~ImportRule();
public:
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ImportRule, mozilla::css::Rule)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
DECL_STYLE_RULE_INHERIT
#ifdef HAVE_CPP_AMBIGUITY_RESOLVING_USING
using Rule::GetStyleSheet; // unhide since nsIDOMCSSImportRule has its own GetStyleSheet
#endif
// Rule methods
#ifdef DEBUG
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
#endif
virtual int32_t GetType() const override;
virtual already_AddRefed<Rule> Clone() const override;
void SetSheet(CSSStyleSheet*);
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
// nsIDOMCSSRule interface
NS_DECL_NSIDOMCSSRULE
// nsIDOMCSSImportRule interface
NS_DECL_NSIDOMCSSIMPORTRULE
private:
nsString mURLSpec;
RefPtr<nsMediaList> mMedia;
RefPtr<CSSStyleSheet> mChildSheet;
};
} // namespace css
} // namespace mozilla
#endif /* mozilla_css_ImportRule_h__ */

View file

@ -0,0 +1,81 @@
/* -*- 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/IncrementalClearCOMRuleArray.h"
#include <algorithm> // For std::min
#include "nsCycleCollector.h"
#include "mozilla/DeferredFinalize.h"
#include "nsTArray.h"
#include "nsCCUncollectableMarker.h"
using namespace mozilla;
typedef nsCOMArray<css::Rule> RuleArray;
typedef nsTArray<RuleArray> RuleArrayArray;
// These methods are based on those in DeferredFinalizerImpl.
static void*
AppendRulesArrayPointer(void* aData, void* aObject)
{
RuleArrayArray* rulesArray = static_cast<RuleArrayArray*>(aData);
RuleArray* oldRules = static_cast<RuleArray*>(aObject);
if (!rulesArray) {
rulesArray = new RuleArrayArray();
}
RuleArray* newRules = rulesArray->AppendElement();
newRules->SwapElements(*oldRules);
return rulesArray;
}
// Remove up to |aSliceBudget| css::Rules from the arrays, starting at
// the end of the last array.
static bool
DeferredFinalizeRulesArray(uint32_t aSliceBudget, void* aData)
{
MOZ_ASSERT(aSliceBudget > 0, "nonsensical/useless call with aSliceBudget == 0");
RuleArrayArray* rulesArray = static_cast<RuleArrayArray*>(aData);
size_t newOuterLen = rulesArray->Length();
while (aSliceBudget > 0 && newOuterLen > 0) {
RuleArray& lastArray = rulesArray->ElementAt(newOuterLen - 1);
uint32_t innerLen = lastArray.Length();
uint32_t currSlice = std::min(innerLen, aSliceBudget);
uint32_t newInnerLen = innerLen - currSlice;
lastArray.TruncateLength(newInnerLen);
aSliceBudget -= currSlice;
if (newInnerLen == 0) {
newOuterLen -= 1;
}
}
rulesArray->TruncateLength(newOuterLen);
if (newOuterLen == 0) {
delete rulesArray;
return true;
}
return false;
}
void
IncrementalClearCOMRuleArray::Clear()
{
// Destroy the array incrementally if it is long and we
// haven't started shutting down.
if (Length() > 10 && nsCCUncollectableMarker::sGeneration) {
DeferredFinalize(AppendRulesArrayPointer, DeferredFinalizeRulesArray, this);
} else {
nsCOMArray<css::Rule>::Clear();
}
MOZ_ASSERT(Length() == 0);
}

View file

@ -0,0 +1,28 @@
/* -*- 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 mozilla_IncrementalClearCOMRuleArray_h
#define mozilla_IncrementalClearCOMRuleArray_h
#include "nsCOMArray.h"
namespace mozilla {
namespace css {
class Rule;
} // namespace css
class IncrementalClearCOMRuleArray : public nsCOMArray<css::Rule>
{
public:
IncrementalClearCOMRuleArray() {}
~IncrementalClearCOMRuleArray() { Clear(); }
void Clear();
};
} // namespace mozilla
#endif /* !defined(mozilla_IncrementalClearCOMRuleArray_h) */

View file

@ -0,0 +1,53 @@
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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 "LayerAnimationInfo.h"
#include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
namespace mozilla {
/* static */ const LayerAnimationInfo::Record LayerAnimationInfo::sRecords[] =
{ { eCSSProperty_transform,
nsDisplayItem::TYPE_TRANSFORM,
nsChangeHint_UpdateTransformLayer },
{ eCSSProperty_opacity,
nsDisplayItem::TYPE_OPACITY,
nsChangeHint_UpdateOpacityLayer } };
#ifdef DEBUG
/* static */ void
LayerAnimationInfo::Initialize()
{
for (const Record& record : sRecords) {
MOZ_ASSERT(nsCSSProps::PropHasFlags(record.mProperty,
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),
"CSS property with entry in LayerAnimation::sRecords does not "
"have the CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR flag");
}
// Check that every property with the flag for animating on the
// compositor has an entry in LayerAnimationInfo::sRecords.
for (nsCSSPropertyID prop = nsCSSPropertyID(0);
prop < eCSSProperty_COUNT;
prop = nsCSSPropertyID(prop + 1)) {
if (nsCSSProps::PropHasFlags(prop,
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR)) {
bool found = false;
for (const Record& record : sRecords) {
if (record.mProperty == prop) {
found = true;
break;
}
}
MOZ_ASSERT(found,
"CSS property with the CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR "
"flag does not have an entry in LayerAnimationInfo::sRecords");
}
}
}
#endif
} // namespace mozilla

View file

@ -0,0 +1,33 @@
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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 mozilla_LayerAnimationInfo_h
#define mozilla_LayerAnimationInfo_h
#include "nsChangeHint.h"
#include "nsCSSPropertyID.h"
#include "nsDisplayList.h" // For nsDisplayItem::Type
namespace mozilla {
struct LayerAnimationInfo {
#ifdef DEBUG
static void Initialize();
#endif
// For CSS properties that may be animated on a separate layer, represents
// a record of the corresponding layer type and change hint.
struct Record {
nsCSSPropertyID mProperty;
nsDisplayItem::Type mLayerType;
nsChangeHint mChangeHint;
};
static const size_t kRecords = 2;
static const Record sRecords[kRecords];
};
} // namespace mozilla
#endif /* !defined(mozilla_LayerAnimationInfo_h) */

2694
layout/style/Loader.cpp Normal file

File diff suppressed because it is too large Load diff

598
layout/style/Loader.h Normal file
View file

@ -0,0 +1,598 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* loading of CSS style sheets using the network APIs */
#ifndef mozilla_css_Loader_h
#define mozilla_css_Loader_h
#include "nsIPrincipal.h"
#include "nsAutoPtr.h"
#include "nsCompatibility.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDataHashtable.h"
#include "nsRefPtrHashtable.h"
#include "nsStringFwd.h"
#include "nsTArray.h"
#include "nsTObserverArray.h"
#include "nsURIHashKey.h"
#include "mozilla/Attributes.h"
#include "mozilla/CORSMode.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/StyleBackendType.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/net/ReferrerPolicy.h"
class nsICSSLoaderObserver;
class nsIConsoleReportCollector;
class nsIContent;
class nsIDocument;
class nsMediaList;
class nsIStyleSheetLinkingElement;
namespace mozilla {
namespace dom {
class Element;
} // namespace dom
} // namespace mozilla
namespace mozilla {
class URIPrincipalReferrerPolicyAndCORSModeHashKey : public nsURIHashKey
{
public:
typedef URIPrincipalReferrerPolicyAndCORSModeHashKey* KeyType;
typedef const URIPrincipalReferrerPolicyAndCORSModeHashKey* KeyTypePointer;
typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
explicit URIPrincipalReferrerPolicyAndCORSModeHashKey(const URIPrincipalReferrerPolicyAndCORSModeHashKey* aKey)
: nsURIHashKey(aKey->mKey),
mPrincipal(aKey->mPrincipal),
mCORSMode(aKey->mCORSMode),
mReferrerPolicy(aKey->mReferrerPolicy)
{
MOZ_COUNT_CTOR(URIPrincipalReferrerPolicyAndCORSModeHashKey);
}
URIPrincipalReferrerPolicyAndCORSModeHashKey(nsIURI* aURI,
nsIPrincipal* aPrincipal,
CORSMode aCORSMode,
ReferrerPolicy aReferrerPolicy)
: nsURIHashKey(aURI),
mPrincipal(aPrincipal),
mCORSMode(aCORSMode),
mReferrerPolicy(aReferrerPolicy)
{
MOZ_COUNT_CTOR(URIPrincipalReferrerPolicyAndCORSModeHashKey);
}
URIPrincipalReferrerPolicyAndCORSModeHashKey(const URIPrincipalReferrerPolicyAndCORSModeHashKey& toCopy)
: nsURIHashKey(toCopy),
mPrincipal(toCopy.mPrincipal),
mCORSMode(toCopy.mCORSMode),
mReferrerPolicy(toCopy.mReferrerPolicy)
{
MOZ_COUNT_CTOR(URIPrincipalReferrerPolicyAndCORSModeHashKey);
}
~URIPrincipalReferrerPolicyAndCORSModeHashKey()
{
MOZ_COUNT_DTOR(URIPrincipalReferrerPolicyAndCORSModeHashKey);
}
URIPrincipalReferrerPolicyAndCORSModeHashKey* GetKey() const {
return const_cast<URIPrincipalReferrerPolicyAndCORSModeHashKey*>(this);
}
const URIPrincipalReferrerPolicyAndCORSModeHashKey* GetKeyPointer() const { return this; }
bool KeyEquals(const URIPrincipalReferrerPolicyAndCORSModeHashKey* aKey) const {
if (!nsURIHashKey::KeyEquals(aKey->mKey)) {
return false;
}
if (!mPrincipal != !aKey->mPrincipal) {
// One or the other has a principal, but not both... not equal
return false;
}
if (mCORSMode != aKey->mCORSMode) {
// Different CORS modes; we don't match
return false;
}
if (mReferrerPolicy != aKey->mReferrerPolicy) {
// Different ReferrerPolicy; we don't match
return false;
}
bool eq;
return !mPrincipal ||
(NS_SUCCEEDED(mPrincipal->Equals(aKey->mPrincipal, &eq)) && eq);
}
static const URIPrincipalReferrerPolicyAndCORSModeHashKey*
KeyToPointer(URIPrincipalReferrerPolicyAndCORSModeHashKey* aKey) { return aKey; }
static PLDHashNumber HashKey(const URIPrincipalReferrerPolicyAndCORSModeHashKey* aKey) {
return nsURIHashKey::HashKey(aKey->mKey);
}
nsIURI* GetURI() const { return nsURIHashKey::GetKey(); }
enum { ALLOW_MEMMOVE = true };
protected:
nsCOMPtr<nsIPrincipal> mPrincipal;
CORSMode mCORSMode;
ReferrerPolicy mReferrerPolicy;
};
namespace css {
class SheetLoadData;
class ImportRule;
/*********************
* Style sheet reuse *
*********************/
class MOZ_RAII LoaderReusableStyleSheets
{
public:
LoaderReusableStyleSheets()
{
}
/**
* Look for a reusable sheet (see AddReusableSheet) matching the
* given URL. If found, set aResult, remove the reused sheet from
* the internal list, and return true. If not found, return false;
* in this case, aResult is not modified.
*
* @param aURL the url to match
* @param aResult [out] the style sheet which can be reused
*/
bool FindReusableStyleSheet(nsIURI* aURL, RefPtr<CSSStyleSheet>& aResult);
/**
* Indicate that a certain style sheet is available for reuse if its
* URI matches the URI of an @import. Sheets should be added in the
* opposite order in which they are intended to be reused.
*
* @param aSheet the sheet which can be reused
*/
void AddReusableSheet(CSSStyleSheet* aSheet) {
mReusableSheets.AppendElement(aSheet);
}
private:
LoaderReusableStyleSheets(const LoaderReusableStyleSheets&) = delete;
LoaderReusableStyleSheets& operator=(const LoaderReusableStyleSheets&) = delete;
// The sheets that can be reused.
nsTArray<RefPtr<CSSStyleSheet>> mReusableSheets;
};
/***********************************************************************
* Enum that describes the state of the sheet returned by CreateSheet. *
***********************************************************************/
enum StyleSheetState {
eSheetStateUnknown = 0,
eSheetNeedsParser,
eSheetPending,
eSheetLoading,
eSheetComplete
};
class Loader final {
typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
public:
explicit Loader(StyleBackendType aType);
explicit Loader(nsIDocument*);
private:
// Private destructor, to discourage deletion outside of Release():
~Loader();
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Loader)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Loader)
void DropDocumentReference(); // notification that doc is going away
void SetCompatibilityMode(nsCompatibility aCompatMode)
{ mCompatMode = aCompatMode; }
nsCompatibility GetCompatibilityMode() { return mCompatMode; }
nsresult SetPreferredSheet(const nsAString& aTitle);
// XXXbz sort out what the deal is with events! When should they fire?
/**
* Load an inline style sheet. If a successful result is returned and
* *aCompleted is false, then aObserver is guaranteed to be notified
* asynchronously once the sheet is marked complete. If an error is
* returned, or if *aCompleted is true, aObserver will not be notified. In
* addition to parsing the sheet, this method will insert it into the
* stylesheet list of this CSSLoader's document.
*
* @param aElement the element linking to the stylesheet. This must not be
* null and must implement nsIStyleSheetLinkingElement.
* @param aBuffer the stylesheet data
* @param aLineNumber the line number at which the stylesheet data started.
* @param aTitle the title of the sheet.
* @param aMedia the media string for the sheet.
* @param aObserver the observer to notify when the load completes.
* May be null.
* @param [out] aCompleted whether parsing of the sheet completed.
* @param [out] aIsAlternate whether the stylesheet ended up being an
* alternate sheet.
*/
nsresult LoadInlineStyle(nsIContent* aElement,
const nsAString& aBuffer,
uint32_t aLineNumber,
const nsAString& aTitle,
const nsAString& aMedia,
mozilla::dom::Element* aScopeElement,
nsICSSLoaderObserver* aObserver,
bool* aCompleted,
bool* aIsAlternate);
/**
* Load a linked (document) stylesheet. If a successful result is returned,
* aObserver is guaranteed to be notified asynchronously once the sheet is
* loaded and marked complete. If an error is returned, aObserver will not
* be notified. In addition to loading the sheet, this method will insert it
* into the stylesheet list of this CSSLoader's document.
*
* @param aElement the element linking to the the stylesheet. May be null.
* @param aURL the URL of the sheet.
* @param aTitle the title of the sheet.
* @param aMedia the media string for the sheet.
* @param aHasAlternateRel whether the rel for this link included
* "alternate".
* @param aCORSMode the CORS mode for this load.
* @param aObserver the observer to notify when the load completes.
* May be null.
* @param [out] aIsAlternate whether the stylesheet actually ended up beinga
* an alternate sheet. Note that this need not match
* aHasAlternateRel.
*/
nsresult LoadStyleLink(nsIContent* aElement,
nsIURI* aURL,
const nsAString& aTitle,
const nsAString& aMedia,
bool aHasAlternateRel,
CORSMode aCORSMode,
ReferrerPolicy aReferrerPolicy,
const nsAString& aIntegrity,
nsICSSLoaderObserver* aObserver,
bool* aIsAlternate);
/**
* Load a child (@import-ed) style sheet. In addition to loading the sheet,
* this method will insert it into the child sheet list of aParentSheet. If
* there is no sheet currently being parsed and the child sheet is not
* complete when this method returns, then when the child sheet becomes
* complete aParentSheet will be QIed to nsICSSLoaderObserver and
* asynchronously notified, just like for LoadStyleLink. Note that if the
* child sheet is already complete when this method returns, no
* nsICSSLoaderObserver notification will be sent.
*
* @param aParentSheet the parent of this child sheet
* @param aURL the URL of the child sheet
* @param aMedia the already-parsed media list for the child sheet
* @param aRule the @import rule importing this child. This is used to
* properly order the child sheet list of aParentSheet.
* @param aSavedSheets any saved style sheets which could be reused
* for this load
*/
nsresult LoadChildSheet(StyleSheet* aParentSheet,
nsIURI* aURL,
nsMediaList* aMedia,
ImportRule* aRule,
LoaderReusableStyleSheets* aSavedSheets);
/**
* Synchronously load and return the stylesheet at aURL. Any child sheets
* will also be loaded synchronously. Note that synchronous loads over some
* protocols may involve spinning up a new event loop, so use of this method
* does NOT guarantee not receiving any events before the sheet loads. This
* method can be used to load sheets not associated with a document.
*
* @param aURL the URL of the sheet to load
* @param aParsingMode the mode in which to parse the sheet
* (see comments at enum SheetParsingMode, above).
* @param aUseSystemPrincipal if true, give the resulting sheet the system
* principal no matter where it's being loaded from.
* @param [out] aSheet the loaded, complete sheet.
*
* NOTE: At the moment, this method assumes the sheet will be UTF-8, but
* ideally it would allow arbitrary encodings. Callers should NOT depend on
* non-UTF8 sheets being treated as UTF-8 by this method.
*
* NOTE: A successful return from this method doesn't indicate anything about
* whether the data could be parsed as CSS and doesn't indicate anything
* about the status of child sheets of the returned sheet.
*/
nsresult LoadSheetSync(nsIURI* aURL,
SheetParsingMode aParsingMode,
bool aUseSystemPrincipal,
RefPtr<StyleSheet>* aSheet);
/**
* As above, but defaults aParsingMode to eAuthorSheetFeatures and
* aUseSystemPrincipal to false.
*/
nsresult LoadSheetSync(nsIURI* aURL, RefPtr<StyleSheet>* aSheet) {
return LoadSheetSync(aURL, eAuthorSheetFeatures, false, aSheet);
}
/**
* Asynchronously load the stylesheet at aURL. If a successful result is
* returned, aObserver is guaranteed to be notified asynchronously once the
* sheet is loaded and marked complete. This method can be used to load
* sheets not associated with a document. This method cannot be used to
* load user or agent sheets.
*
* @param aURL the URL of the sheet to load
* @param aOriginPrincipal the principal to use for security checks. This
* can be null to indicate that these checks should
* be skipped.
* @param aCharset the encoding to use for converting the sheet data
* from bytes to Unicode. May be empty to indicate that the
* charset of the CSSLoader's document should be used. This
* is only used if neither the network transport nor the
* sheet itself indicate an encoding.
* @param aObserver the observer to notify when the load completes.
* Must not be null.
* @param [out] aSheet the sheet to load. Note that the sheet may well
* not be loaded by the time this method returns.
*/
nsresult LoadSheet(nsIURI* aURL,
nsIPrincipal* aOriginPrincipal,
const nsCString& aCharset,
nsICSSLoaderObserver* aObserver,
RefPtr<StyleSheet>* aSheet);
/**
* Same as above, to be used when the caller doesn't care about the
* not-yet-loaded sheet.
*/
nsresult LoadSheet(nsIURI* aURL,
bool aIsPreload,
nsIPrincipal* aOriginPrincipal,
const nsCString& aCharset,
nsICSSLoaderObserver* aObserver,
CORSMode aCORSMode = CORS_NONE,
ReferrerPolicy aReferrerPolicy = mozilla::net::RP_Default,
const nsAString& aIntegrity = EmptyString());
/**
* Stop loading all sheets. All nsICSSLoaderObservers involved will be
* notified with NS_BINDING_ABORTED as the status, possibly synchronously.
*/
nsresult Stop(void);
/**
* nsresult Loader::StopLoadingSheet(nsIURI* aURL), which notifies the
* nsICSSLoaderObserver with NS_BINDING_ABORTED, was removed in Bug 556446.
* It can be found in revision 2c44a32052ad.
*/
/**
* Whether the loader is enabled or not.
* When disabled, processing of new styles is disabled and an attempt
* to do so will fail with a return code of
* NS_ERROR_NOT_AVAILABLE. Note that this DOES NOT disable
* currently loading styles or already processed styles.
*/
bool GetEnabled() { return mEnabled; }
void SetEnabled(bool aEnabled) { mEnabled = aEnabled; }
/**
* Get the document we live for. May return null.
*/
nsIDocument* GetDocument() const { return mDocument; }
/**
* Return true if this loader has pending loads (ones that would send
* notifications to an nsICSSLoaderObserver attached to this loader).
* If called from inside nsICSSLoaderObserver::StyleSheetLoaded, this will
* return false if and only if that is the last StyleSheetLoaded
* notification the CSSLoader knows it's going to send. In other words, if
* two sheets load at once (via load coalescing, e.g.), HasPendingLoads()
* will return true during notification for the first one, and false
* during notification for the second one.
*/
bool HasPendingLoads();
/**
* Add an observer to this loader. The observer will be notified
* for all loads that would have notified their own observers (even
* if those loads don't have observers attached to them).
* Load-specific observers will be notified before generic
* observers. The loader holds a reference to the observer.
*
* aObserver must not be null.
*/
nsresult AddObserver(nsICSSLoaderObserver* aObserver);
/**
* Remove an observer added via AddObserver.
*/
void RemoveObserver(nsICSSLoaderObserver* aObserver);
// These interfaces are public only for the benefit of static functions
// within nsCSSLoader.cpp.
// IsAlternate can change our currently selected style set if none
// is selected and aHasAlternateRel is false.
bool IsAlternate(const nsAString& aTitle, bool aHasAlternateRel);
typedef nsTArray<RefPtr<SheetLoadData> > LoadDataArray;
// Measure our size.
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
// Marks all the sheets at the given URI obsolete, and removes them from the
// cache.
nsresult ObsoleteSheet(nsIURI* aURI);
private:
friend class SheetLoadData;
nsresult CheckContentPolicy(nsIPrincipal* aSourcePrincipal,
nsIURI* aTargetURI,
nsISupports* aContext,
bool aIsPreload);
// For inline style, the aURI param is null, but the aLinkingContent
// must be non-null then. The loader principal must never be null
// if aURI is not null.
// *aIsAlternate is set based on aTitle and aHasAlternateRel.
nsresult CreateSheet(nsIURI* aURI,
nsIContent* aLinkingContent,
nsIPrincipal* aLoaderPrincipal,
css::SheetParsingMode aParsingMode,
CORSMode aCORSMode,
ReferrerPolicy aReferrerPolicy,
const nsAString& aIntegrity,
bool aSyncLoad,
bool aHasAlternateRel,
const nsAString& aTitle,
StyleSheetState& aSheetState,
bool *aIsAlternate,
RefPtr<StyleSheet>* aSheet);
// Pass in either a media string or the nsMediaList from the
// CSSParser. Don't pass both.
// This method will set the sheet's enabled state based on isAlternate
void PrepareSheet(StyleSheet* aSheet,
const nsAString& aTitle,
const nsAString& aMediaString,
nsMediaList* aMediaList,
dom::Element* aScopeElement,
bool isAlternate);
nsresult InsertSheetInDoc(StyleSheet* aSheet,
nsIContent* aLinkingContent,
nsIDocument* aDocument);
nsresult InsertChildSheet(StyleSheet* aSheet,
StyleSheet* aParentSheet,
ImportRule* aParentRule);
nsresult InternalLoadNonDocumentSheet(nsIURI* aURL,
bool aIsPreload,
SheetParsingMode aParsingMode,
bool aUseSystemPrincipal,
nsIPrincipal* aOriginPrincipal,
const nsCString& aCharset,
RefPtr<StyleSheet>* aSheet,
nsICSSLoaderObserver* aObserver,
CORSMode aCORSMode = CORS_NONE,
ReferrerPolicy aReferrerPolicy = mozilla::net::RP_Default,
const nsAString& aIntegrity = EmptyString());
// Post a load event for aObserver to be notified about aSheet. The
// notification will be sent with status NS_OK unless the load event is
// canceled at some point (in which case it will be sent with
// NS_BINDING_ABORTED). aWasAlternate indicates the state when the load was
// initiated, not the state at some later time. aURI should be the URI the
// sheet was loaded from (may be null for inline sheets). aElement is the
// owning element for this sheet.
nsresult PostLoadEvent(nsIURI* aURI,
StyleSheet* aSheet,
nsICSSLoaderObserver* aObserver,
bool aWasAlternate,
nsIStyleSheetLinkingElement* aElement);
// Start the loads of all the sheets in mPendingDatas
void StartAlternateLoads();
// Handle an event posted by PostLoadEvent
void HandleLoadEvent(SheetLoadData* aEvent);
// Note: LoadSheet is responsible for releasing aLoadData and setting the
// sheet to complete on failure.
nsresult LoadSheet(SheetLoadData* aLoadData,
StyleSheetState aSheetState,
bool aIsPreLoad);
// Parse the stylesheet in aLoadData. The sheet data comes from aInput.
// Set aCompleted to true if the parse finished, false otherwise (e.g. if the
// sheet had an @import). If aCompleted is true when this returns, then
// ParseSheet also called SheetComplete on aLoadData.
nsresult ParseSheet(const nsAString& aInput,
SheetLoadData* aLoadData,
bool& aCompleted);
// The load of the sheet in aLoadData is done, one way or another. Do final
// cleanup, including releasing aLoadData.
void SheetComplete(SheetLoadData* aLoadData, nsresult aStatus);
// The guts of SheetComplete. This may be called recursively on parent datas
// or datas that had glommed on to a single load. The array is there so load
// datas whose observers need to be notified can be added to it.
void DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
LoadDataArray& aDatasToNotify);
StyleBackendType GetStyleBackendType() const;
struct Sheets {
nsBaseHashtable<URIPrincipalReferrerPolicyAndCORSModeHashKey,
RefPtr<StyleSheet>,
StyleSheet*> mCompleteSheets;
nsDataHashtable<URIPrincipalReferrerPolicyAndCORSModeHashKey, SheetLoadData*>
mLoadingDatas; // weak refs
nsDataHashtable<URIPrincipalReferrerPolicyAndCORSModeHashKey, SheetLoadData*>
mPendingDatas; // weak refs
};
nsAutoPtr<Sheets> mSheets;
// We're not likely to have many levels of @import... But likely to have
// some. Allocate some storage, what the hell.
AutoTArray<SheetLoadData*, 8> mParsingDatas;
// The array of posted stylesheet loaded events (SheetLoadDatas) we have.
// Note that these are rare.
LoadDataArray mPostedEvents;
// Our array of "global" observers
nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver> > mObservers;
// This reference is nulled by the Document in it's destructor through
// DropDocumentReference().
nsIDocument* MOZ_NON_OWNING_REF mDocument; // the document we live for
// Number of datas still waiting to be notified on if we're notifying on a
// whole bunch at once (e.g. in one of the stop methods). This is used to
// make sure that HasPendingLoads() won't return false until we're notifying
// on the last data we're working with.
uint32_t mDatasToNotifyOn;
nsCompatibility mCompatMode;
nsString mPreferredSheet; // title of preferred sheet
// Set explicitly when the Loader(StyleBackendType) constructor is used, or
// taken from the document when the Loader(nsIDocument*) constructor is used.
mozilla::Maybe<StyleBackendType> mStyleBackendType;
bool mEnabled; // is enabled to load new styles
nsCOMPtr<nsIConsoleReportCollector> mReporter;
#ifdef DEBUG
bool mSyncCallback;
#endif
};
} // namespace css
} // namespace mozilla
#endif /* mozilla_css_Loader_h */

16
layout/style/Makefile.in Normal file
View file

@ -0,0 +1,16 @@
# 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/.
# TODO This list should be emitted to a .pp file via
# GenerateCSSPropsGenerated.py (bug 1281614)
csspropsidlnames_dependencies = \
$(srcdir)/nsCSSPropList.h \
$(srcdir)/nsCSSPropAliasList.h \
$(srcdir)/nsCSSPropsGenerated.inc.in \
$(srcdir)/PythonCSSProps.h \
$(srcdir)/GenerateCSSPropsGenerated.py \
$(GLOBAL_DEPS) \
$(NULL)
nsCSSPropsGenerated.inc : $(csspropsidlnames_dependencies)

View file

@ -0,0 +1,207 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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/. */
/* implements DOM interface for querying and observing media queries */
#include "mozilla/dom/MediaQueryList.h"
#include "nsPresContext.h"
#include "nsIMediaList.h"
#include "nsCSSParser.h"
#include "nsIDocument.h"
namespace mozilla {
namespace dom {
MediaQueryList::MediaQueryList(nsIDocument *aDocument,
const nsAString &aMediaQueryList)
: mDocument(aDocument),
mMediaList(new nsMediaList),
mMatchesValid(false)
{
PR_INIT_CLIST(this);
nsCSSParser parser;
parser.ParseMediaList(aMediaQueryList, nullptr, 0, mMediaList, false);
}
MediaQueryList::~MediaQueryList()
{
if (mDocument) {
PR_REMOVE_LINK(this);
}
}
NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaQueryList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallbacks)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaQueryList)
if (tmp->mDocument) {
PR_REMOVE_LINK(tmp);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
}
tmp->RemoveAllListeners();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(MediaQueryList)
NS_INTERFACE_MAP_BEGIN(MediaQueryList)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(MediaQueryList)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaQueryList)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaQueryList)
void
MediaQueryList::GetMedia(nsAString &aMedia)
{
mMediaList->GetText(aMedia);
}
bool
MediaQueryList::Matches()
{
if (!mMatchesValid) {
MOZ_ASSERT(!HasListeners(),
"when listeners present, must keep mMatches current");
RecomputeMatches();
}
return mMatches;
}
void
MediaQueryList::AddListener(MediaQueryListListener& aListener)
{
if (!HasListeners()) {
// When we have listeners, the pres context owns a reference to
// this. This is a cyclic reference that can only be broken by
// cycle collection.
NS_ADDREF_THIS();
}
if (!mMatchesValid) {
MOZ_ASSERT(!HasListeners(),
"when listeners present, must keep mMatches current");
RecomputeMatches();
}
for (uint32_t i = 0; i < mCallbacks.Length(); ++i) {
if (aListener == *mCallbacks[i]) {
// Already registered
return;
}
}
if (!mCallbacks.AppendElement(&aListener, fallible)) {
if (!HasListeners()) {
// Append failed; undo the AddRef above.
NS_RELEASE_THIS();
}
}
}
void
MediaQueryList::RemoveListener(MediaQueryListListener& aListener)
{
for (uint32_t i = 0; i < mCallbacks.Length(); ++i) {
if (aListener == *mCallbacks[i]) {
mCallbacks.RemoveElementAt(i);
if (!HasListeners()) {
// See NS_ADDREF_THIS() in AddListener.
NS_RELEASE_THIS();
}
break;
}
}
}
void
MediaQueryList::RemoveAllListeners()
{
bool hadListeners = HasListeners();
mCallbacks.Clear();
if (hadListeners) {
// See NS_ADDREF_THIS() in AddListener.
NS_RELEASE_THIS();
}
}
void
MediaQueryList::RecomputeMatches()
{
if (!mDocument) {
return;
}
if (mDocument->GetParentDocument()) {
// Flush frames on the parent so our prescontext will get
// recreated as needed.
mDocument->GetParentDocument()->FlushPendingNotifications(Flush_Frames);
// That might have killed our document, so recheck that.
if (!mDocument) {
return;
}
}
nsIPresShell* shell = mDocument->GetShell();
if (!shell) {
// XXXbz What's the right behavior here? Spec doesn't say.
return;
}
nsPresContext* presContext = shell->GetPresContext();
if (!presContext) {
// XXXbz What's the right behavior here? Spec doesn't say.
return;
}
mMatches = mMediaList->Matches(presContext, nullptr);
mMatchesValid = true;
}
void
MediaQueryList::MediumFeaturesChanged(
nsTArray<HandleChangeData>& aListenersToNotify)
{
mMatchesValid = false;
if (HasListeners()) {
bool oldMatches = mMatches;
RecomputeMatches();
if (mMatches != oldMatches) {
for (uint32_t i = 0, i_end = mCallbacks.Length(); i != i_end; ++i) {
HandleChangeData *d = aListenersToNotify.AppendElement(fallible);
if (d) {
d->mql = this;
d->callback = mCallbacks[i];
}
}
}
}
}
nsISupports*
MediaQueryList::GetParentObject() const
{
return mDocument;
}
JSObject*
MediaQueryList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return MediaQueryListBinding::Wrap(aCx, this, aGivenProto);
}
} // namespace dom
} // namespace mozilla

View file

@ -0,0 +1,93 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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/. */
/* implements DOM interface for querying and observing media queries */
#ifndef mozilla_dom_MediaQueryList_h
#define mozilla_dom_MediaQueryList_h
#include "nsISupports.h"
#include "nsCycleCollectionParticipant.h"
#include "nsCOMPtr.h"
#include "nsTArray.h"
#include "prclist.h"
#include "mozilla/Attributes.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/MediaQueryListBinding.h"
class nsIDocument;
class nsMediaList;
namespace mozilla {
namespace dom {
class MediaQueryList final : public nsISupports,
public nsWrapperCache,
public PRCList
{
public:
// The caller who constructs is responsible for calling Evaluate
// before calling any other methods.
MediaQueryList(nsIDocument *aDocument,
const nsAString &aMediaQueryList);
private:
~MediaQueryList();
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaQueryList)
nsISupports* GetParentObject() const;
struct HandleChangeData {
RefPtr<MediaQueryList> mql;
RefPtr<mozilla::dom::MediaQueryListListener> callback;
};
// Appends listeners that need notification to aListenersToNotify
void MediumFeaturesChanged(nsTArray<HandleChangeData>& aListenersToNotify);
bool HasListeners() const { return !mCallbacks.IsEmpty(); }
void RemoveAllListeners();
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// WebIDL methods
void GetMedia(nsAString& aMedia);
bool Matches();
void AddListener(mozilla::dom::MediaQueryListListener& aListener);
void RemoveListener(mozilla::dom::MediaQueryListListener& aListener);
private:
void RecomputeMatches();
// We only need a pointer to the document to support lazy
// reevaluation following dynamic changes. However, this lazy
// reevaluation is perhaps somewhat important, since some usage
// patterns may involve the creation of large numbers of
// MediaQueryList objects which almost immediately become garbage
// (after a single call to the .matches getter).
//
// This pointer does make us a little more dependent on cycle
// collection.
//
// We have a non-null mDocument for our entire lifetime except
// after cycle collection unlinking. Having a non-null mDocument
// is equivalent to being in that document's mDOMMediaQueryLists
// linked list.
nsCOMPtr<nsIDocument> mDocument;
RefPtr<nsMediaList> mMediaList;
bool mMatches;
bool mMatchesValid;
nsTArray<RefPtr<mozilla::dom::MediaQueryListListener>> mCallbacks;
};
} // namespace dom
} // namespace mozilla
#endif /* !defined(mozilla_dom_MediaQueryList_h) */

View file

@ -0,0 +1,70 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* class for CSS @namespace rules */
#ifndef mozilla_css_NameSpaceRule_h__
#define mozilla_css_NameSpaceRule_h__
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/Rule.h"
#include "nsIDOMCSSRule.h"
class nsIAtom;
// IID for the NameSpaceRule class {f0b0dbe1-5031-4a21-b06a-dc141ef2af98}
#define NS_CSS_NAMESPACE_RULE_IMPL_CID \
{0xf0b0dbe1, 0x5031, 0x4a21, {0xb0, 0x6a, 0xdc, 0x14, 0x1e, 0xf2, 0xaf, 0x98}}
namespace mozilla {
namespace css {
class NameSpaceRule final : public Rule,
public nsIDOMCSSRule
{
public:
NameSpaceRule(nsIAtom* aPrefix, const nsString& aURLSpec,
uint32_t aLineNumber, uint32_t aColumnNumber);
private:
// for |Clone|
NameSpaceRule(const NameSpaceRule& aCopy);
~NameSpaceRule();
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_NAMESPACE_RULE_IMPL_CID)
NS_DECL_ISUPPORTS
// Rule methods
DECL_STYLE_RULE_INHERIT
#ifdef DEBUG
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
#endif
virtual int32_t GetType() const override;
virtual already_AddRefed<Rule> Clone() const override;
nsIAtom* GetPrefix() const { return mPrefix; }
void GetURLSpec(nsString& aURLSpec) const { aURLSpec = mURLSpec; }
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
override MOZ_MUST_OVERRIDE;
// nsIDOMCSSRule interface
NS_DECL_NSIDOMCSSRULE
private:
nsCOMPtr<nsIAtom> mPrefix;
nsString mURLSpec;
};
NS_DEFINE_STATIC_IID_ACCESSOR(NameSpaceRule, NS_CSS_NAMESPACE_RULE_IMPL_CID)
} // namespace css
} // namespace mozilla
#endif /* mozilla_css_NameSpaceRule_h__ */

View file

@ -0,0 +1,41 @@
/* A file meant as input to the preprocessor only */
/* DO_PROP serves as an extra level of indirection to allow expansion
of CSS_PROP_DOMPROP_PREFIXED */
[
#define PROP_STRINGIFY_INTERNAL(X) #X
#define PROP_STRINGIFY(X) PROP_STRINGIFY_INTERNAL(X)
#define DO_PROP(name, method, id, flags, pref, proptype) \
[ #name, #method, #id, PROP_STRINGIFY(flags), pref, proptype ],
#define CSS_PROP(name, id, method, flags, pref, parsevariant, kwtable, \
stylestruct, stylestructoffset, animtype) \
DO_PROP(name, method, id, flags, pref, "longhand")
#define CSS_PROP_SHORTHAND(name, id, method, flags, pref) \
DO_PROP(name, method, id, flags, pref, "shorthand")
#define CSS_PROP_LOGICAL(name, id, method, flags, pref, parsevariant, kwtable, \
group, stylestruct, stylestructoffset, animtype) \
DO_PROP(name, method, id, flags, pref, "logical")
#define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) publicname_
#include "nsCSSPropList.h"
#undef CSS_PROP_PUBLIC_OR_PRIVATE
#undef CSS_PROP_LOGICAL
#undef CSS_PROP_SHORTHAND
#undef CSS_PROP
#define CSS_PROP_ALIAS(name, id, method, pref) \
DO_PROP(name, method, id, 0, pref, "alias")
#include "nsCSSPropAliasList.h"
#undef CSS_PROP_ALIAS
#undef DO_PROP
#undef PROP_STRINGIFY
#undef PROP_STRINGIFY_INTERNAL
]

141
layout/style/Rule.h Normal file
View file

@ -0,0 +1,141 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* base class for all rule types in a CSS style sheet */
#ifndef mozilla_css_Rule_h___
#define mozilla_css_Rule_h___
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/MemoryReporting.h"
#include "nsISupports.h"
#include "nsIDOMCSSRule.h"
class nsIDocument;
struct nsRuleData;
template<class T> struct already_AddRefed;
class nsHTMLCSSStyleSheet;
namespace mozilla {
namespace css {
class GroupRule;
#define DECL_STYLE_RULE_INHERIT_NO_DOMRULE \
/* nothing */
#define DECL_STYLE_RULE_INHERIT \
DECL_STYLE_RULE_INHERIT_NO_DOMRULE \
virtual nsIDOMCSSRule* GetDOMRule() override; \
virtual nsIDOMCSSRule* GetExistingDOMRule() override;
class Rule : public nsISupports {
protected:
Rule(uint32_t aLineNumber, uint32_t aColumnNumber)
: mSheet(nullptr),
mParentRule(nullptr),
mLineNumber(aLineNumber),
mColumnNumber(aColumnNumber)
{
}
Rule(const Rule& aCopy)
: mSheet(aCopy.mSheet),
mParentRule(aCopy.mParentRule),
mLineNumber(aCopy.mLineNumber),
mColumnNumber(aCopy.mColumnNumber)
{
}
virtual ~Rule() {}
public:
#ifdef DEBUG
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const = 0;
#endif
// The constants in this list must maintain the following invariants:
// If a rule of type N must appear before a rule of type M in stylesheets
// then N < M
// Note that CSSStyleSheet::RebuildChildList assumes that no other kinds of
// rules can come between two rules of type IMPORT_RULE.
enum {
UNKNOWN_RULE = 0,
CHARSET_RULE,
IMPORT_RULE,
NAMESPACE_RULE,
STYLE_RULE,
MEDIA_RULE,
FONT_FACE_RULE,
PAGE_RULE,
KEYFRAME_RULE,
KEYFRAMES_RULE,
DOCUMENT_RULE,
SUPPORTS_RULE,
FONT_FEATURE_VALUES_RULE,
COUNTER_STYLE_RULE
};
virtual int32_t GetType() const = 0;
CSSStyleSheet* GetStyleSheet() const { return mSheet; }
// Return the document the rule lives in, if any
nsIDocument* GetDocument() const
{
CSSStyleSheet* sheet = GetStyleSheet();
return sheet ? sheet->GetDocument() : nullptr;
}
virtual void SetStyleSheet(CSSStyleSheet* aSheet);
void SetParentRule(GroupRule* aRule) {
// We don't reference count this up reference. The group rule
// will tell us when it's going away or when we're detached from
// it.
mParentRule = aRule;
}
uint32_t GetLineNumber() const { return mLineNumber; }
uint32_t GetColumnNumber() const { return mColumnNumber; }
/**
* Clones |this|. Never returns nullptr.
*/
virtual already_AddRefed<Rule> Clone() const = 0;
// Note that this returns null for inline style rules since they aren't
// supposed to have a DOM rule representation (and our code wouldn't work).
virtual nsIDOMCSSRule* GetDOMRule() = 0;
// Like GetDOMRule(), but won't create one if we don't have one yet
virtual nsIDOMCSSRule* GetExistingDOMRule() = 0;
// to implement methods on nsIDOMCSSRule
nsresult GetParentRule(nsIDOMCSSRule** aParentRule);
nsresult GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet);
Rule* GetCSSRule();
// This is pure virtual because all of Rule's data members are non-owning and
// thus measured elsewhere.
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
const MOZ_MUST_OVERRIDE = 0;
protected:
// This is sometimes null (e.g., for style attributes).
CSSStyleSheet* mSheet;
// When the parent GroupRule is destroyed, it will call SetParentRule(nullptr)
// on this object. (Through SetParentRuleReference);
GroupRule* MOZ_NON_OWNING_REF mParentRule;
// Keep the same type so that MSVC packs them.
uint32_t mLineNumber;
uint32_t mColumnNumber;
};
} // namespace css
} // namespace mozilla
#endif /* mozilla_css_Rule_h___ */

View file

@ -0,0 +1,53 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=78: */
/* 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/. */
/*
* an object that stores the result of determining whether a style struct that
* was computed can be cached in the rule tree, and if so, what the cache
* key is
*/
#include "RuleNodeCacheConditions.h"
#include "nsStyleContext.h"
#include "WritingModes.h"
using namespace mozilla;
bool
RuleNodeCacheConditions::Matches(nsStyleContext* aStyleContext) const
{
MOZ_ASSERT(Cacheable());
if ((mBits & eHaveFontSize) &&
mFontSize != aStyleContext->StyleFont()->mFont.size) {
return false;
}
if ((mBits & eHaveWritingMode) &&
(GetWritingMode() != WritingMode(aStyleContext).GetBits())) {
return false;
}
return true;
}
#ifdef DEBUG
void
RuleNodeCacheConditions::List() const
{
printf("{ ");
bool first = true;
if (mBits & eHaveFontSize) {
printf("FontSize(%d)", mFontSize);
first = false;
}
if (mBits & eHaveWritingMode) {
if (!first) {
printf(", ");
}
printf("WritingMode(0x%x)", GetWritingMode());
}
printf(" }");
}
#endif

View file

@ -0,0 +1,156 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/*
* an object that stores the result of determining whether a style struct that
* was computed can be cached in the rule tree, and if so, what the conditions
* it relies on are
*/
#ifndef RuleNodeCacheConditions_h_
#define RuleNodeCacheConditions_h_
#include "mozilla/Attributes.h"
#include "nsCoord.h"
#include "nsTArray.h"
class nsStyleContext;
namespace mozilla {
/**
* nsRuleNodeCacheConditions is used to store information about whether
* we can store a style struct that we're computing in the rule tree.
*
* For inherited structs (i.e., structs with inherited properties), we
* cache the struct in the rule tree if it does not depend on any data
* in the style context tree, and otherwise store it in the style
* context tree. This means that for inherited structs, setting any
* conditions is equivalent to making the struct uncacheable.
*
* For reset structs (i.e., structs with non-inherited properties), we
* are also able to cache structs in the rule tree conditionally on
* certain common conditions. For these structs, setting conditions
* (SetFontSizeDependency, SetWritingModeDependency) instead causes the
* struct to be stored, with the condition, in the rule tree.
*/
class RuleNodeCacheConditions
{
public:
RuleNodeCacheConditions()
: mFontSize(0), mBits(0) {}
RuleNodeCacheConditions(const RuleNodeCacheConditions& aOther)
: mFontSize(aOther.mFontSize), mBits(aOther.mBits) {}
RuleNodeCacheConditions& operator=(const RuleNodeCacheConditions& aOther)
{
mFontSize = aOther.mFontSize;
mBits = aOther.mBits;
return *this;
}
bool operator==(const RuleNodeCacheConditions& aOther) const
{
return mFontSize == aOther.mFontSize &&
mBits == aOther.mBits;
}
bool operator!=(const RuleNodeCacheConditions& aOther) const
{
return !(*this == aOther);
}
bool Matches(nsStyleContext* aStyleContext) const;
/**
* Record that the data being computed depend on the font-size
* property of the element for which they are being computed.
*
* Note that we sometimes actually call this when there is a
* dependency on the font-size property of the parent element, but we
* only do so while computing inherited structs (nsStyleFont), and we
* only store reset structs conditionally.
*/
void SetFontSizeDependency(nscoord aCoord)
{
MOZ_ASSERT(!(mBits & eHaveFontSize) || mFontSize == aCoord);
mFontSize = aCoord;
mBits |= eHaveFontSize;
}
/**
* Record that the data being computed depend on the writing mode of
* the element for which they are being computed, which in turn
* depends on its 'writing-mode', 'direction', and 'text-orientation'
* properties.
*/
void SetWritingModeDependency(uint8_t aWritingMode)
{
MOZ_ASSERT(!(mBits & eHaveWritingMode) || GetWritingMode() == aWritingMode);
mBits |= (static_cast<uint64_t>(aWritingMode) << eWritingModeShift) |
eHaveWritingMode;
}
void SetUncacheable()
{
mBits |= eUncacheable;
}
void Clear()
{
*this = RuleNodeCacheConditions();
}
bool Cacheable() const
{
return !(mBits & eUncacheable);
}
bool CacheableWithDependencies() const
{
return !(mBits & eUncacheable) &&
(mBits & eHaveBitsMask) != 0;
}
bool CacheableWithoutDependencies() const
{
// We're not uncacheable and we have don't have a font-size or
// writing mode value.
return (mBits & eHaveBitsMask) == 0;
}
#ifdef DEBUG
void List() const;
#endif
private:
enum {
eUncacheable = 0x0001,
eHaveFontSize = 0x0002,
eHaveWritingMode = 0x0004,
eHaveBitsMask = 0x00ff,
eWritingModeMask = 0xff00,
eWritingModeShift = 8,
};
uint8_t GetWritingMode() const
{
return static_cast<uint8_t>(
(mBits & eWritingModeMask) >> eWritingModeShift);
}
// The font size from which em units are derived.
nscoord mFontSize;
// Values in mBits:
// bit 0: are we set to "uncacheable"?
// bit 1: do we have a font size value?
// bit 2: do we have a writing mode value?
// bits 3-7: unused
// bits 8-15: writing mode (uint8_t)
// bits 16-31: unused
uint32_t mBits;
};
} // namespace mozilla
#endif // !defined(RuleNodeCacheConditions_h_)

View file

@ -0,0 +1,286 @@
/* -*- 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/. */
/*
* cache of re-usable nsCSSRuleProcessors for given sets of style sheets
*/
#include "RuleProcessorCache.h"
#include <algorithm>
#include "nsCSSRuleProcessor.h"
#include "nsThreadUtils.h"
using namespace mozilla;
NS_IMPL_ISUPPORTS(RuleProcessorCache, nsIMemoryReporter)
MOZ_DEFINE_MALLOC_SIZE_OF(RuleProcessorCacheMallocSizeOf)
NS_IMETHODIMP
RuleProcessorCache::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize)
{
MOZ_COLLECT_REPORT(
"explicit/layout/rule-processor-cache", KIND_HEAP, UNITS_BYTES,
SizeOfIncludingThis(RuleProcessorCacheMallocSizeOf),
"Memory used for cached rule processors.");
return NS_OK;
}
RuleProcessorCache::~RuleProcessorCache()
{
UnregisterWeakMemoryReporter(this);
for (Entry& e : mEntries) {
for (DocumentEntry& de : e.mDocumentEntries) {
if (de.mRuleProcessor->GetExpirationState()->IsTracked()) {
mExpirationTracker.RemoveObject(de.mRuleProcessor);
}
de.mRuleProcessor->SetInRuleProcessorCache(false);
}
}
}
void
RuleProcessorCache::InitMemoryReporter()
{
RegisterWeakMemoryReporter(this);
}
/* static */ bool
RuleProcessorCache::EnsureGlobal()
{
MOZ_ASSERT(NS_IsMainThread());
if (gShutdown) {
return false;
}
if (!gRuleProcessorCache) {
gRuleProcessorCache = new RuleProcessorCache;
gRuleProcessorCache->InitMemoryReporter();
}
return true;
}
/* static */ void
RuleProcessorCache::RemoveSheet(CSSStyleSheet* aSheet)
{
if (!EnsureGlobal()) {
return;
}
gRuleProcessorCache->DoRemoveSheet(aSheet);
}
#ifdef DEBUG
/* static */ bool
RuleProcessorCache::HasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
{
if (!EnsureGlobal()) {
return false;
}
return gRuleProcessorCache->DoHasRuleProcessor(aRuleProcessor);
}
#endif
/* static */ void
RuleProcessorCache::RemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
{
if (!EnsureGlobal()) {
return;
}
gRuleProcessorCache->DoRemoveRuleProcessor(aRuleProcessor);
}
/* static */ nsCSSRuleProcessor*
RuleProcessorCache::GetRuleProcessor(const nsTArray<CSSStyleSheet*>& aSheets,
nsPresContext* aPresContext)
{
if (!EnsureGlobal()) {
return nullptr;
}
return gRuleProcessorCache->DoGetRuleProcessor(aSheets, aPresContext);
}
/* static */ void
RuleProcessorCache::PutRuleProcessor(
const nsTArray<CSSStyleSheet*>& aSheets,
nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
const nsDocumentRuleResultCacheKey& aCacheKey,
nsCSSRuleProcessor* aRuleProcessor)
{
if (!EnsureGlobal()) {
return;
}
gRuleProcessorCache->DoPutRuleProcessor(aSheets, Move(aDocumentRulesInSheets),
aCacheKey, aRuleProcessor);
}
/* static */ void
RuleProcessorCache::StartTracking(nsCSSRuleProcessor* aRuleProcessor)
{
if (!EnsureGlobal()) {
return;
}
return gRuleProcessorCache->DoStartTracking(aRuleProcessor);
}
/* static */ void
RuleProcessorCache::StopTracking(nsCSSRuleProcessor* aRuleProcessor)
{
if (!EnsureGlobal()) {
return;
}
return gRuleProcessorCache->DoStopTracking(aRuleProcessor);
}
void
RuleProcessorCache::DoRemoveSheet(CSSStyleSheet* aSheet)
{
Entry* last = std::remove_if(mEntries.begin(), mEntries.end(),
HasSheet_ThenRemoveRuleProcessors(this, aSheet));
mEntries.TruncateLength(last - mEntries.begin());
}
nsCSSRuleProcessor*
RuleProcessorCache::DoGetRuleProcessor(const nsTArray<CSSStyleSheet*>& aSheets,
nsPresContext* aPresContext)
{
for (Entry& e : mEntries) {
if (e.mSheets == aSheets) {
for (DocumentEntry& de : e.mDocumentEntries) {
if (de.mCacheKey.Matches(aPresContext, e.mDocumentRulesInSheets)) {
return de.mRuleProcessor;
}
}
// Entry::mSheets is unique; if we matched aSheets but didn't
// find a matching DocumentEntry, we won't find one later in
// mEntries.
return nullptr;
}
}
return nullptr;
}
void
RuleProcessorCache::DoPutRuleProcessor(
const nsTArray<CSSStyleSheet*>& aSheets,
nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
const nsDocumentRuleResultCacheKey& aCacheKey,
nsCSSRuleProcessor* aRuleProcessor)
{
MOZ_ASSERT(!aRuleProcessor->IsInRuleProcessorCache());
Entry* entry = nullptr;
for (Entry& e : mEntries) {
if (e.mSheets == aSheets) {
entry = &e;
break;
}
}
if (!entry) {
entry = mEntries.AppendElement();
entry->mSheets = aSheets;
entry->mDocumentRulesInSheets = aDocumentRulesInSheets;
for (CSSStyleSheet* sheet : aSheets) {
sheet->SetInRuleProcessorCache();
}
} else {
MOZ_ASSERT(entry->mDocumentRulesInSheets == aDocumentRulesInSheets,
"DocumentRule array shouldn't have changed");
}
#ifdef DEBUG
for (DocumentEntry& de : entry->mDocumentEntries) {
MOZ_ASSERT(de.mCacheKey != aCacheKey,
"should not have duplicate document cache keys");
}
#endif
DocumentEntry* documentEntry = entry->mDocumentEntries.AppendElement();
documentEntry->mCacheKey = aCacheKey;
documentEntry->mRuleProcessor = aRuleProcessor;
aRuleProcessor->SetInRuleProcessorCache(true);
}
#ifdef DEBUG
bool
RuleProcessorCache::DoHasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
{
for (Entry& e : mEntries) {
for (DocumentEntry& de : e.mDocumentEntries) {
if (de.mRuleProcessor == aRuleProcessor) {
return true;
}
}
}
return false;
}
#endif
void
RuleProcessorCache::DoRemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
{
MOZ_ASSERT(aRuleProcessor->IsInRuleProcessorCache());
aRuleProcessor->SetInRuleProcessorCache(false);
mExpirationTracker.RemoveObjectIfTracked(aRuleProcessor);
for (Entry& e : mEntries) {
for (size_t i = 0; i < e.mDocumentEntries.Length(); i++) {
if (e.mDocumentEntries[i].mRuleProcessor == aRuleProcessor) {
e.mDocumentEntries.RemoveElementAt(i);
return;
}
}
}
MOZ_ASSERT_UNREACHABLE("should have found rule processor");
}
void
RuleProcessorCache::DoStartTracking(nsCSSRuleProcessor* aRuleProcessor)
{
mExpirationTracker.AddObject(aRuleProcessor);
}
void
RuleProcessorCache::DoStopTracking(nsCSSRuleProcessor* aRuleProcessor)
{
mExpirationTracker.RemoveObjectIfTracked(aRuleProcessor);
}
size_t
RuleProcessorCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
{
size_t n = aMallocSizeOf(this);
int count = 0;
n += mEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
for (Entry& e : mEntries) {
n += e.mDocumentEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
for (DocumentEntry& de : e.mDocumentEntries) {
count++;
n += de.mRuleProcessor->SizeOfIncludingThis(aMallocSizeOf);
}
}
return n;
}
void
RuleProcessorCache::ExpirationTracker::RemoveObjectIfTracked(
nsCSSRuleProcessor* aRuleProcessor)
{
if (aRuleProcessor->GetExpirationState()->IsTracked()) {
RemoveObject(aRuleProcessor);
}
}
bool RuleProcessorCache::gShutdown = false;
mozilla::StaticRefPtr<RuleProcessorCache> RuleProcessorCache::gRuleProcessorCache;

View file

@ -0,0 +1,150 @@
/* -*- 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/. */
/*
* cache of re-usable nsCSSRuleProcessors for given sets of style sheets
*/
#ifndef mozilla_RuleProcessorCache_h
#define mozilla_RuleProcessorCache_h
#include "mozilla/MemoryReporting.h"
#include "mozilla/StaticPtr.h"
#include "nsCSSRuleProcessor.h"
#include "nsExpirationTracker.h"
#include "nsIMediaList.h"
#include "nsIMemoryReporter.h"
#include "nsTArray.h"
class nsCSSRuleProcessor;
namespace mozilla {
class CSSStyleSheet;
namespace css {
class DocumentRule;
} // namespace css
} // namespace mozilla
namespace mozilla {
/**
* The RuleProcessorCache is a singleton object that caches
* nsCSSRuleProcessors keyed off a list of style sheets and the result of
* evaluating all @-moz-documents in the style sheets. nsStyleSet gets and
* puts nsCSSRuleProcessors from/to the RuleProcessorCache.
*
* State bits on CSSStyleSheet and nsCSSRuleProcessor track whether they are in
* the RuleProcessorCache. This lets us remove them from the RuleProcessorCache
* when they're going away.
*/
class RuleProcessorCache final : public nsIMemoryReporter
{
NS_DECL_ISUPPORTS
NS_DECL_NSIMEMORYREPORTER
public:
static nsCSSRuleProcessor* GetRuleProcessor(
const nsTArray<CSSStyleSheet*>& aSheets,
nsPresContext* aPresContext);
static void PutRuleProcessor(
const nsTArray<CSSStyleSheet*>& aSheets,
nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
const nsDocumentRuleResultCacheKey& aCacheKey,
nsCSSRuleProcessor* aRuleProcessor);
static void StartTracking(nsCSSRuleProcessor* aRuleProcessor);
static void StopTracking(nsCSSRuleProcessor* aRuleProcessor);
#ifdef DEBUG
static bool HasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor);
#endif
static void RemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor);
static void RemoveSheet(CSSStyleSheet* aSheet);
static void Shutdown() { gShutdown = true; gRuleProcessorCache = nullptr; }
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
private:
class ExpirationTracker : public nsExpirationTracker<nsCSSRuleProcessor,3>
{
public:
explicit ExpirationTracker(RuleProcessorCache* aCache)
: nsExpirationTracker<nsCSSRuleProcessor,3>(
10000, "RuleProcessorCache::ExpirationTracker")
, mCache(aCache) {}
void RemoveObjectIfTracked(nsCSSRuleProcessor* aRuleProcessor);
virtual void NotifyExpired(nsCSSRuleProcessor* aRuleProcessor) override {
mCache->RemoveRuleProcessor(aRuleProcessor);
}
private:
RuleProcessorCache* mCache;
};
RuleProcessorCache() : mExpirationTracker(this) {}
~RuleProcessorCache();
void InitMemoryReporter();
static bool EnsureGlobal();
static StaticRefPtr<RuleProcessorCache> gRuleProcessorCache;
static bool gShutdown;
void DoRemoveSheet(CSSStyleSheet* aSheet);
nsCSSRuleProcessor* DoGetRuleProcessor(
const nsTArray<CSSStyleSheet*>& aSheets,
nsPresContext* aPresContext);
void DoPutRuleProcessor(const nsTArray<CSSStyleSheet*>& aSheets,
nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
const nsDocumentRuleResultCacheKey& aCacheKey,
nsCSSRuleProcessor* aRuleProcessor);
#ifdef DEBUG
bool DoHasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor);
#endif
void DoRemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor);
void DoStartTracking(nsCSSRuleProcessor* aRuleProcessor);
void DoStopTracking(nsCSSRuleProcessor* aRuleProcessor);
struct DocumentEntry {
nsDocumentRuleResultCacheKey mCacheKey;
RefPtr<nsCSSRuleProcessor> mRuleProcessor;
};
struct Entry {
nsTArray<CSSStyleSheet*> mSheets;
nsTArray<css::DocumentRule*> mDocumentRulesInSheets;
nsTArray<DocumentEntry> mDocumentEntries;
};
// Function object to test whether an Entry object has a given sheet
// in its mSheets array. If it does, removes all of its rule processors
// before returning true.
struct HasSheet_ThenRemoveRuleProcessors {
HasSheet_ThenRemoveRuleProcessors(RuleProcessorCache* aCache,
CSSStyleSheet* aSheet)
: mCache(aCache), mSheet(aSheet) {}
bool operator()(Entry& aEntry) {
if (aEntry.mSheets.Contains(mSheet)) {
for (DocumentEntry& de : aEntry.mDocumentEntries) {
de.mRuleProcessor->SetInRuleProcessorCache(false);
mCache->mExpirationTracker.RemoveObjectIfTracked(de.mRuleProcessor);
}
return true;
}
return false;
}
RuleProcessorCache* mCache;
CSSStyleSheet* mSheet;
};
ExpirationTracker mExpirationTracker;
nsTArray<Entry> mEntries;
};
} // namespace mozilla
#endif // mozilla_RuleProcessorCache_h

View file

@ -0,0 +1,122 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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/.
*/
/*
* style rule processor for rules from SMIL Animation of SVG mapped
* attributes (attributes whose values are mapped into style)
*/
#include "SVGAttrAnimationRuleProcessor.h"
#include "nsRuleProcessorData.h"
#include "nsSVGElement.h"
using namespace mozilla;
using namespace mozilla::dom;
SVGAttrAnimationRuleProcessor::SVGAttrAnimationRuleProcessor()
{
}
SVGAttrAnimationRuleProcessor::~SVGAttrAnimationRuleProcessor()
{
}
NS_IMPL_ISUPPORTS(SVGAttrAnimationRuleProcessor, nsIStyleRuleProcessor)
/* virtual */ void
SVGAttrAnimationRuleProcessor::RulesMatching(ElementRuleProcessorData* aData)
{
ElementRulesMatching(aData->mElement, aData->mRuleWalker);
}
void
SVGAttrAnimationRuleProcessor::ElementRulesMatching(Element* aElement,
nsRuleWalker* aRuleWalker)
{
if (aElement->IsSVGElement()) {
static_cast<nsSVGElement*>(aElement)->
WalkAnimatedContentStyleRules(aRuleWalker);
}
}
/* virtual */ nsRestyleHint
SVGAttrAnimationRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData)
{
return nsRestyleHint(0);
}
/* virtual */ nsRestyleHint
SVGAttrAnimationRuleProcessor::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData)
{
return nsRestyleHint(0);
}
/* virtual */ bool
SVGAttrAnimationRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
{
return false;
}
/* virtual */ nsRestyleHint
SVGAttrAnimationRuleProcessor::HasAttributeDependentStyle(
AttributeRuleProcessorData* aData,
RestyleHintData& aRestyleHintDataResult)
{
return nsRestyleHint(0);
}
/* virtual */ bool
SVGAttrAnimationRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext)
{
return false;
}
/* virtual */ void
SVGAttrAnimationRuleProcessor::RulesMatching(PseudoElementRuleProcessorData* aData)
{
// If SMIL Animation of SVG attributes can ever target
// pseudo-elements, we need to adjust either
// nsStyleSet::RuleNodeWithReplacement or the test in
// ElementRestyler::RestyleSelf (added in bug 977991 patch 4) to
// handle such styles.
}
/* virtual */ void
SVGAttrAnimationRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData)
{
// If SMIL Animation of SVG attributes can ever target anonymous boxes,
// see comment in RulesMatching(PseudoElementRuleProcessorData*).
}
#ifdef MOZ_XUL
/* virtual */ void
SVGAttrAnimationRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData)
{
// If SMIL Animation of SVG attributes can ever target XUL tree pseudos,
// see comment in RulesMatching(PseudoElementRuleProcessorData*).
}
#endif
/* virtual */ size_t
SVGAttrAnimationRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
return 0; // SVGAttrAnimationRuleProcessors are charged to the DOM, not layout
}
/* virtual */ size_t
SVGAttrAnimationRuleProcessor::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return 0; // SVGAttrAnimationRuleProcessors are charged to the DOM, not layout
}
size_t
SVGAttrAnimationRuleProcessor::DOMSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
return n;
}

View file

@ -0,0 +1,68 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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/. */
/*
* style rule processor for rules from SMIL Animation of SVG mapped
* attributes (attributes whose values are mapped into style)
*/
#ifndef mozilla_SVGAttrAnimationRuleProcessor_h_
#define mozilla_SVGAttrAnimationRuleProcessor_h_
#include "nsIStyleRuleProcessor.h"
class nsRuleWalker;
namespace mozilla {
namespace dom {
class Element;
} // namespace dom
class SVGAttrAnimationRuleProcessor final : public nsIStyleRuleProcessor
{
public:
SVGAttrAnimationRuleProcessor();
private:
~SVGAttrAnimationRuleProcessor();
public:
NS_DECL_ISUPPORTS
// nsIStyleRuleProcessor API
virtual void RulesMatching(ElementRuleProcessorData* aData) override;
virtual void RulesMatching(PseudoElementRuleProcessorData* aData) override;
virtual void RulesMatching(AnonBoxRuleProcessorData* aData) override;
#ifdef MOZ_XUL
virtual void RulesMatching(XULTreeRuleProcessorData* aData) override;
#endif
virtual nsRestyleHint HasStateDependentStyle(StateRuleProcessorData* aData) override;
virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) override;
virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override;
virtual nsRestyleHint
HasAttributeDependentStyle(AttributeRuleProcessorData* aData,
RestyleHintData& aRestyleHintDataResult) override;
virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) override;
virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
const MOZ_MUST_OVERRIDE override;
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
const MOZ_MUST_OVERRIDE override;
size_t DOMSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
// A shortcut for nsStyleSet to call RulesMatching with less setup.
void ElementRulesMatching(mozilla::dom::Element* aElement,
nsRuleWalker* aRuleWalker);
private:
SVGAttrAnimationRuleProcessor(const SVGAttrAnimationRuleProcessor& aCopy) = delete;
SVGAttrAnimationRuleProcessor& operator=(const SVGAttrAnimationRuleProcessor& aCopy) = delete;
};
} // namespace mozilla
#endif /* !defined(mozilla_SVGAttrAnimationRuleProcessor_h_) */

View file

@ -0,0 +1,146 @@
/* -*- 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 list of all Servo binding functions */
/* This file contains the list of all Servo binding functions. Each
* entry is defined as a SERVO_BINDING_FUNC macro with the following
* parameters:
* - 'name_' the name of the binding function
* - 'return_' the return type of the binding function
* and the parameter list of the function.
*
* Users of this list should define a macro
* SERVO_BINDING_FUNC(name_, return_, ...)
* before including this file.
*/
// Node data
SERVO_BINDING_FUNC(Servo_Node_ClearNodeData, void, RawGeckoNodeBorrowed node)
// Styleset and Stylesheet management
SERVO_BINDING_FUNC(Servo_StyleSheet_Empty, RawServoStyleSheetStrong,
mozilla::css::SheetParsingMode parsing_mode)
SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes, RawServoStyleSheetStrong,
const nsACString* data,
mozilla::css::SheetParsingMode parsing_mode,
const nsACString* base_url,
ThreadSafeURIHolder* base,
ThreadSafeURIHolder* referrer,
ThreadSafePrincipalHolder* principal)
SERVO_BINDING_FUNC(Servo_StyleSheet_AddRef, void,
RawServoStyleSheetBorrowed sheet)
SERVO_BINDING_FUNC(Servo_StyleSheet_Release, void,
RawServoStyleSheetBorrowed sheet)
SERVO_BINDING_FUNC(Servo_StyleSheet_HasRules, bool,
RawServoStyleSheetBorrowed sheet)
SERVO_BINDING_FUNC(Servo_StyleSet_Init, RawServoStyleSetOwned)
SERVO_BINDING_FUNC(Servo_StyleSet_Drop, void, RawServoStyleSetOwned set)
SERVO_BINDING_FUNC(Servo_StyleSet_AppendStyleSheet, void,
RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
SERVO_BINDING_FUNC(Servo_StyleSet_PrependStyleSheet, void,
RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
SERVO_BINDING_FUNC(Servo_StyleSet_RemoveStyleSheet, void,
RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
SERVO_BINDING_FUNC(Servo_StyleSet_InsertStyleSheetBefore, void,
RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet,
RawServoStyleSheetBorrowed reference)
// Animations API
SERVO_BINDING_FUNC(Servo_ParseProperty,
RawServoDeclarationBlockStrong,
const nsACString* property, const nsACString* value,
const nsACString* base_url, ThreadSafeURIHolder* base,
ThreadSafeURIHolder* referrer,
ThreadSafePrincipalHolder* principal)
SERVO_BINDING_FUNC(Servo_RestyleWithAddedDeclaration,
ServoComputedValuesStrong,
RawServoDeclarationBlockBorrowed declarations,
ServoComputedValuesBorrowed previous_style)
// Style attribute
SERVO_BINDING_FUNC(Servo_ParseStyleAttribute, RawServoDeclarationBlockStrong,
const nsACString* data)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_CreateEmpty,
RawServoDeclarationBlockStrong)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_Clone, RawServoDeclarationBlockStrong,
RawServoDeclarationBlockBorrowed declarations)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_AddRef, void,
RawServoDeclarationBlockBorrowed declarations)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_Release, void,
RawServoDeclarationBlockBorrowed declarations)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_Equals, bool,
RawServoDeclarationBlockBorrowed a,
RawServoDeclarationBlockBorrowed b)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetCssText, void,
RawServoDeclarationBlockBorrowed declarations,
nsAString* result)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_SerializeOneValue, void,
RawServoDeclarationBlockBorrowed declarations,
nsString* buffer)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_Count, uint32_t,
RawServoDeclarationBlockBorrowed declarations)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetNthProperty, bool,
RawServoDeclarationBlockBorrowed declarations,
uint32_t index, nsAString* result)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetPropertyValue, void,
RawServoDeclarationBlockBorrowed declarations,
nsIAtom* property, bool is_custom, nsAString* value)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetPropertyIsImportant, bool,
RawServoDeclarationBlockBorrowed declarations,
nsIAtom* property, bool is_custom)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetProperty, bool,
RawServoDeclarationBlockBorrowed declarations,
nsIAtom* property, bool is_custom,
nsACString* value, bool is_important)
SERVO_BINDING_FUNC(Servo_DeclarationBlock_RemoveProperty, void,
RawServoDeclarationBlockBorrowed declarations,
nsIAtom* property, bool is_custom)
// CSS supports()
SERVO_BINDING_FUNC(Servo_CSSSupports, bool,
const nsACString* name, const nsACString* value)
// Computed style data
SERVO_BINDING_FUNC(Servo_ComputedValues_Get, ServoComputedValuesStrong,
RawGeckoNodeBorrowed node)
SERVO_BINDING_FUNC(Servo_ComputedValues_GetForAnonymousBox,
ServoComputedValuesStrong,
ServoComputedValuesBorrowedOrNull parent_style_or_null,
nsIAtom* pseudoTag, RawServoStyleSetBorrowed set)
SERVO_BINDING_FUNC(Servo_ComputedValues_GetForPseudoElement,
ServoComputedValuesStrong,
ServoComputedValuesBorrowed parent_style,
RawGeckoElementBorrowed match_element, nsIAtom* pseudo_tag,
RawServoStyleSetBorrowed set, bool is_probe)
SERVO_BINDING_FUNC(Servo_ComputedValues_Inherit, ServoComputedValuesStrong,
ServoComputedValuesBorrowedOrNull parent_style)
SERVO_BINDING_FUNC(Servo_ComputedValues_AddRef, void,
ServoComputedValuesBorrowed computed_values)
SERVO_BINDING_FUNC(Servo_ComputedValues_Release, void,
ServoComputedValuesBorrowed computed_values)
// Initialize Servo components. Should be called exactly once at startup.
SERVO_BINDING_FUNC(Servo_Initialize, void)
// Shut down Servo components. Should be called exactly once at shutdown.
SERVO_BINDING_FUNC(Servo_Shutdown, void)
// Restyle hints
SERVO_BINDING_FUNC(Servo_ComputeRestyleHint, nsRestyleHint,
RawGeckoElementBorrowed element, ServoElementSnapshot* snapshot,
RawServoStyleSetBorrowed set)
// Restyle the given subtree.
SERVO_BINDING_FUNC(Servo_RestyleSubtree, void,
RawGeckoNodeBorrowed node, RawServoStyleSetBorrowed set)
// Style-struct management.
#define STYLE_STRUCT(name, checkdata_cb) \
struct nsStyle##name; \
SERVO_BINDING_FUNC(Servo_GetStyle##name, const nsStyle##name*, \
ServoComputedValuesBorrowedOrNull computed_values)
#include "nsStyleStructList.h"
#undef STYLE_STRUCT

View file

@ -0,0 +1,144 @@
/* -*- 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 mozilla_ServoBindingTypes_h
#define mozilla_ServoBindingTypes_h
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
struct ServoComputedValues;
struct RawServoStyleSheet;
struct RawServoStyleSet;
struct RawServoDeclarationBlock;
namespace mozilla {
namespace dom {
class Element;
class StyleChildrenIterator;
} // namespace dom
} // namespace mozilla
class nsCSSValue;
class nsIDocument;
class nsINode;
using mozilla::dom::StyleChildrenIterator;
typedef nsINode RawGeckoNode;
typedef mozilla::dom::Element RawGeckoElement;
typedef nsIDocument RawGeckoDocument;
// We have these helper types so that we can directly generate
// things like &T or Borrowed<T> on the Rust side in the function, providing
// additional safety benefits.
//
// FFI has a problem with templated types, so we just use raw pointers here.
//
// The "Borrowed" types generate &T or Borrowed<T> in the nullable case.
//
// The "Owned" types generate Owned<T> or OwnedOrNull<T>. Some of these
// are Servo-managed and can be converted to Box<ServoType> on the
// Servo side.
//
// The "Arc" types are Servo-managed Arc<ServoType>s, which are passed
// over FFI as Strong<T> (which is nullable).
// Note that T != ServoType, rather T is ArcInner<ServoType>
#define DECL_BORROWED_REF_TYPE_FOR(type_) typedef type_ const* type_##Borrowed;
#define DECL_NULLABLE_BORROWED_REF_TYPE_FOR(type_) typedef type_ const* type_##BorrowedOrNull;
#define DECL_BORROWED_MUT_REF_TYPE_FOR(type_) typedef type_* type_##BorrowedMut;
#define DECL_NULLABLE_BORROWED_MUT_REF_TYPE_FOR(type_) typedef type_* type_##BorrowedMutOrNull;
#define DECL_ARC_REF_TYPE_FOR(type_) \
DECL_NULLABLE_BORROWED_REF_TYPE_FOR(type_) \
DECL_BORROWED_REF_TYPE_FOR(type_) \
struct MOZ_MUST_USE_TYPE type_##Strong \
{ \
type_* mPtr; \
already_AddRefed<type_> Consume(); \
};
#define DECL_OWNED_REF_TYPE_FOR(type_) \
typedef type_* type_##Owned; \
DECL_BORROWED_REF_TYPE_FOR(type_) \
DECL_BORROWED_MUT_REF_TYPE_FOR(type_)
#define DECL_NULLABLE_OWNED_REF_TYPE_FOR(type_) \
typedef type_* type_##OwnedOrNull; \
DECL_NULLABLE_BORROWED_REF_TYPE_FOR(type_) \
DECL_NULLABLE_BORROWED_MUT_REF_TYPE_FOR(type_)
DECL_ARC_REF_TYPE_FOR(ServoComputedValues)
DECL_ARC_REF_TYPE_FOR(RawServoStyleSheet)
DECL_ARC_REF_TYPE_FOR(RawServoDeclarationBlock)
// This is a reference to a reference of RawServoDeclarationBlock, which
// corresponds to Option<&Arc<RawServoDeclarationBlock>> in Servo side.
DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawServoDeclarationBlockStrong)
DECL_OWNED_REF_TYPE_FOR(RawServoStyleSet)
DECL_NULLABLE_OWNED_REF_TYPE_FOR(StyleChildrenIterator)
DECL_OWNED_REF_TYPE_FOR(StyleChildrenIterator)
// We don't use BorrowedMut because the nodes may alias
// Servo itself doesn't directly read or mutate these;
// it only asks Gecko to do so. In case we wish to in
// the future, we should ensure that things being mutated
// are protected from noalias violations by a cell type
DECL_BORROWED_REF_TYPE_FOR(RawGeckoNode)
DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawGeckoNode)
DECL_BORROWED_REF_TYPE_FOR(RawGeckoElement)
DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawGeckoElement)
DECL_BORROWED_REF_TYPE_FOR(RawGeckoDocument)
DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawGeckoDocument)
DECL_BORROWED_MUT_REF_TYPE_FOR(StyleChildrenIterator)
DECL_BORROWED_REF_TYPE_FOR(nsCSSValue)
DECL_BORROWED_MUT_REF_TYPE_FOR(nsCSSValue)
#undef DECL_ARC_REF_TYPE_FOR
#undef DECL_OWNED_REF_TYPE_FOR
#undef DECL_NULLABLE_OWNED_REF_TYPE_FOR
#undef DECL_BORROWED_REF_TYPE_FOR
#undef DECL_NULLABLE_BORROWED_REF_TYPE_FOR
#undef DECL_BORROWED_MUT_REF_TYPE_FOR
#undef DECL_NULLABLE_BORROWED_MUT_REF_TYPE_FOR
#define DEFINE_REFPTR_TRAITS(name_, type_) \
extern "C" { \
void Servo_##name_##_AddRef(type_##Borrowed ptr); \
void Servo_##name_##_Release(type_##Borrowed ptr); \
} \
namespace mozilla { \
template<> struct RefPtrTraits<type_> { \
static void AddRef(type_* aPtr) { \
Servo_##name_##_AddRef(aPtr); \
} \
static void Release(type_* aPtr) { \
Servo_##name_##_Release(aPtr); \
} \
}; \
}
DEFINE_REFPTR_TRAITS(StyleSheet, RawServoStyleSheet)
DEFINE_REFPTR_TRAITS(ComputedValues, ServoComputedValues)
DEFINE_REFPTR_TRAITS(DeclarationBlock, RawServoDeclarationBlock)
#undef DEFINE_REFPTR_TRAITS
extern "C" void Servo_StyleSet_Drop(RawServoStyleSetOwned ptr);
namespace mozilla {
template<>
class DefaultDelete<RawServoStyleSet>
{
public:
void operator()(RawServoStyleSet* aPtr) const
{
Servo_StyleSet_Drop(aPtr);
}
};
}
#endif // mozilla_ServoBindingTypes_h

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,285 @@
/* -*- 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 mozilla_ServoBindings_h
#define mozilla_ServoBindings_h
#include <stdint.h>
#include "mozilla/ServoTypes.h"
#include "mozilla/ServoBindingTypes.h"
#include "mozilla/ServoElementSnapshot.h"
#include "mozilla/css/SheetParsingMode.h"
#include "nsChangeHint.h"
#include "nsStyleStruct.h"
/*
* API for Servo to access Gecko data structures. This file must compile as valid
* C code in order for the binding generator to parse it.
*
* Functions beginning with Gecko_ are implemented in Gecko and invoked from Servo.
* Functions beginning with Servo_ are implemented in Servo and invoked from Gecko.
*/
class nsIAtom;
class nsIPrincipal;
class nsIURI;
struct nsFont;
namespace mozilla {
class FontFamilyList;
enum FontFamilyType : uint32_t;
}
using mozilla::FontFamilyList;
using mozilla::FontFamilyType;
using mozilla::ServoElementSnapshot;
struct nsStyleList;
struct nsStyleImage;
struct nsStyleGradientStop;
class nsStyleGradient;
class nsStyleCoord;
struct nsStyleDisplay;
#define NS_DECL_THREADSAFE_FFI_REFCOUNTING(class_, name_) \
void Gecko_AddRef##name_##ArbitraryThread(class_* aPtr); \
void Gecko_Release##name_##ArbitraryThread(class_* aPtr);
#define NS_IMPL_THREADSAFE_FFI_REFCOUNTING(class_, name_) \
static_assert(class_::HasThreadSafeRefCnt::value, \
"NS_DECL_THREADSAFE_FFI_REFCOUNTING can only be used with " \
"classes that have thread-safe refcounting"); \
void Gecko_AddRef##name_##ArbitraryThread(class_* aPtr) \
{ NS_ADDREF(aPtr); } \
void Gecko_Release##name_##ArbitraryThread(class_* aPtr) \
{ NS_RELEASE(aPtr); }
#define NS_DECL_HOLDER_FFI_REFCOUNTING(class_, name_) \
typedef nsMainThreadPtrHolder<class_> ThreadSafe##name_##Holder; \
void Gecko_AddRef##name_##ArbitraryThread(ThreadSafe##name_##Holder* aPtr); \
void Gecko_Release##name_##ArbitraryThread(ThreadSafe##name_##Holder* aPtr);
#define NS_IMPL_HOLDER_FFI_REFCOUNTING(class_, name_) \
void Gecko_AddRef##name_##ArbitraryThread(ThreadSafe##name_##Holder* aPtr) \
{ NS_ADDREF(aPtr); } \
void Gecko_Release##name_##ArbitraryThread(ThreadSafe##name_##Holder* aPtr) \
{ NS_RELEASE(aPtr); } \
extern "C" {
// Object refcounting.
NS_DECL_HOLDER_FFI_REFCOUNTING(nsIPrincipal, Principal)
NS_DECL_HOLDER_FFI_REFCOUNTING(nsIURI, URI)
// DOM Traversal.
uint32_t Gecko_ChildrenCount(RawGeckoNodeBorrowed node);
bool Gecko_NodeIsElement(RawGeckoNodeBorrowed node);
RawGeckoNodeBorrowedOrNull Gecko_GetParentNode(RawGeckoNodeBorrowed node);
RawGeckoNodeBorrowedOrNull Gecko_GetFirstChild(RawGeckoNodeBorrowed node);
RawGeckoNodeBorrowedOrNull Gecko_GetLastChild(RawGeckoNodeBorrowed node);
RawGeckoNodeBorrowedOrNull Gecko_GetPrevSibling(RawGeckoNodeBorrowed node);
RawGeckoNodeBorrowedOrNull Gecko_GetNextSibling(RawGeckoNodeBorrowed node);
RawGeckoElementBorrowedOrNull Gecko_GetParentElement(RawGeckoElementBorrowed element);
RawGeckoElementBorrowedOrNull Gecko_GetFirstChildElement(RawGeckoElementBorrowed element);
RawGeckoElementBorrowedOrNull Gecko_GetLastChildElement(RawGeckoElementBorrowed element);
RawGeckoElementBorrowedOrNull Gecko_GetPrevSiblingElement(RawGeckoElementBorrowed element);
RawGeckoElementBorrowedOrNull Gecko_GetNextSiblingElement(RawGeckoElementBorrowed element);
RawGeckoElementBorrowedOrNull Gecko_GetDocumentElement(RawGeckoDocumentBorrowed document);
// By default, Servo walks the DOM by traversing the siblings of the DOM-view
// first child. This generally works, but misses anonymous children, which we
// want to traverse during styling. To support these cases, we create an
// optional heap-allocated iterator for nodes that need it. If the creation
// method returns null, Servo falls back to the aforementioned simpler (and
// faster) sibling traversal.
StyleChildrenIteratorOwnedOrNull Gecko_MaybeCreateStyleChildrenIterator(RawGeckoNodeBorrowed node);
void Gecko_DropStyleChildrenIterator(StyleChildrenIteratorOwned it);
RawGeckoNodeBorrowedOrNull Gecko_GetNextStyleChild(StyleChildrenIteratorBorrowedMut it);
// Selector Matching.
uint8_t Gecko_ElementState(RawGeckoElementBorrowed element);
bool Gecko_IsHTMLElementInHTMLDocument(RawGeckoElementBorrowed element);
bool Gecko_IsLink(RawGeckoElementBorrowed element);
bool Gecko_IsTextNode(RawGeckoNodeBorrowed node);
bool Gecko_IsVisitedLink(RawGeckoElementBorrowed element);
bool Gecko_IsUnvisitedLink(RawGeckoElementBorrowed element);
bool Gecko_IsRootElement(RawGeckoElementBorrowed element);
nsIAtom* Gecko_LocalName(RawGeckoElementBorrowed element);
nsIAtom* Gecko_Namespace(RawGeckoElementBorrowed element);
nsIAtom* Gecko_GetElementId(RawGeckoElementBorrowed element);
// Attributes.
#define SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \
nsIAtom* prefix_##AtomAttrValue(implementor_ element, nsIAtom* attribute); \
bool prefix_##HasAttr(implementor_ element, nsIAtom* ns, nsIAtom* name); \
bool prefix_##AttrEquals(implementor_ element, nsIAtom* ns, nsIAtom* name, \
nsIAtom* str, bool ignoreCase); \
bool prefix_##AttrDashEquals(implementor_ element, nsIAtom* ns, \
nsIAtom* name, nsIAtom* str); \
bool prefix_##AttrIncludes(implementor_ element, nsIAtom* ns, \
nsIAtom* name, nsIAtom* str); \
bool prefix_##AttrHasSubstring(implementor_ element, nsIAtom* ns, \
nsIAtom* name, nsIAtom* str); \
bool prefix_##AttrHasPrefix(implementor_ element, nsIAtom* ns, \
nsIAtom* name, nsIAtom* str); \
bool prefix_##AttrHasSuffix(implementor_ element, nsIAtom* ns, \
nsIAtom* name, nsIAtom* str); \
uint32_t prefix_##ClassOrClassList(implementor_ element, nsIAtom** class_, \
nsIAtom*** classList);
SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElementBorrowed)
SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot,
ServoElementSnapshot*)
#undef SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS
// Style attributes.
RawServoDeclarationBlockStrongBorrowedOrNull
Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed element);
// Atoms.
nsIAtom* Gecko_Atomize(const char* aString, uint32_t aLength);
void Gecko_AddRefAtom(nsIAtom* aAtom);
void Gecko_ReleaseAtom(nsIAtom* aAtom);
const uint16_t* Gecko_GetAtomAsUTF16(nsIAtom* aAtom, uint32_t* aLength);
bool Gecko_AtomEqualsUTF8(nsIAtom* aAtom, const char* aString, uint32_t aLength);
bool Gecko_AtomEqualsUTF8IgnoreCase(nsIAtom* aAtom, const char* aString, uint32_t aLength);
// Strings (temporary until bug 1294742)
void Gecko_Utf8SliceToString(nsString* aString,
const uint8_t* aBuffer,
size_t aBufferLen);
// Font style
void Gecko_FontFamilyList_Clear(FontFamilyList* aList);
void Gecko_FontFamilyList_AppendNamed(FontFamilyList* aList, nsIAtom* aName);
void Gecko_FontFamilyList_AppendGeneric(FontFamilyList* list, FontFamilyType familyType);
void Gecko_CopyFontFamilyFrom(nsFont* dst, const nsFont* src);
// Counter style.
void Gecko_SetListStyleType(nsStyleList* style_struct, uint32_t type);
void Gecko_CopyListStyleTypeFrom(nsStyleList* dst, const nsStyleList* src);
// background-image style.
// TODO: support element() and -moz-image()
void Gecko_SetNullImageValue(nsStyleImage* image);
void Gecko_SetGradientImageValue(nsStyleImage* image, nsStyleGradient* gradient);
void Gecko_SetUrlImageValue(nsStyleImage* image,
const uint8_t* url_bytes,
uint32_t url_length,
ThreadSafeURIHolder* base_uri,
ThreadSafeURIHolder* referrer,
ThreadSafePrincipalHolder* principal);
void Gecko_CopyImageValueFrom(nsStyleImage* image, const nsStyleImage* other);
nsStyleGradient* Gecko_CreateGradient(uint8_t shape,
uint8_t size,
bool repeating,
bool legacy_syntax,
uint32_t stops);
// list-style-image style.
void Gecko_SetListStyleImageNone(nsStyleList* style_struct);
void Gecko_SetListStyleImage(nsStyleList* style_struct,
const uint8_t* string_bytes, uint32_t string_length,
ThreadSafeURIHolder* base_uri,
ThreadSafeURIHolder* referrer,
ThreadSafePrincipalHolder* principal);
void Gecko_CopyListStyleImageFrom(nsStyleList* dest, const nsStyleList* src);
// Display style.
void Gecko_SetMozBinding(nsStyleDisplay* style_struct,
const uint8_t* string_bytes, uint32_t string_length,
ThreadSafeURIHolder* base_uri,
ThreadSafeURIHolder* referrer,
ThreadSafePrincipalHolder* principal);
void Gecko_CopyMozBindingFrom(nsStyleDisplay* des, const nsStyleDisplay* src);
// Dirtiness tracking.
uint32_t Gecko_GetNodeFlags(RawGeckoNodeBorrowed node);
void Gecko_SetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
void Gecko_UnsetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
// Incremental restyle.
// TODO: We would avoid a few ffi calls if we decide to make an API like the
// former CalcAndStoreStyleDifference, but that would effectively mean breaking
// some safety guarantees in the servo side.
//
// Also, we might want a ComputedValues to ComputedValues API for animations?
// Not if we do them in Gecko...
nsStyleContext* Gecko_GetStyleContext(RawGeckoNodeBorrowed node,
nsIAtom* aPseudoTagOrNull);
nsChangeHint Gecko_CalcStyleDifference(nsStyleContext* oldstyle,
ServoComputedValuesBorrowed newstyle);
void Gecko_StoreStyleDifference(RawGeckoNodeBorrowed node, nsChangeHint change);
// `array` must be an nsTArray
// If changing this signature, please update the
// friend function declaration in nsTArray.h
void Gecko_EnsureTArrayCapacity(void* array, size_t capacity, size_t elem_size);
// Same here, `array` must be an nsTArray<T>, for some T.
//
// Important note: Only valid for POD types, since destructors won't be run
// otherwise. This is ensured with rust traits for the relevant structs.
void Gecko_ClearPODTArray(void* array, size_t elem_size, size_t elem_align);
// Clear the mContents field in nsStyleContent. This is needed to run the
// destructors, otherwise we'd leak the images (though we still don't support
// those), strings, and whatnot.
void Gecko_ClearStyleContents(nsStyleContent* content);
void Gecko_CopyStyleContentsFrom(nsStyleContent* content, const nsStyleContent* other);
void Gecko_EnsureImageLayersLength(nsStyleImageLayers* layers, size_t len,
nsStyleImageLayers::LayerType layer_type);
// Clean up pointer-based coordinates
void Gecko_ResetStyleCoord(nsStyleUnit* unit, nsStyleUnion* value);
// Set an nsStyleCoord to a computed `calc()` value
void Gecko_SetStyleCoordCalcValue(nsStyleUnit* unit, nsStyleUnion* value, nsStyleCoord::CalcValue calc);
void Gecko_CopyClipPathValueFrom(mozilla::StyleClipPath* dst, const mozilla::StyleClipPath* src);
void Gecko_DestroyClipPath(mozilla::StyleClipPath* clip);
mozilla::StyleBasicShape* Gecko_NewBasicShape(mozilla::StyleBasicShapeType type);
void Gecko_ResetFilters(nsStyleEffects* effects, size_t new_len);
void Gecko_CopyFiltersFrom(nsStyleEffects* aSrc, nsStyleEffects* aDest);
void Gecko_FillAllBackgroundLists(nsStyleImageLayers* layers, uint32_t max_len);
void Gecko_FillAllMaskLists(nsStyleImageLayers* layers, uint32_t max_len);
NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
nsCSSShadowArray* Gecko_NewCSSShadowArray(uint32_t len);
NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSSShadowArray, CSSShadowArray);
nsStyleQuoteValues* Gecko_NewStyleQuoteValues(uint32_t len);
NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsStyleQuoteValues, QuoteValues);
nsCSSValueSharedList* Gecko_NewCSSValueSharedList(uint32_t len);
void Gecko_CSSValue_SetAbsoluteLength(nsCSSValueBorrowedMut css_value, nscoord len);
void Gecko_CSSValue_SetNumber(nsCSSValueBorrowedMut css_value, float number);
void Gecko_CSSValue_SetKeyword(nsCSSValueBorrowedMut css_value, nsCSSKeyword keyword);
void Gecko_CSSValue_SetPercentage(nsCSSValueBorrowedMut css_value, float percent);
void Gecko_CSSValue_SetAngle(nsCSSValueBorrowedMut css_value, float radians);
void Gecko_CSSValue_SetCalc(nsCSSValueBorrowedMut css_value, nsStyleCoord::CalcValue calc);
void Gecko_CSSValue_SetFunction(nsCSSValueBorrowedMut css_value, int32_t len);
nsCSSValueBorrowedMut Gecko_CSSValue_GetArrayItem(nsCSSValueBorrowedMut css_value, int32_t index);
NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSSValueSharedList, CSSValueSharedList);
// Style-struct management.
#define STYLE_STRUCT(name, checkdata_cb) \
void Gecko_Construct_nsStyle##name(nsStyle##name* ptr); \
void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr, \
const nsStyle##name* other); \
void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr);
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
#define SERVO_BINDING_FUNC(name_, return_, ...) return_ name_(__VA_ARGS__);
#include "mozilla/ServoBindingList.h"
#undef SERVO_BINDING_FUNC
} // extern "C"
#endif // mozilla_ServoBindings_h

View file

@ -0,0 +1,110 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/ServoDeclarationBlock.h"
#include "mozilla/ServoBindings.h"
#include "nsCSSProps.h"
namespace mozilla {
/* static */ already_AddRefed<ServoDeclarationBlock>
ServoDeclarationBlock::FromCssText(const nsAString& aCssText)
{
NS_ConvertUTF16toUTF8 value(aCssText);
RefPtr<RawServoDeclarationBlock>
raw = Servo_ParseStyleAttribute(&value).Consume();
RefPtr<ServoDeclarationBlock> decl = new ServoDeclarationBlock(raw.forget());
return decl.forget();
}
/**
* An RAII class holding an atom for the given property.
*/
class MOZ_STACK_CLASS PropertyAtomHolder
{
public:
explicit PropertyAtomHolder(const nsAString& aProperty)
{
nsCSSPropertyID propID =
nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent);
if (propID == eCSSPropertyExtra_variable) {
mIsCustomProperty = true;
mAtom = NS_Atomize(
Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH)).take();
} else {
mIsCustomProperty = false;
if (propID != eCSSProperty_UNKNOWN) {
mAtom = nsCSSProps::AtomForProperty(propID);
} else {
mAtom = nullptr;
}
}
}
~PropertyAtomHolder()
{
if (mIsCustomProperty) {
NS_RELEASE(mAtom);
}
}
explicit operator bool() const { return !!mAtom; }
nsIAtom* Atom() const { MOZ_ASSERT(mAtom); return mAtom; }
bool IsCustomProperty() const { return mIsCustomProperty; }
private:
nsIAtom* mAtom;
bool mIsCustomProperty;
};
void
ServoDeclarationBlock::GetPropertyValue(const nsAString& aProperty,
nsAString& aValue) const
{
if (PropertyAtomHolder holder{aProperty}) {
Servo_DeclarationBlock_GetPropertyValue(
mRaw, holder.Atom(), holder.IsCustomProperty(), &aValue);
}
}
void
ServoDeclarationBlock::GetPropertyValueByID(nsCSSPropertyID aPropID,
nsAString& aValue) const
{
nsIAtom* atom = nsCSSProps::AtomForProperty(aPropID);
Servo_DeclarationBlock_GetPropertyValue(mRaw, atom, false, &aValue);
}
bool
ServoDeclarationBlock::GetPropertyIsImportant(const nsAString& aProperty) const
{
if (PropertyAtomHolder holder{aProperty}) {
return Servo_DeclarationBlock_GetPropertyIsImportant(
mRaw, holder.Atom(), holder.IsCustomProperty());
}
return false;
}
void
ServoDeclarationBlock::RemoveProperty(const nsAString& aProperty)
{
AssertMutable();
if (PropertyAtomHolder holder{aProperty}) {
Servo_DeclarationBlock_RemoveProperty(mRaw, holder.Atom(),
holder.IsCustomProperty());
}
}
void
ServoDeclarationBlock::RemovePropertyByID(nsCSSPropertyID aPropID)
{
AssertMutable();
nsIAtom* atom = nsCSSProps::AtomForProperty(aPropID);
Servo_DeclarationBlock_RemoveProperty(mRaw, atom, false);
}
} // namespace mozilla

View file

@ -0,0 +1,72 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 mozilla_ServoDeclarationBlock_h
#define mozilla_ServoDeclarationBlock_h
#include "mozilla/ServoBindings.h"
#include "mozilla/DeclarationBlock.h"
namespace mozilla {
class ServoDeclarationBlock final : public DeclarationBlock
{
public:
ServoDeclarationBlock()
: ServoDeclarationBlock(Servo_DeclarationBlock_CreateEmpty().Consume()) {}
ServoDeclarationBlock(const ServoDeclarationBlock& aCopy)
: DeclarationBlock(aCopy)
, mRaw(Servo_DeclarationBlock_Clone(aCopy.mRaw).Consume()) {}
NS_INLINE_DECL_REFCOUNTING(ServoDeclarationBlock)
static already_AddRefed<ServoDeclarationBlock>
FromCssText(const nsAString& aCssText);
RawServoDeclarationBlock* Raw() const { return mRaw; }
RawServoDeclarationBlock* const* RefRaw() const {
static_assert(sizeof(RefPtr<RawServoDeclarationBlock>) ==
sizeof(RawServoDeclarationBlock*),
"RefPtr should just be a pointer");
return reinterpret_cast<RawServoDeclarationBlock* const*>(&mRaw);
}
void ToString(nsAString& aResult) const {
Servo_DeclarationBlock_GetCssText(mRaw, &aResult);
}
uint32_t Count() const {
return Servo_DeclarationBlock_Count(mRaw);
}
bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const {
aReturn.Truncate();
return Servo_DeclarationBlock_GetNthProperty(mRaw, aIndex, &aReturn);
}
void GetPropertyValue(const nsAString& aProperty, nsAString& aValue) const;
void GetPropertyValueByID(nsCSSPropertyID aPropID, nsAString& aValue) const;
void GetAuthoredPropertyValue(const nsAString& aProperty,
nsAString& aValue) const {
GetPropertyValue(aProperty, aValue);
}
bool GetPropertyIsImportant(const nsAString& aProperty) const;
void RemoveProperty(const nsAString& aProperty);
void RemovePropertyByID(nsCSSPropertyID aPropID);
protected:
explicit ServoDeclarationBlock(
already_AddRefed<RawServoDeclarationBlock> aRaw)
: DeclarationBlock(StyleBackendType::Servo), mRaw(aRaw) {}
private:
~ServoDeclarationBlock() {}
RefPtr<RawServoDeclarationBlock> mRaw;
};
} // namespace mozilla
#endif // mozilla_ServoDeclarationBlock_h

View file

@ -0,0 +1,46 @@
/* -*- 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/ServoElementSnapshot.h"
#include "mozilla/dom/Element.h"
#include "nsIContentInlines.h"
#include "nsContentUtils.h"
namespace mozilla {
ServoElementSnapshot::ServoElementSnapshot(Element* aElement)
: mContains(Flags(0))
, mState(0)
, mExplicitRestyleHint(nsRestyleHint(0))
, mExplicitChangeHint(nsChangeHint(0))
{
mIsHTMLElementInHTMLDocument =
aElement->IsHTMLElement() && aElement->IsInHTMLDocument();
mIsInChromeDocument =
nsContentUtils::IsChromeDoc(aElement->OwnerDoc());
}
void
ServoElementSnapshot::AddAttrs(Element* aElement)
{
MOZ_ASSERT(aElement);
if (HasAny(Flags::Attributes)) {
return;
}
uint32_t attrCount = aElement->GetAttrCount();
const nsAttrName* attrName;
for (uint32_t i = 0; i < attrCount; ++i) {
attrName = aElement->GetAttrNameAt(i);
const nsAttrValue* attrValue =
aElement->GetParsedAttr(attrName->LocalName(), attrName->NamespaceID());
mAttrs.AppendElement(ServoAttrSnapshot(*attrName, *attrValue));
}
mContains |= Flags::Attributes;
}
} // namespace mozilla

View file

@ -0,0 +1,169 @@
/* -*- 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 mozilla_ServoElementSnapshot_h
#define mozilla_ServoElementSnapshot_h
#include "mozilla/EventStates.h"
#include "mozilla/TypedEnumBits.h"
#include "mozilla/dom/BorrowedAttrInfo.h"
#include "nsAttrName.h"
#include "nsAttrValue.h"
#include "nsChangeHint.h"
#include "nsIAtom.h"
namespace mozilla {
namespace dom {
class Element;
} // namespace dom
/**
* A structure representing a single attribute name and value.
*
* This is pretty similar to the private nsAttrAndChildArray::InternalAttr.
*/
struct ServoAttrSnapshot
{
nsAttrName mName;
nsAttrValue mValue;
ServoAttrSnapshot(const nsAttrName& aName, const nsAttrValue& aValue)
: mName(aName)
, mValue(aValue)
{
}
};
/**
* A bitflags enum class used to determine what data does a ServoElementSnapshot
* contains.
*/
enum class ServoElementSnapshotFlags : uint8_t
{
State = 1 << 0,
Attributes = 1 << 1,
All = State | Attributes
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoElementSnapshotFlags)
/**
* This class holds all non-tree-structural state of an element that might be
* used for selector matching eventually.
*
* This means the attributes, and the element state, such as :hover, :active,
* etc...
*/
class ServoElementSnapshot
{
typedef dom::BorrowedAttrInfo BorrowedAttrInfo;
typedef dom::Element Element;
typedef EventStates::ServoType ServoStateType;
public:
typedef ServoElementSnapshotFlags Flags;
explicit ServoElementSnapshot(Element* aElement);
bool HasAttrs() { return HasAny(Flags::Attributes); }
bool HasState() { return HasAny(Flags::State); }
/**
* Captures the given state (if not previously captured).
*/
void AddState(EventStates aState)
{
if (!HasAny(Flags::State)) {
mState = aState.ServoValue();
mContains |= Flags::State;
}
}
/**
* Captures the given element attributes (if not previously captured).
*/
void AddAttrs(Element* aElement);
void AddExplicitChangeHint(nsChangeHint aMinChangeHint)
{
mExplicitChangeHint |= aMinChangeHint;
}
void AddExplicitRestyleHint(nsRestyleHint aRestyleHint)
{
mExplicitRestyleHint |= aRestyleHint;
}
nsRestyleHint ExplicitRestyleHint() { return mExplicitRestyleHint; }
nsChangeHint ExplicitChangeHint() { return mExplicitChangeHint; }
/**
* Needed methods for attribute matching.
*/
BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const
{
if (aIndex >= mAttrs.Length()) {
return BorrowedAttrInfo(nullptr, nullptr);
}
return BorrowedAttrInfo(&mAttrs[aIndex].mName, &mAttrs[aIndex].mValue);
}
const nsAttrValue* GetParsedAttr(nsIAtom* aLocalName) const
{
return GetParsedAttr(aLocalName, kNameSpaceID_None);
}
const nsAttrValue* GetParsedAttr(nsIAtom* aLocalName,
int32_t aNamespaceID) const
{
uint32_t i, len = mAttrs.Length();
if (aNamespaceID == kNameSpaceID_None) {
// This should be the common case so lets make an optimized loop
for (i = 0; i < len; ++i) {
if (mAttrs[i].mName.Equals(aLocalName)) {
return &mAttrs[i].mValue;
}
}
return nullptr;
}
for (i = 0; i < len; ++i) {
if (mAttrs[i].mName.Equals(aLocalName, aNamespaceID)) {
return &mAttrs[i].mValue;
}
}
return nullptr;
}
bool IsInChromeDocument() const
{
return mIsInChromeDocument;
}
bool HasAny(Flags aFlags) { return bool(mContains & aFlags); }
private:
// TODO: Profile, a 1 or 2 element AutoTArray could be worth it, given we know
// we're dealing with attribute changes when we take snapshots of attributes,
// though it can be wasted space if we deal with a lot of state-only
// snapshots.
Flags mContains;
nsTArray<ServoAttrSnapshot> mAttrs;
ServoStateType mState;
nsRestyleHint mExplicitRestyleHint;
nsChangeHint mExplicitChangeHint;
bool mIsHTMLElementInHTMLDocument;
bool mIsInChromeDocument;
};
} // namespace mozilla
#endif

View file

@ -0,0 +1,479 @@
/* -*- 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/ServoStyleSet.h"
#include "mozilla/ServoRestyleManager.h"
#include "mozilla/dom/ChildIterator.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSPseudoElements.h"
#include "nsIDocumentInlines.h"
#include "nsPrintfCString.h"
#include "nsStyleContext.h"
#include "nsStyleSet.h"
using namespace mozilla;
using namespace mozilla::dom;
ServoStyleSet::ServoStyleSet()
: mPresContext(nullptr)
, mRawSet(Servo_StyleSet_Init())
, mBatching(0)
{
}
void
ServoStyleSet::Init(nsPresContext* aPresContext)
{
mPresContext = aPresContext;
}
void
ServoStyleSet::BeginShutdown()
{
}
void
ServoStyleSet::Shutdown()
{
mRawSet = nullptr;
}
bool
ServoStyleSet::GetAuthorStyleDisabled() const
{
return false;
}
nsresult
ServoStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled)
{
MOZ_CRASH("stylo: not implemented");
}
void
ServoStyleSet::BeginUpdate()
{
++mBatching;
}
nsresult
ServoStyleSet::EndUpdate()
{
MOZ_ASSERT(mBatching > 0);
if (--mBatching > 0) {
return NS_OK;
}
// ... do something ...
return NS_OK;
}
already_AddRefed<nsStyleContext>
ServoStyleSet::ResolveStyleFor(Element* aElement,
nsStyleContext* aParentContext)
{
return GetContext(aElement, aParentContext, nullptr,
CSSPseudoElementType::NotPseudo);
}
already_AddRefed<nsStyleContext>
ServoStyleSet::GetContext(nsIContent* aContent,
nsStyleContext* aParentContext,
nsIAtom* aPseudoTag,
CSSPseudoElementType aPseudoType)
{
RefPtr<ServoComputedValues> computedValues =
Servo_ComputedValues_Get(aContent).Consume();
MOZ_ASSERT(computedValues);
return GetContext(computedValues.forget(), aParentContext, aPseudoTag, aPseudoType);
}
already_AddRefed<nsStyleContext>
ServoStyleSet::GetContext(already_AddRefed<ServoComputedValues> aComputedValues,
nsStyleContext* aParentContext,
nsIAtom* aPseudoTag,
CSSPseudoElementType aPseudoType)
{
// XXXbholley: nsStyleSet does visited handling here.
// XXXbholley: Figure out the correct thing to pass here. Does this fixup
// duplicate something that servo already does?
bool skipFixup = false;
return NS_NewStyleContext(aParentContext, mPresContext, aPseudoTag,
aPseudoType, Move(aComputedValues), skipFixup);
}
already_AddRefed<nsStyleContext>
ServoStyleSet::ResolveStyleFor(Element* aElement,
nsStyleContext* aParentContext,
TreeMatchContext& aTreeMatchContext)
{
// aTreeMatchContext is used to speed up selector matching,
// but if the element already has a ServoComputedValues computed in
// advance, then we shouldn't need to use it.
return ResolveStyleFor(aElement, aParentContext);
}
already_AddRefed<nsStyleContext>
ServoStyleSet::ResolveStyleForText(nsIContent* aTextNode,
nsStyleContext* aParentContext)
{
MOZ_ASSERT(aTextNode && aTextNode->IsNodeOfType(nsINode::eTEXT));
MOZ_ASSERT(aTextNode->GetParent());
MOZ_ASSERT(aParentContext);
// Gecko expects text node style contexts to be like elements that match no
// rules: inherit the inherit structs, reset the reset structs. This is cheap
// enough to do on the main thread, which means that the parallel style system
// can avoid worrying about text nodes.
const ServoComputedValues* parentComputedValues =
aParentContext->StyleSource().AsServoComputedValues();
RefPtr<ServoComputedValues> computedValues =
Servo_ComputedValues_Inherit(parentComputedValues).Consume();
return GetContext(computedValues.forget(), aParentContext,
nsCSSAnonBoxes::mozText, CSSPseudoElementType::AnonBox);
}
already_AddRefed<nsStyleContext>
ServoStyleSet::ResolveStyleForOtherNonElement(nsStyleContext* aParentContext)
{
// The parent context can be null if the non-element share a style context
// with the root of an anonymous subtree.
const ServoComputedValues* parent =
aParentContext ? aParentContext->StyleSource().AsServoComputedValues() : nullptr;
RefPtr<ServoComputedValues> computedValues =
Servo_ComputedValues_Inherit(parent).Consume();
MOZ_ASSERT(computedValues);
return GetContext(computedValues.forget(), aParentContext,
nsCSSAnonBoxes::mozOtherNonElement,
CSSPseudoElementType::AnonBox);
}
already_AddRefed<nsStyleContext>
ServoStyleSet::ResolvePseudoElementStyle(Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext,
Element* aPseudoElement)
{
if (aPseudoElement) {
NS_ERROR("stylo: We don't support CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE yet");
}
MOZ_ASSERT(aParentContext);
MOZ_ASSERT(aType < CSSPseudoElementType::Count);
nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType);
RefPtr<ServoComputedValues> computedValues =
Servo_ComputedValues_GetForPseudoElement(
aParentContext->StyleSource().AsServoComputedValues(),
aParentElement, pseudoTag, mRawSet.get(), /* is_probe = */ false).Consume();
MOZ_ASSERT(computedValues);
return GetContext(computedValues.forget(), aParentContext, pseudoTag, aType);
}
// aFlags is an nsStyleSet flags bitfield
already_AddRefed<nsStyleContext>
ServoStyleSet::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag,
nsStyleContext* aParentContext,
uint32_t aFlags)
{
MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(aPseudoTag));
MOZ_ASSERT(aFlags == 0 ||
aFlags == nsStyleSet::eSkipParentDisplayBasedStyleFixup);
bool skipFixup = aFlags & nsStyleSet::eSkipParentDisplayBasedStyleFixup;
const ServoComputedValues* parentStyle =
aParentContext ? aParentContext->StyleSource().AsServoComputedValues()
: nullptr;
RefPtr<ServoComputedValues> computedValues =
Servo_ComputedValues_GetForAnonymousBox(parentStyle, aPseudoTag,
mRawSet.get()).Consume();
#ifdef DEBUG
if (!computedValues) {
nsString pseudo;
aPseudoTag->ToString(pseudo);
NS_ERROR(nsPrintfCString("stylo: could not get anon-box: %s",
NS_ConvertUTF16toUTF8(pseudo).get()).get());
MOZ_CRASH();
}
#endif
return NS_NewStyleContext(aParentContext, mPresContext, aPseudoTag,
CSSPseudoElementType::AnonBox,
computedValues.forget(), skipFixup);
}
// manage the set of style sheets in the style set
nsresult
ServoStyleSet::AppendStyleSheet(SheetType aType,
ServoStyleSheet* aSheet)
{
MOZ_ASSERT(aSheet);
MOZ_ASSERT(aSheet->IsApplicable());
MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
mSheets[aType].RemoveElement(aSheet);
mSheets[aType].AppendElement(aSheet);
// Maintain a mirrored list of sheets on the servo side.
Servo_StyleSet_AppendStyleSheet(mRawSet.get(), aSheet->RawSheet());
return NS_OK;
}
nsresult
ServoStyleSet::PrependStyleSheet(SheetType aType,
ServoStyleSheet* aSheet)
{
MOZ_ASSERT(aSheet);
MOZ_ASSERT(aSheet->IsApplicable());
MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
mSheets[aType].RemoveElement(aSheet);
mSheets[aType].InsertElementAt(0, aSheet);
// Maintain a mirrored list of sheets on the servo side.
Servo_StyleSet_PrependStyleSheet(mRawSet.get(), aSheet->RawSheet());
return NS_OK;
}
nsresult
ServoStyleSet::RemoveStyleSheet(SheetType aType,
ServoStyleSheet* aSheet)
{
MOZ_ASSERT(aSheet);
MOZ_ASSERT(aSheet->IsApplicable());
MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
mSheets[aType].RemoveElement(aSheet);
// Maintain a mirrored list of sheets on the servo side.
Servo_StyleSet_RemoveStyleSheet(mRawSet.get(), aSheet->RawSheet());
return NS_OK;
}
nsresult
ServoStyleSet::ReplaceSheets(SheetType aType,
const nsTArray<RefPtr<ServoStyleSheet>>& aNewSheets)
{
// Gecko uses a two-dimensional array keyed by sheet type, whereas Servo
// stores a flattened list. This makes ReplaceSheets a pretty clunky thing
// to express. If the need ever arises, we can easily make this more efficent,
// probably by aligning the representations better between engines.
for (ServoStyleSheet* sheet : mSheets[aType]) {
Servo_StyleSet_RemoveStyleSheet(mRawSet.get(), sheet->RawSheet());
}
mSheets[aType].Clear();
mSheets[aType].AppendElements(aNewSheets);
for (ServoStyleSheet* sheet : mSheets[aType]) {
Servo_StyleSet_AppendStyleSheet(mRawSet.get(), sheet->RawSheet());
}
return NS_OK;
}
nsresult
ServoStyleSet::InsertStyleSheetBefore(SheetType aType,
ServoStyleSheet* aNewSheet,
ServoStyleSheet* aReferenceSheet)
{
MOZ_ASSERT(aNewSheet);
MOZ_ASSERT(aReferenceSheet);
MOZ_ASSERT(aNewSheet->IsApplicable());
mSheets[aType].RemoveElement(aNewSheet);
size_t idx = mSheets[aType].IndexOf(aReferenceSheet);
if (idx == mSheets[aType].NoIndex) {
return NS_ERROR_INVALID_ARG;
}
mSheets[aType].InsertElementAt(idx, aNewSheet);
// Maintain a mirrored list of sheets on the servo side.
Servo_StyleSet_InsertStyleSheetBefore(mRawSet.get(), aNewSheet->RawSheet(),
aReferenceSheet->RawSheet());
return NS_OK;
}
int32_t
ServoStyleSet::SheetCount(SheetType aType) const
{
MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
return mSheets[aType].Length();
}
ServoStyleSheet*
ServoStyleSet::StyleSheetAt(SheetType aType,
int32_t aIndex) const
{
MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
return mSheets[aType][aIndex];
}
nsresult
ServoStyleSet::RemoveDocStyleSheet(ServoStyleSheet* aSheet)
{
return RemoveStyleSheet(SheetType::Doc, aSheet);
}
nsresult
ServoStyleSet::AddDocStyleSheet(ServoStyleSheet* aSheet,
nsIDocument* aDocument)
{
RefPtr<StyleSheet> strong(aSheet);
mSheets[SheetType::Doc].RemoveElement(aSheet);
size_t index =
aDocument->FindDocStyleSheetInsertionPoint(mSheets[SheetType::Doc], aSheet);
mSheets[SheetType::Doc].InsertElementAt(index, aSheet);
// Maintain a mirrored list of sheets on the servo side.
ServoStyleSheet* followingSheet =
mSheets[SheetType::Doc].SafeElementAt(index + 1);
if (followingSheet) {
Servo_StyleSet_InsertStyleSheetBefore(mRawSet.get(), aSheet->RawSheet(),
followingSheet->RawSheet());
} else {
Servo_StyleSet_AppendStyleSheet(mRawSet.get(), aSheet->RawSheet());
}
return NS_OK;
}
already_AddRefed<nsStyleContext>
ServoStyleSet::ProbePseudoElementStyle(Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext)
{
MOZ_ASSERT(aParentContext);
MOZ_ASSERT(aType < CSSPseudoElementType::Count);
nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType);
RefPtr<ServoComputedValues> computedValues =
Servo_ComputedValues_GetForPseudoElement(
aParentContext->StyleSource().AsServoComputedValues(),
aParentElement, pseudoTag, mRawSet.get(), /* is_probe = */ true).Consume();
if (!computedValues) {
return nullptr;
}
// For :before and :after pseudo-elements, having display: none or no
// 'content' property is equivalent to not having the pseudo-element
// at all.
if (computedValues &&
(pseudoTag == nsCSSPseudoElements::before ||
pseudoTag == nsCSSPseudoElements::after)) {
const nsStyleDisplay *display = Servo_GetStyleDisplay(computedValues);
const nsStyleContent *content = Servo_GetStyleContent(computedValues);
// XXXldb What is contentCount for |content: ""|?
if (display->mDisplay == StyleDisplay::None ||
content->ContentCount() == 0) {
return nullptr;
}
}
return GetContext(computedValues.forget(), aParentContext, pseudoTag, aType);
}
already_AddRefed<nsStyleContext>
ServoStyleSet::ProbePseudoElementStyle(Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext,
TreeMatchContext& aTreeMatchContext,
Element* aPseudoElement)
{
if (aPseudoElement) {
NS_ERROR("stylo: We don't support CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE yet");
}
return ProbePseudoElementStyle(aParentElement, aType, aParentContext);
}
nsRestyleHint
ServoStyleSet::HasStateDependentStyle(dom::Element* aElement,
EventStates aStateMask)
{
NS_WARNING("stylo: HasStateDependentStyle always returns zero!");
return nsRestyleHint(0);
}
nsRestyleHint
ServoStyleSet::HasStateDependentStyle(dom::Element* aElement,
CSSPseudoElementType aPseudoType,
dom::Element* aPseudoElement,
EventStates aStateMask)
{
NS_WARNING("stylo: HasStateDependentStyle always returns zero!");
return nsRestyleHint(0);
}
nsRestyleHint
ServoStyleSet::ComputeRestyleHint(dom::Element* aElement,
ServoElementSnapshot* aSnapshot)
{
return Servo_ComputeRestyleHint(aElement, aSnapshot, mRawSet.get());
}
static void
ClearDirtyBits(nsIContent* aContent)
{
bool traverseDescendants = aContent->HasDirtyDescendantsForServo();
aContent->UnsetIsDirtyAndHasDirtyDescendantsForServo();
if (!traverseDescendants) {
return;
}
StyleChildrenIterator it(aContent);
for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
ClearDirtyBits(n);
}
}
void
ServoStyleSet::StyleDocument(bool aLeaveDirtyBits)
{
// Grab the root.
nsIDocument* doc = mPresContext->Document();
nsIContent* root = doc->GetRootElement();
MOZ_ASSERT(root);
// Restyle the document, clearing the dirty bits if requested.
Servo_RestyleSubtree(root, mRawSet.get());
if (!aLeaveDirtyBits) {
ClearDirtyBits(root);
doc->UnsetHasDirtyDescendantsForServo();
}
}
void
ServoStyleSet::StyleNewSubtree(nsIContent* aContent)
{
MOZ_ASSERT(aContent->IsDirtyForServo());
if (aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT)) {
Servo_RestyleSubtree(aContent, mRawSet.get());
}
ClearDirtyBits(aContent);
}
void
ServoStyleSet::StyleNewChildren(nsIContent* aParent)
{
MOZ_ASSERT(aParent->HasDirtyDescendantsForServo());
Servo_RestyleSubtree(aParent, mRawSet.get());
ClearDirtyBits(aParent);
}

View file

@ -0,0 +1,175 @@
/* -*- 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 mozilla_ServoStyleSet_h
#define mozilla_ServoStyleSet_h
#include "mozilla/EnumeratedArray.h"
#include "mozilla/EventStates.h"
#include "mozilla/ServoBindingTypes.h"
#include "mozilla/ServoElementSnapshot.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/SheetType.h"
#include "mozilla/UniquePtr.h"
#include "nsCSSPseudoElements.h"
#include "nsChangeHint.h"
#include "nsIAtom.h"
#include "nsTArray.h"
namespace mozilla {
namespace dom {
class Element;
} // namespace dom
class CSSStyleSheet;
class ServoRestyleManager;
class ServoStyleSheet;
} // namespace mozilla
class nsIDocument;
class nsStyleContext;
class nsPresContext;
struct TreeMatchContext;
namespace mozilla {
/**
* The set of style sheets that apply to a document, backed by a Servo
* Stylist. A ServoStyleSet contains ServoStyleSheets.
*/
class ServoStyleSet
{
friend class ServoRestyleManager;
public:
ServoStyleSet();
void Init(nsPresContext* aPresContext);
void BeginShutdown();
void Shutdown();
bool GetAuthorStyleDisabled() const;
nsresult SetAuthorStyleDisabled(bool aStyleDisabled);
void BeginUpdate();
nsresult EndUpdate();
already_AddRefed<nsStyleContext>
ResolveStyleFor(dom::Element* aElement,
nsStyleContext* aParentContext);
already_AddRefed<nsStyleContext>
ResolveStyleFor(dom::Element* aElement,
nsStyleContext* aParentContext,
TreeMatchContext& aTreeMatchContext);
already_AddRefed<nsStyleContext>
ResolveStyleForText(nsIContent* aTextNode,
nsStyleContext* aParentContext);
already_AddRefed<nsStyleContext>
ResolveStyleForOtherNonElement(nsStyleContext* aParentContext);
already_AddRefed<nsStyleContext>
ResolvePseudoElementStyle(dom::Element* aParentElement,
mozilla::CSSPseudoElementType aType,
nsStyleContext* aParentContext,
dom::Element* aPseudoElement);
// aFlags is an nsStyleSet flags bitfield
already_AddRefed<nsStyleContext>
ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag, nsStyleContext* aParentContext,
uint32_t aFlags = 0);
// manage the set of style sheets in the style set
nsresult AppendStyleSheet(SheetType aType, ServoStyleSheet* aSheet);
nsresult PrependStyleSheet(SheetType aType, ServoStyleSheet* aSheet);
nsresult RemoveStyleSheet(SheetType aType, ServoStyleSheet* aSheet);
nsresult ReplaceSheets(SheetType aType,
const nsTArray<RefPtr<ServoStyleSheet>>& aNewSheets);
nsresult InsertStyleSheetBefore(SheetType aType,
ServoStyleSheet* aNewSheet,
ServoStyleSheet* aReferenceSheet);
int32_t SheetCount(SheetType aType) const;
ServoStyleSheet* StyleSheetAt(SheetType aType, int32_t aIndex) const;
nsresult RemoveDocStyleSheet(ServoStyleSheet* aSheet);
nsresult AddDocStyleSheet(ServoStyleSheet* aSheet, nsIDocument* aDocument);
// check whether there is ::before/::after style for an element
already_AddRefed<nsStyleContext>
ProbePseudoElementStyle(dom::Element* aParentElement,
mozilla::CSSPseudoElementType aType,
nsStyleContext* aParentContext);
already_AddRefed<nsStyleContext>
ProbePseudoElementStyle(dom::Element* aParentElement,
mozilla::CSSPseudoElementType aType,
nsStyleContext* aParentContext,
TreeMatchContext& aTreeMatchContext,
dom::Element* aPseudoElement = nullptr);
// Test if style is dependent on content state
nsRestyleHint HasStateDependentStyle(dom::Element* aElement,
EventStates aStateMask);
nsRestyleHint HasStateDependentStyle(
dom::Element* aElement, mozilla::CSSPseudoElementType aPseudoType,
dom::Element* aPseudoElement, EventStates aStateMask);
/**
* Computes a restyle hint given a element and a previous element snapshot.
*/
nsRestyleHint ComputeRestyleHint(dom::Element* aElement,
ServoElementSnapshot* aSnapshot);
/**
* Performs a Servo traversal to compute style for all dirty nodes in the
* document. The root element must be non-null.
*
* If aLeaveDirtyBits is true, the dirty/dirty-descendant bits are not
* cleared.
*/
void StyleDocument(bool aLeaveDirtyBits);
/**
* Eagerly styles a subtree of dirty nodes that were just appended to the
* tree. This is used in situations where we need the style immediately and
* cannot wait for a future batch restyle.
*
* The subtree must have the root dirty bit set, which currently gets
* propagated to all descendants. The dirty bits are cleared before
* returning.
*/
void StyleNewSubtree(nsIContent* aContent);
/**
* Like the above, but does not assume that the root node is dirty. When
* appending multiple children to a potentially-non-dirty node, it's
* preferable to call StyleNewChildren on the node rather than making multiple
* calls to StyleNewSubtree on each child, since it allows for more
* parallelism.
*/
void StyleNewChildren(nsIContent* aParent);
private:
already_AddRefed<nsStyleContext> GetContext(already_AddRefed<ServoComputedValues>,
nsStyleContext* aParentContext,
nsIAtom* aPseudoTag,
CSSPseudoElementType aPseudoType);
already_AddRefed<nsStyleContext> GetContext(nsIContent* aContent,
nsStyleContext* aParentContext,
nsIAtom* aPseudoTag,
CSSPseudoElementType aPseudoType);
nsPresContext* mPresContext;
UniquePtr<RawServoStyleSet> mRawSet;
EnumeratedArray<SheetType, SheetType::Count,
nsTArray<RefPtr<ServoStyleSheet>>> mSheets;
int32_t mBatching;
};
} // namespace mozilla
#endif // mozilla_ServoStyleSet_h

View file

@ -0,0 +1,143 @@
/* -*- 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/ServoStyleSheet.h"
#include "mozilla/StyleBackendType.h"
namespace mozilla {
ServoStyleSheet::ServoStyleSheet(css::SheetParsingMode aParsingMode,
CORSMode aCORSMode,
net::ReferrerPolicy aReferrerPolicy,
const dom::SRIMetadata& aIntegrity)
: StyleSheet(StyleBackendType::Servo, aParsingMode)
, mSheetInfo(aCORSMode, aReferrerPolicy, aIntegrity)
{
}
ServoStyleSheet::~ServoStyleSheet()
{
DropSheet();
}
bool
ServoStyleSheet::HasRules() const
{
return mSheet && Servo_StyleSheet_HasRules(mSheet);
}
void
ServoStyleSheet::SetOwningDocument(nsIDocument* aDocument)
{
// XXXheycam: Traverse to child ServoStyleSheets to set this, like
// CSSStyleSheet::SetOwningDocument does.
mDocument = aDocument;
}
ServoStyleSheet*
ServoStyleSheet::GetParentSheet() const
{
// XXXheycam: When we implement support for child sheets, we'll have
// to fix SetOwningDocument to propagate the owning document down
// to the children.
MOZ_CRASH("stylo: not implemented");
}
void
ServoStyleSheet::AppendStyleSheet(ServoStyleSheet* aSheet)
{
// XXXheycam: When we implement support for child sheets, we'll have
// to fix SetOwningDocument to propagate the owning document down
// to the children.
MOZ_CRASH("stylo: not implemented");
}
nsresult
ServoStyleSheet::ParseSheet(const nsAString& aInput,
nsIURI* aSheetURI,
nsIURI* aBaseURI,
nsIPrincipal* aSheetPrincipal,
uint32_t aLineNumber)
{
DropSheet();
RefPtr<ThreadSafeURIHolder> base = new ThreadSafeURIHolder(aBaseURI);
RefPtr<ThreadSafeURIHolder> referrer = new ThreadSafeURIHolder(aSheetURI);
RefPtr<ThreadSafePrincipalHolder> principal =
new ThreadSafePrincipalHolder(aSheetPrincipal);
nsCString baseString;
nsresult rv = aBaseURI->GetSpec(baseString);
NS_ENSURE_SUCCESS(rv, rv);
NS_ConvertUTF16toUTF8 input(aInput);
mSheet = Servo_StyleSheet_FromUTF8Bytes(&input, mParsingMode, &baseString,
base, referrer, principal).Consume();
return NS_OK;
}
void
ServoStyleSheet::LoadFailed()
{
mSheet = Servo_StyleSheet_Empty(mParsingMode).Consume();
}
void
ServoStyleSheet::DropSheet()
{
mSheet = nullptr;
}
size_t
ServoStyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
MOZ_CRASH("stylo: not implemented");
}
#ifdef DEBUG
void
ServoStyleSheet::List(FILE* aOut, int32_t aIndex) const
{
MOZ_CRASH("stylo: not implemented");
}
#endif
nsMediaList*
ServoStyleSheet::Media()
{
return nullptr;
}
nsIDOMCSSRule*
ServoStyleSheet::GetDOMOwnerRule() const
{
return nullptr;
}
CSSRuleList*
ServoStyleSheet::GetCssRulesInternal(ErrorResult& aRv)
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return nullptr;
}
uint32_t
ServoStyleSheet::InsertRuleInternal(const nsAString& aRule,
uint32_t aIndex, ErrorResult& aRv)
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return 0;
}
void
ServoStyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv)
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
}
} // namespace mozilla

View file

@ -0,0 +1,90 @@
/* -*- 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 mozilla_ServoStyleSheet_h
#define mozilla_ServoStyleSheet_h
#include "mozilla/dom/SRIMetadata.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ServoBindingTypes.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInfo.h"
#include "nsStringFwd.h"
namespace mozilla {
/**
* CSS style sheet object that is a wrapper for a Servo Stylesheet.
*/
class ServoStyleSheet : public StyleSheet
{
public:
ServoStyleSheet(css::SheetParsingMode aParsingMode,
CORSMode aCORSMode,
net::ReferrerPolicy aReferrerPolicy,
const dom::SRIMetadata& aIntegrity);
bool HasRules() const;
void SetOwningDocument(nsIDocument* aDocument);
ServoStyleSheet* GetParentSheet() const;
void AppendStyleSheet(ServoStyleSheet* aSheet);
MOZ_MUST_USE nsresult ParseSheet(const nsAString& aInput,
nsIURI* aSheetURI,
nsIURI* aBaseURI,
nsIPrincipal* aSheetPrincipal,
uint32_t aLineNumber);
/**
* Called instead of ParseSheet to initialize the Servo stylesheet object
* for a failed load. Either ParseSheet or LoadFailed must be called before
* adding a ServoStyleSheet to a ServoStyleSet.
*/
void LoadFailed();
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
#ifdef DEBUG
void List(FILE* aOut = stdout, int32_t aIndex = 0) const;
#endif
RawServoStyleSheet* RawSheet() const { return mSheet; }
// WebIDL StyleSheet API
nsMediaList* Media() final;
// WebIDL CSSStyleSheet API
// Can't be inline because we can't include ImportRule here. And can't be
// called GetOwnerRule because that would be ambiguous with the ImportRule
// version.
nsIDOMCSSRule* GetDOMOwnerRule() const final;
void WillDirty() {}
void DidDirty() {}
protected:
virtual ~ServoStyleSheet();
// Internal methods which do not have security check and completeness check.
dom::CSSRuleList* GetCssRulesInternal(ErrorResult& aRv);
uint32_t InsertRuleInternal(const nsAString& aRule,
uint32_t aIndex, ErrorResult& aRv);
void DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv);
private:
void DropSheet();
RefPtr<RawServoStyleSheet> mSheet;
StyleSheetInfo mSheetInfo;
friend class StyleSheet;
};
} // namespace mozilla
#endif // mozilla_ServoStyleSheet_h

40
layout/style/ServoTypes.h Normal file
View file

@ -0,0 +1,40 @@
/* -*- 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 mozilla_ServoTypes_h
#define mozilla_ServoTypes_h
/*
* Type definitions used to interact with Servo. This gets included by nsINode,
* so don't add significant include dependencies to this file.
*/
struct ServoNodeData;
namespace mozilla {
/*
* Replaced types. These get mapped to associated Servo types in bindgen.
*/
template<typename T>
struct ServoUnsafeCell {
T value;
// Ensure that primitive types (i.e. pointers) get zero-initialized.
ServoUnsafeCell() : value() {};
};
template<typename T>
struct ServoCell {
ServoUnsafeCell<T> value;
T Get() const { return value.value; }
void Set(T arg) { value.value = arg; }
ServoCell() : value() {};
};
} // namespace mozilla
#endif // mozilla_ServoTypes_h

82
layout/style/ServoUtils.h Normal file
View file

@ -0,0 +1,82 @@
/* -*- 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/. */
/* some utilities for stylo */
#ifndef mozilla_ServoUtils_h
#define mozilla_ServoUtils_h
#include "mozilla/TypeTraits.h"
#ifdef MOZ_STYLO
# define MOZ_DECL_STYLO_CHECK_METHODS \
bool IsGecko() const { return !IsServo(); } \
bool IsServo() const { return mType == StyleBackendType::Servo; }
#else
# define MOZ_DECL_STYLO_CHECK_METHODS \
bool IsGecko() const { return true; } \
bool IsServo() const { return false; }
#endif
/**
* Macro used in a base class of |geckotype_| and |servotype_|.
* The class should define |StyleBackendType mType;| itself.
*/
#define MOZ_DECL_STYLO_METHODS(geckotype_, servotype_) \
MOZ_DECL_STYLO_CHECK_METHODS \
inline geckotype_* AsGecko(); \
inline servotype_* AsServo(); \
inline const geckotype_* AsGecko() const; \
inline const servotype_* AsServo() const;
/**
* Macro used in inline header of class |type_| with its Gecko and Servo
* subclasses named |geckotype_| and |servotype_| correspondingly for
* implementing the inline methods defined by MOZ_DECL_STYLO_METHODS.
*/
#define MOZ_DEFINE_STYLO_METHODS(type_, geckotype_, servotype_) \
geckotype_* type_::AsGecko() { \
MOZ_ASSERT(IsGecko()); \
return static_cast<geckotype_*>(this); \
} \
servotype_* type_::AsServo() { \
MOZ_ASSERT(IsServo()); \
return static_cast<servotype_*>(this); \
} \
const geckotype_* type_::AsGecko() const { \
MOZ_ASSERT(IsGecko()); \
return static_cast<const geckotype_*>(this); \
} \
const servotype_* type_::AsServo() const { \
MOZ_ASSERT(IsServo()); \
return static_cast<const servotype_*>(this); \
}
#define MOZ_STYLO_THIS_TYPE mozilla::RemovePointer<decltype(this)>::Type
#define MOZ_STYLO_GECKO_TYPE mozilla::RemovePointer<decltype(AsGecko())>::Type
#define MOZ_STYLO_SERVO_TYPE mozilla::RemovePointer<decltype(AsServo())>::Type
/**
* Macro used to forward a method call to the concrete method defined by
* the Servo or Gecko implementation. The class of the method using it
* should use MOZ_DECL_STYLO_METHODS to define basic stylo methods.
*/
#define MOZ_STYLO_FORWARD_CONCRETE(method_, geckoargs_, servoargs_) \
static_assert(!mozilla::IsSame<decltype(&MOZ_STYLO_THIS_TYPE::method_), \
decltype(&MOZ_STYLO_GECKO_TYPE::method_)> \
::value, "Gecko subclass should define its own " #method_); \
static_assert(!mozilla::IsSame<decltype(&MOZ_STYLO_THIS_TYPE::method_), \
decltype(&MOZ_STYLO_SERVO_TYPE::method_)> \
::value, "Servo subclass should define its own " #method_); \
if (IsServo()) { \
return AsServo()->method_ servoargs_; \
} \
return AsGecko()->method_ geckoargs_;
#define MOZ_STYLO_FORWARD(method_, args_) \
MOZ_STYLO_FORWARD_CONCRETE(method_, args_, args_)
#endif // mozilla_ServoUtils_h

View file

@ -0,0 +1,43 @@
/* -*- 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 mozilla_css_SheetParsingMode_h
#define mozilla_css_SheetParsingMode_h
namespace mozilla {
namespace css {
/**
* Enum defining the mode in which a sheet is to be parsed. This is
* usually, but not always, the same as the cascade level at which the
* sheet will apply (see nsStyleSet.h). Most of the Loader APIs only
* support loading of author sheets.
*
* Author sheets are the normal case: styles embedded in or linked
* from HTML pages. They are also the most restricted.
*
* User sheets can do anything author sheets can do, and also get
* access to a few CSS extensions that are not yet suitable for
* exposure on the public Web, but are very useful for expressing
* user style overrides, such as @-moz-document rules.
*
* Agent sheets have access to all author- and user-sheet features
* plus more extensions that are necessary for internal use but,
* again, not yet suitable for exposure on the public Web. Some of
* these are outright unsafe to expose; in particular, incorrect
* styling of anonymous box pseudo-elements can violate layout
* invariants.
*/
enum SheetParsingMode {
eAuthorSheetFeatures = 0,
eUserSheetFeatures,
eAgentSheetFeatures
};
} // namespace css
} // namespace mozilla
#endif // mozilla_css_SheetParsingMode_h

38
layout/style/SheetType.h Normal file
View file

@ -0,0 +1,38 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* enum to represent a level in the cascade */
#ifndef mozilla_SheetType_h
#define mozilla_SheetType_h
#include <stdint.h>
namespace mozilla {
// The "origins" of the CSS cascade, from lowest precedence to
// highest (for non-!important rules).
//
// Be sure to update NS_RULE_NODE_LEVEL_MASK when changing the number
// of sheet types; static assertions enforce this.
enum class SheetType : uint8_t {
Agent, // CSS
User, // CSS
PresHint,
SVGAttrAnimation,
Doc, // CSS
ScopedDoc,
StyleAttr,
Override, // CSS
Animation,
Transition,
Count,
Unknown = 0xff
};
} // namespace mozilla
#endif // mozilla_SheetType_h

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,602 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* Utilities for animation of computed style values */
#ifndef mozilla_StyleAnimationValue_h_
#define mozilla_StyleAnimationValue_h_
#include "mozilla/gfx/MatrixFwd.h"
#include "mozilla/UniquePtr.h"
#include "nsStringFwd.h"
#include "nsStringBuffer.h"
#include "nsCoord.h"
#include "nsColor.h"
#include "nsCSSProps.h"
#include "nsCSSValue.h"
#include "nsStyleCoord.h"
class nsIFrame;
class nsStyleContext;
class gfx3DMatrix;
struct RawServoDeclarationBlock;
namespace mozilla {
namespace css {
class StyleRule;
} // namespace css
namespace dom {
class Element;
} // namespace dom
enum class CSSPseudoElementType : uint8_t;
struct PropertyStyleAnimationValuePair;
/**
* Utility class to handle animated style values
*/
class StyleAnimationValue {
public:
// Mathematical methods
// --------------------
/**
* Adds |aCount| copies of |aValueToAdd| to |aDest|. The result of this
* addition is stored in aDest.
*
* Note that if |aCount| is 0, then |aDest| will be unchanged. Also, if
* this method fails, then |aDest| will be unchanged.
*
* @param aDest The value to add to.
* @param aValueToAdd The value to add.
* @param aCount The number of times to add aValueToAdd.
* @return true on success, false on failure.
*/
static MOZ_MUST_USE bool
Add(nsCSSPropertyID aProperty, StyleAnimationValue& aDest,
const StyleAnimationValue& aValueToAdd, uint32_t aCount) {
return AddWeighted(aProperty, 1.0, aDest, aCount, aValueToAdd, aDest);
}
/**
* Calculates a measure of 'distance' between two colors.
*
* @param aStartColor The start of the interval for which the distance
* should be calculated.
* @param aEndColor The end of the interval for which the distance
* should be calculated.
* @return the result of the calculation.
*/
static double ComputeColorDistance(const css::RGBAColorData& aStartColor,
const css::RGBAColorData& aEndColor);
/**
* Calculates a measure of 'distance' between two values.
*
* This measure of Distance is guaranteed to be proportional to
* portions passed to Interpolate, Add, or AddWeighted. However, for
* some types of StyleAnimationValue it may not produce sensible results
* for paced animation.
*
* If this method succeeds, the returned distance value is guaranteed to be
* non-negative.
*
* @param aStartValue The start of the interval for which the distance
* should be calculated.
* @param aEndValue The end of the interval for which the distance
* should be calculated.
* @param aStyleContext The style context to use for processing the
* translate part of transforms.
* @param aDistance The result of the calculation.
* @return true on success, false on failure.
*/
static MOZ_MUST_USE bool
ComputeDistance(nsCSSPropertyID aProperty,
const StyleAnimationValue& aStartValue,
const StyleAnimationValue& aEndValue,
nsStyleContext* aStyleContext,
double& aDistance);
/**
* Calculates an interpolated value that is the specified |aPortion| between
* the two given values.
*
* This really just does the following calculation:
* aResultValue = (1.0 - aPortion) * aStartValue + aPortion * aEndValue
*
* @param aStartValue The value defining the start of the interval of
* interpolation.
* @param aEndValue The value defining the end of the interval of
* interpolation.
* @param aPortion A number in the range [0.0, 1.0] defining the
* distance of the interpolated value in the interval.
* @param [out] aResultValue The resulting interpolated value.
* @return true on success, false on failure.
*/
static MOZ_MUST_USE bool
Interpolate(nsCSSPropertyID aProperty,
const StyleAnimationValue& aStartValue,
const StyleAnimationValue& aEndValue,
double aPortion,
StyleAnimationValue& aResultValue) {
return AddWeighted(aProperty, 1.0 - aPortion, aStartValue,
aPortion, aEndValue, aResultValue);
}
/**
* Does the calculation:
* aResultValue = aCoeff1 * aValue1 + aCoeff2 * aValue2
*
* @param [out] aResultValue The resulting interpolated value. May be
* the same as aValue1 or aValue2.
* @return true on success, false on failure.
*
* NOTE: Current callers always pass aCoeff1 and aCoeff2 >= 0. They
* are currently permitted to be negative; however, if, as we add
* support more value types types, we find that this causes
* difficulty, we might change this to restrict them to being
* positive.
*/
static MOZ_MUST_USE bool
AddWeighted(nsCSSPropertyID aProperty,
double aCoeff1, const StyleAnimationValue& aValue1,
double aCoeff2, const StyleAnimationValue& aValue2,
StyleAnimationValue& aResultValue);
/**
* Accumulates |aValueToAccumulate| onto |aDest| |aCount| times.
* The result is stored in |aDest| on success.
*
* @param aDest The base value to be accumulated.
* @param aValueToAccumulate The value to accumulate.
* @param aCount The number of times to accumulate
* aValueToAccumulate.
* @return true on success, false on failure.
*
* NOTE: This function will work as a wrapper of StyleAnimationValue::Add()
* if |aProperty| isn't color or shadow or filter. For these properties,
* this function may return a color value that at least one of its components
* has a value which is outside the range [0, 1] so that we can calculate
* plausible values as interpolation with the return value.
*/
static MOZ_MUST_USE bool
Accumulate(nsCSSPropertyID aProperty, StyleAnimationValue& aDest,
const StyleAnimationValue& aValueToAccumulate,
uint64_t aCount);
// Type-conversion methods
// -----------------------
/**
* Creates a computed value for the given specified value
* (property ID + string). A style context is needed in case the
* specified value depends on inherited style or on the values of other
* properties.
*
* @param aProperty The property whose value we're computing.
* @param aTargetElement The content node to which our computed value is
* applicable. For pseudo-elements, this is the parent
* element to which the pseudo is attached, not the
* generated content node.
* @param aStyleContext The style context used to compute values from the
* specified value. For pseudo-elements, this should
* be the style context corresponding to the pseudo
* element.
* @param aSpecifiedValue The specified value, from which we'll build our
* computed value.
* @param aUseSVGMode A flag to indicate whether we should parse
* |aSpecifiedValue| in SVG mode.
* @param [out] aComputedValue The resulting computed value.
* @param [out] aIsContextSensitive
* Set to true if |aSpecifiedValue| may produce
* a different |aComputedValue| depending on other CSS
* properties on |aTargetElement| or its ancestors.
* false otherwise.
* Note that the operation of this method is
* significantly faster when |aIsContextSensitive| is
* nullptr.
* @return true on success, false on failure.
*/
static MOZ_MUST_USE bool
ComputeValue(nsCSSPropertyID aProperty,
mozilla::dom::Element* aTargetElement,
nsStyleContext* aStyleContext,
const nsAString& aSpecifiedValue,
bool aUseSVGMode,
StyleAnimationValue& aComputedValue,
bool* aIsContextSensitive = nullptr);
/**
* Like ComputeValue, but returns an array of StyleAnimationValues.
*
* On success, when aProperty is a longhand, aResult will have a single
* value in it. When aProperty is a shorthand, aResult will be filled with
* values for all of aProperty's longhand components. aEnabledState
* is used to filter the longhand components that will be appended
* to aResult. On failure, aResult might still have partial results
* in it.
*/
static MOZ_MUST_USE bool
ComputeValues(nsCSSPropertyID aProperty,
mozilla::CSSEnabledState aEnabledState,
mozilla::dom::Element* aTargetElement,
nsStyleContext* aStyleContext,
const nsAString& aSpecifiedValue,
bool aUseSVGMode,
nsTArray<PropertyStyleAnimationValuePair>& aResult);
/**
* A variant on ComputeValues that takes an nsCSSValue as the specified
* value. Only longhand properties are supported.
*/
static MOZ_MUST_USE bool
ComputeValues(nsCSSPropertyID aProperty,
mozilla::CSSEnabledState aEnabledState,
mozilla::dom::Element* aTargetElement,
nsStyleContext* aStyleContext,
const nsCSSValue& aSpecifiedValue,
bool aUseSVGMode,
nsTArray<PropertyStyleAnimationValuePair>& aResult);
/**
* A variant of ComputeValues that takes a RawServoDeclarationBlock
* as the specified value.
*/
static MOZ_MUST_USE bool
ComputeValues(nsCSSPropertyID aProperty,
mozilla::CSSEnabledState aEnabledState,
nsStyleContext* aStyleContext,
const RawServoDeclarationBlock& aDeclarations,
nsTArray<PropertyStyleAnimationValuePair>& aValues);
/**
* Creates a specified value for the given computed value.
*
* The first two overloads fill in an nsCSSValue object; the third
* produces a string. For the overload that takes a const
* StyleAnimationValue& reference, the nsCSSValue result may depend on
* objects owned by the |aComputedValue| object, so users of that variant
* must keep |aComputedValue| alive longer than |aSpecifiedValue|.
* The overload that takes an rvalue StyleAnimationValue reference
* transfers ownership for some resources such that the |aComputedValue|
* does not depend on the lifetime of |aSpecifiedValue|.
*
* @param aProperty The property whose value we're uncomputing.
* @param aComputedValue The computed value to be converted.
* @param [out] aSpecifiedValue The resulting specified value.
* @return true on success, false on failure.
*
* These functions are not MOZ_MUST_USE because failing to check the return
* value is common and reasonable.
*/
static MOZ_MUST_USE bool
UncomputeValue(nsCSSPropertyID aProperty,
const StyleAnimationValue& aComputedValue,
nsCSSValue& aSpecifiedValue);
static MOZ_MUST_USE bool
UncomputeValue(nsCSSPropertyID aProperty,
StyleAnimationValue&& aComputedValue,
nsCSSValue& aSpecifiedValue);
static MOZ_MUST_USE bool
UncomputeValue(nsCSSPropertyID aProperty,
const StyleAnimationValue& aComputedValue,
nsAString& aSpecifiedValue);
/**
* Gets the computed value for the given property from the given style
* context.
*
* Obtaining the computed value allows us to animate properties when the
* content author has specified a value like "inherit" or "initial" or some
* other keyword that isn't directly interpolatable, but which *computes* to
* something interpolatable.
*
* @param aProperty The property whose value we're looking up.
* @param aStyleContext The style context to check for the computed value.
* @param [out] aComputedValue The resulting computed value.
* @return true on success, false on failure.
*/
static MOZ_MUST_USE bool ExtractComputedValue(
nsCSSPropertyID aProperty,
nsStyleContext* aStyleContext,
StyleAnimationValue& aComputedValue);
/**
* Interpolates between 2 matrices by decomposing them.
*
* @param aMatrix1 First matrix, using CSS pixel units.
* @param aMatrix2 Second matrix, using CSS pixel units.
* @param aProgress Interpolation value in the range [0.0, 1.0]
*/
static gfx::Matrix4x4 InterpolateTransformMatrix(const gfx::Matrix4x4 &aMatrix1,
const gfx::Matrix4x4 &aMatrix2,
double aProgress);
static already_AddRefed<nsCSSValue::Array>
AppendTransformFunction(nsCSSKeyword aTransformFunction,
nsCSSValueList**& aListTail);
/**
* The types and values for the values that we extract and animate.
*/
enum Unit {
eUnit_Null, // not initialized
eUnit_Normal,
eUnit_Auto,
eUnit_None,
eUnit_Enumerated,
eUnit_Visibility, // special case for transitions (which converts
// Enumerated to Visibility as needed)
eUnit_Integer,
eUnit_Coord,
eUnit_Percent,
eUnit_Float,
eUnit_Color, // nsCSSValue* (never null), always with an nscolor or
// an nsCSSValueFloatColor
eUnit_CurrentColor,
eUnit_ComplexColor, // ComplexColorValue* (never null)
eUnit_Calc, // nsCSSValue* (never null), always with a single
// calc() expression that's either length or length+percent
eUnit_ObjectPosition, // nsCSSValue* (never null), always with a
// 4-entry nsCSSValue::Array
eUnit_URL, // nsCSSValue* (never null), always with a css::URLValue
eUnit_DiscreteCSSValue, // nsCSSValue* (never null)
eUnit_CSSValuePair, // nsCSSValuePair* (never null)
eUnit_CSSValueTriplet, // nsCSSValueTriplet* (never null)
eUnit_CSSRect, // nsCSSRect* (never null)
eUnit_Dasharray, // nsCSSValueList* (never null)
eUnit_Shadow, // nsCSSValueList* (may be null)
eUnit_Shape, // nsCSSValue::Array* (never null)
eUnit_Filter, // nsCSSValueList* (may be null)
eUnit_Transform, // nsCSSValueList* (never null)
eUnit_BackgroundPositionCoord, // nsCSSValueList* (never null)
eUnit_CSSValuePairList, // nsCSSValuePairList* (never null)
eUnit_UnparsedString // nsStringBuffer* (never null)
};
private:
Unit mUnit;
union {
int32_t mInt;
nscoord mCoord;
float mFloat;
nsCSSValue* mCSSValue;
nsCSSValuePair* mCSSValuePair;
nsCSSValueTriplet* mCSSValueTriplet;
nsCSSRect* mCSSRect;
nsCSSValue::Array* mCSSValueArray;
nsCSSValueList* mCSSValueList;
nsCSSValueSharedList* mCSSValueSharedList;
nsCSSValuePairList* mCSSValuePairList;
nsStringBuffer* mString;
css::ComplexColorValue* mComplexColor;
} mValue;
public:
Unit GetUnit() const {
NS_ASSERTION(mUnit != eUnit_Null, "uninitialized");
return mUnit;
}
// Accessor to let us verify assumptions about presence of null unit,
// without tripping the assertion in GetUnit().
bool IsNull() const {
return mUnit == eUnit_Null;
}
int32_t GetIntValue() const {
NS_ASSERTION(IsIntUnit(mUnit), "unit mismatch");
return mValue.mInt;
}
nscoord GetCoordValue() const {
NS_ASSERTION(mUnit == eUnit_Coord, "unit mismatch");
return mValue.mCoord;
}
float GetPercentValue() const {
NS_ASSERTION(mUnit == eUnit_Percent, "unit mismatch");
return mValue.mFloat;
}
float GetFloatValue() const {
NS_ASSERTION(mUnit == eUnit_Float, "unit mismatch");
return mValue.mFloat;
}
nsCSSValue* GetCSSValueValue() const {
NS_ASSERTION(IsCSSValueUnit(mUnit), "unit mismatch");
return mValue.mCSSValue;
}
nsCSSValuePair* GetCSSValuePairValue() const {
NS_ASSERTION(IsCSSValuePairUnit(mUnit), "unit mismatch");
return mValue.mCSSValuePair;
}
nsCSSValueTriplet* GetCSSValueTripletValue() const {
NS_ASSERTION(IsCSSValueTripletUnit(mUnit), "unit mismatch");
return mValue.mCSSValueTriplet;
}
nsCSSRect* GetCSSRectValue() const {
NS_ASSERTION(IsCSSRectUnit(mUnit), "unit mismatch");
return mValue.mCSSRect;
}
nsCSSValue::Array* GetCSSValueArrayValue() const {
NS_ASSERTION(IsCSSValueArrayUnit(mUnit), "unit mismatch");
return mValue.mCSSValueArray;
}
nsCSSValueList* GetCSSValueListValue() const {
NS_ASSERTION(IsCSSValueListUnit(mUnit), "unit mismatch");
return mValue.mCSSValueList;
}
nsCSSValueSharedList* GetCSSValueSharedListValue() const {
NS_ASSERTION(IsCSSValueSharedListValue(mUnit), "unit mismatch");
return mValue.mCSSValueSharedList;
}
nsCSSValuePairList* GetCSSValuePairListValue() const {
NS_ASSERTION(IsCSSValuePairListUnit(mUnit), "unit mismatch");
return mValue.mCSSValuePairList;
}
const char16_t* GetStringBufferValue() const {
NS_ASSERTION(IsStringUnit(mUnit), "unit mismatch");
return GetBufferValue(mValue.mString);
}
void GetStringValue(nsAString& aBuffer) const {
NS_ASSERTION(IsStringUnit(mUnit), "unit mismatch");
aBuffer.Truncate();
uint32_t len = NS_strlen(GetBufferValue(mValue.mString));
mValue.mString->ToString(len, aBuffer);
}
/// @return the scale for this value, calculated with reference to @aForFrame.
gfxSize GetScaleValue(const nsIFrame* aForFrame) const;
const css::ComplexColorData& GetComplexColorData() const {
MOZ_ASSERT(mUnit == eUnit_ComplexColor, "unit mismatch");
return *mValue.mComplexColor;
}
StyleComplexColor GetStyleComplexColorValue() const {
return GetComplexColorData().ToComplexColor();
}
UniquePtr<nsCSSValueList> TakeCSSValueListValue() {
nsCSSValueList* list = GetCSSValueListValue();
mValue.mCSSValueList = nullptr;
mUnit = eUnit_Null;
return UniquePtr<nsCSSValueList>(list);
}
UniquePtr<nsCSSValuePairList> TakeCSSValuePairListValue() {
nsCSSValuePairList* list = GetCSSValuePairListValue();
mValue.mCSSValuePairList = nullptr;
mUnit = eUnit_Null;
return UniquePtr<nsCSSValuePairList>(list);
}
explicit StyleAnimationValue(Unit aUnit = eUnit_Null) : mUnit(aUnit) {
NS_ASSERTION(aUnit == eUnit_Null || aUnit == eUnit_Normal ||
aUnit == eUnit_Auto || aUnit == eUnit_None,
"must be valueless unit");
}
StyleAnimationValue(const StyleAnimationValue& aOther)
: mUnit(eUnit_Null) { *this = aOther; }
StyleAnimationValue(StyleAnimationValue&& aOther)
: mUnit(aOther.mUnit)
, mValue(aOther.mValue)
{
aOther.mUnit = eUnit_Null;
}
enum IntegerConstructorType { IntegerConstructor };
StyleAnimationValue(int32_t aInt, Unit aUnit, IntegerConstructorType);
enum CoordConstructorType { CoordConstructor };
StyleAnimationValue(nscoord aLength, CoordConstructorType);
enum PercentConstructorType { PercentConstructor };
StyleAnimationValue(float aPercent, PercentConstructorType);
enum FloatConstructorType { FloatConstructor };
StyleAnimationValue(float aFloat, FloatConstructorType);
enum ColorConstructorType { ColorConstructor };
StyleAnimationValue(nscolor aColor, ColorConstructorType);
~StyleAnimationValue() { FreeValue(); }
void SetNormalValue();
void SetAutoValue();
void SetNoneValue();
void SetIntValue(int32_t aInt, Unit aUnit);
template<typename T,
typename = typename std::enable_if<std::is_enum<T>::value>::type>
void SetIntValue(T aInt, Unit aUnit)
{
static_assert(mozilla::EnumTypeFitsWithin<T, int32_t>::value,
"aValue must be an enum that fits within mValue.mInt");
SetIntValue(static_cast<int32_t>(aInt), aUnit);
}
void SetCoordValue(nscoord aCoord);
void SetPercentValue(float aPercent);
void SetFloatValue(float aFloat);
void SetColorValue(nscolor aColor);
void SetCurrentColorValue();
void SetComplexColorValue(const StyleComplexColor& aColor);
void SetComplexColorValue(already_AddRefed<css::ComplexColorValue> aValue);
void SetUnparsedStringValue(const nsString& aString);
void SetCSSValueArrayValue(nsCSSValue::Array* aValue, Unit aUnit);
// These setters take ownership of |aValue|, and are therefore named
// "SetAndAdopt*".
void SetAndAdoptCSSValueValue(nsCSSValue *aValue, Unit aUnit);
void SetAndAdoptCSSValuePairValue(nsCSSValuePair *aValue, Unit aUnit);
void SetAndAdoptCSSValueTripletValue(nsCSSValueTriplet *aValue, Unit aUnit);
void SetAndAdoptCSSRectValue(nsCSSRect *aValue, Unit aUnit);
void SetAndAdoptCSSValueListValue(nsCSSValueList *aValue, Unit aUnit);
void SetAndAdoptCSSValuePairListValue(nsCSSValuePairList *aValue);
void SetTransformValue(nsCSSValueSharedList* aList);
StyleAnimationValue& operator=(const StyleAnimationValue& aOther);
StyleAnimationValue& operator=(StyleAnimationValue&& aOther)
{
MOZ_ASSERT(this != &aOther, "Do not move itself");
if (this != &aOther) {
FreeValue();
mUnit = aOther.mUnit;
mValue = aOther.mValue;
aOther.mUnit = eUnit_Null;
}
return *this;
}
bool operator==(const StyleAnimationValue& aOther) const;
bool operator!=(const StyleAnimationValue& aOther) const
{ return !(*this == aOther); }
private:
void FreeValue();
static const char16_t* GetBufferValue(nsStringBuffer* aBuffer) {
return static_cast<char16_t*>(aBuffer->Data());
}
static bool IsIntUnit(Unit aUnit) {
return aUnit == eUnit_Enumerated || aUnit == eUnit_Visibility ||
aUnit == eUnit_Integer;
}
static bool IsCSSValueUnit(Unit aUnit) {
return aUnit == eUnit_Color ||
aUnit == eUnit_Calc ||
aUnit == eUnit_ObjectPosition ||
aUnit == eUnit_URL ||
aUnit == eUnit_DiscreteCSSValue;
}
static bool IsCSSValuePairUnit(Unit aUnit) {
return aUnit == eUnit_CSSValuePair;
}
static bool IsCSSValueTripletUnit(Unit aUnit) {
return aUnit == eUnit_CSSValueTriplet;
}
static bool IsCSSRectUnit(Unit aUnit) {
return aUnit == eUnit_CSSRect;
}
static bool IsCSSValueArrayUnit(Unit aUnit) {
return aUnit == eUnit_Shape;
}
static bool IsCSSValueListUnit(Unit aUnit) {
return aUnit == eUnit_Dasharray || aUnit == eUnit_Filter ||
aUnit == eUnit_Shadow ||
aUnit == eUnit_BackgroundPositionCoord;
}
static bool IsCSSValueSharedListValue(Unit aUnit) {
return aUnit == eUnit_Transform;
}
static bool IsCSSValuePairListUnit(Unit aUnit) {
return aUnit == eUnit_CSSValuePairList;
}
static bool IsStringUnit(Unit aUnit) {
return aUnit == eUnit_UnparsedString;
}
};
struct PropertyStyleAnimationValuePair
{
nsCSSPropertyID mProperty;
StyleAnimationValue mValue;
};
} // namespace mozilla
#endif

View file

@ -0,0 +1,23 @@
/* -*- 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 mozilla_StyleBackendType_h
#define mozilla_StyleBackendType_h
namespace mozilla {
/**
* Enumeration that represents one of the two supported style system backends.
*/
enum class StyleBackendType : uint8_t
{
Gecko = 1,
Servo
};
} // namespace mozilla
#endif // mozilla_StyleBackendType_h

View file

@ -0,0 +1,45 @@
/* -*- 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/. */
/* represent a color combines a numeric color and currentcolor */
#ifndef mozilla_StyleComplexColor_h_
#define mozilla_StyleComplexColor_h_
#include "nsColor.h"
namespace mozilla {
/**
* This struct represents a combined color from a numeric color and
* the current foreground color (currentcolor keyword).
* Conceptually, the formula is "color * (1 - p) + currentcolor * p"
* where p is mForegroundRatio. See mozilla::LinearBlendColors for
* the actual algorithm.
*/
struct StyleComplexColor
{
nscolor mColor;
uint8_t mForegroundRatio;
static StyleComplexColor FromColor(nscolor aColor) { return {aColor, 0}; }
static StyleComplexColor CurrentColor() { return {NS_RGBA(0, 0, 0, 0), 255}; }
bool IsNumericColor() const { return mForegroundRatio == 0; }
bool IsCurrentColor() const { return mForegroundRatio == 255; }
bool operator==(const StyleComplexColor& aOther) const {
return mForegroundRatio == aOther.mForegroundRatio &&
(IsCurrentColor() || mColor == aOther.mColor);
}
bool operator!=(const StyleComplexColor& aOther) const {
return !(*this == aOther);
}
};
}
#endif // mozilla_StyleComplexColor_h_

View file

@ -0,0 +1,161 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 mozilla_StyleContextSource_h
#define mozilla_StyleContextSource_h
#include "mozilla/ServoBindingTypes.h"
#include "nsRuleNode.h"
namespace mozilla {
// Tagged union between Gecko Rule Nodes and Servo Computed Values.
//
// The rule node is the node in the lexicographic tree of rule nodes
// (the "rule tree") that indicates which style rules are used to
// compute the style data, and in what cascading order. The least
// specific rule matched is the one whose rule node is a child of the
// root of the rule tree, and the most specific rule matched is the
// |mRule| member of the rule node.
//
// In the Servo case, we hold an atomically-refcounted handle to a
// Servo ComputedValues struct, which is more or less the Servo equivalent
// of an nsStyleContext.
// Underlying pointer without any strong ownership semantics.
struct NonOwningStyleContextSource
{
MOZ_IMPLICIT NonOwningStyleContextSource(nsRuleNode* aRuleNode)
: mBits(reinterpret_cast<uintptr_t>(aRuleNode)) {}
explicit NonOwningStyleContextSource(const ServoComputedValues* aComputedValues)
: mBits(reinterpret_cast<uintptr_t>(aComputedValues) | 1) {}
bool operator==(const NonOwningStyleContextSource& aOther) const {
MOZ_ASSERT(IsServoComputedValues() == aOther.IsServoComputedValues(),
"Comparing Servo to Gecko - probably a bug");
return mBits == aOther.mBits;
}
bool operator!=(const NonOwningStyleContextSource& aOther) const {
return !(*this == aOther);
}
// We intentionally avoid exposing IsGeckoRuleNode() here, because that would
// encourage callers to do:
//
// if (source.IsGeckoRuleNode()) {
// // Code that we would run unconditionally if it weren't for Servo.
// }
//
// We want these branches to compile away when MOZ_STYLO is disabled, but that
// won't happen if there's an implicit null-check.
bool IsNull() const { return !mBits; }
bool IsGeckoRuleNodeOrNull() const { return !IsServoComputedValues(); }
bool IsServoComputedValues() const {
#ifdef MOZ_STYLO
return mBits & 1;
#else
return false;
#endif
}
nsRuleNode* AsGeckoRuleNode() const {
MOZ_ASSERT(IsGeckoRuleNodeOrNull() && !IsNull());
return reinterpret_cast<nsRuleNode*>(mBits);
}
const ServoComputedValues* AsServoComputedValues() const {
MOZ_ASSERT(IsServoComputedValues());
return reinterpret_cast<ServoComputedValues*>(mBits & ~1);
}
bool MatchesNoRules() const {
if (IsGeckoRuleNodeOrNull()) {
return AsGeckoRuleNode()->IsRoot();
}
// Just assume a Servo-backed StyleContextSource always matches some rules.
//
// MatchesNoRules is used to ensure style contexts that match no rules
// go into a separate mEmptyChild list on their parent. This is only used
// as an optimization so that calling FindChildWithRules for style context
// sharing is faster for text nodes (which match no rules, and are common).
// Since Servo will handle sharing for us, there's no need to split children
// into two lists.
return false;
}
private:
uintptr_t mBits;
};
// Higher-level struct that owns a strong reference to the source. The source
// is never null.
struct OwningStyleContextSource
{
explicit OwningStyleContextSource(already_AddRefed<nsRuleNode> aRuleNode)
: mRaw(aRuleNode.take())
{
MOZ_COUNT_CTOR(OwningStyleContextSource);
MOZ_ASSERT(!mRaw.IsNull());
};
explicit OwningStyleContextSource(already_AddRefed<ServoComputedValues> aComputedValues)
: mRaw(aComputedValues.take())
{
MOZ_COUNT_CTOR(OwningStyleContextSource);
MOZ_ASSERT(!mRaw.IsNull());
}
OwningStyleContextSource(OwningStyleContextSource&& aOther)
: mRaw(aOther.mRaw)
{
MOZ_COUNT_CTOR(OwningStyleContextSource);
aOther.mRaw = nullptr;
}
OwningStyleContextSource& operator=(OwningStyleContextSource&) = delete;
OwningStyleContextSource(OwningStyleContextSource&) = delete;
~OwningStyleContextSource() {
MOZ_COUNT_DTOR(OwningStyleContextSource);
if (mRaw.IsNull()) {
// We must have invoked the move constructor.
} else if (IsGeckoRuleNode()) {
RefPtr<nsRuleNode> releaseme = dont_AddRef(AsGeckoRuleNode());
} else {
MOZ_ASSERT(IsServoComputedValues());
RefPtr<ServoComputedValues> releaseme =
dont_AddRef(AsServoComputedValues());
}
}
bool operator==(const OwningStyleContextSource& aOther) const {
return mRaw == aOther.mRaw;
}
bool operator!=(const OwningStyleContextSource& aOther) const {
return !(*this == aOther);
}
bool IsNull() const { return mRaw.IsNull(); }
bool IsGeckoRuleNode() const {
MOZ_ASSERT(!mRaw.IsNull());
return mRaw.IsGeckoRuleNodeOrNull();
}
bool IsServoComputedValues() const { return mRaw.IsServoComputedValues(); }
NonOwningStyleContextSource AsRaw() const { return mRaw; }
nsRuleNode* AsGeckoRuleNode() const { return mRaw.AsGeckoRuleNode(); }
ServoComputedValues* AsServoComputedValues() const {
return const_cast<ServoComputedValues*>(mRaw.AsServoComputedValues());
}
bool MatchesNoRules() const { return mRaw.MatchesNoRules(); }
private:
NonOwningStyleContextSource mRaw;
};
} // namespace mozilla
#endif // mozilla_StyleContextSource_h

1577
layout/style/StyleRule.cpp Normal file

File diff suppressed because it is too large Load diff

369
layout/style/StyleRule.h Normal file
View file

@ -0,0 +1,369 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/*
* representation of CSS style rules (selectors+declaration) and CSS
* selectors
*/
#ifndef mozilla_css_StyleRule_h__
#define mozilla_css_StyleRule_h__
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/Rule.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsCSSPseudoElements.h"
#include "nsIStyleRule.h"
class nsIAtom;
struct nsCSSSelectorList;
namespace mozilla {
enum class CSSPseudoClassType : uint8_t;
class CSSStyleSheet;
} // namespace mozilla
struct nsAtomList {
public:
explicit nsAtomList(nsIAtom* aAtom);
explicit nsAtomList(const nsString& aAtomValue);
~nsAtomList(void);
/** Do a deep clone. Should be used only on the first in the linked list. */
nsAtomList* Clone() const { return Clone(true); }
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
nsCOMPtr<nsIAtom> mAtom;
nsAtomList* mNext;
private:
nsAtomList* Clone(bool aDeep) const;
nsAtomList(const nsAtomList& aCopy) = delete;
nsAtomList& operator=(const nsAtomList& aCopy) = delete;
};
struct nsPseudoClassList {
public:
typedef mozilla::CSSPseudoClassType CSSPseudoClassType;
explicit nsPseudoClassList(CSSPseudoClassType aType);
nsPseudoClassList(CSSPseudoClassType aType, const char16_t *aString);
nsPseudoClassList(CSSPseudoClassType aType, const int32_t *aIntPair);
nsPseudoClassList(CSSPseudoClassType aType,
nsCSSSelectorList *aSelectorList /* takes ownership */);
~nsPseudoClassList(void);
/** Do a deep clone. Should be used only on the first in the linked list. */
nsPseudoClassList* Clone() const { return Clone(true); }
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
union {
// For a given value of mType, we have either:
// a. no value, which means mMemory is always null
// (if none of the conditions for (b), (c), or (d) is true)
// b. a string value, which means mString/mMemory is non-null
// (if nsCSSPseudoClasses::HasStringArg(mType))
// c. an integer pair value, which means mNumbers/mMemory is non-null
// (if nsCSSPseudoClasses::HasNthPairArg(mType))
// d. a selector list, which means mSelectors is non-null
// (if nsCSSPseudoClasses::HasSelectorListArg(mType))
void* mMemory; // mString and mNumbers use moz_xmalloc/free
char16_t* mString;
int32_t* mNumbers;
nsCSSSelectorList* mSelectors;
} u;
CSSPseudoClassType mType;
nsPseudoClassList* mNext;
private:
nsPseudoClassList* Clone(bool aDeep) const;
nsPseudoClassList(const nsPseudoClassList& aCopy) = delete;
nsPseudoClassList& operator=(const nsPseudoClassList& aCopy) = delete;
};
#define NS_ATTR_FUNC_SET 0 // [attr]
#define NS_ATTR_FUNC_EQUALS 1 // [attr=value]
#define NS_ATTR_FUNC_INCLUDES 2 // [attr~=value] (space separated)
#define NS_ATTR_FUNC_DASHMATCH 3 // [attr|=value] ('-' truncated)
#define NS_ATTR_FUNC_BEGINSMATCH 4 // [attr^=value] (begins with)
#define NS_ATTR_FUNC_ENDSMATCH 5 // [attr$=value] (ends with)
#define NS_ATTR_FUNC_CONTAINSMATCH 6 // [attr*=value] (contains substring)
struct nsAttrSelector {
public:
enum class ValueCaseSensitivity : uint8_t {
CaseSensitive,
CaseInsensitive,
CaseInsensitiveInHTML
};
nsAttrSelector(int32_t aNameSpace, const nsString& aAttr);
nsAttrSelector(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunction,
const nsString& aValue,
ValueCaseSensitivity aValueCaseSensitivity);
nsAttrSelector(int32_t aNameSpace, nsIAtom* aLowercaseAttr,
nsIAtom* aCasedAttr, uint8_t aFunction,
const nsString& aValue,
ValueCaseSensitivity aValueCaseSensitivity);
~nsAttrSelector(void);
/** Do a deep clone. Should be used only on the first in the linked list. */
nsAttrSelector* Clone() const { return Clone(true); }
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
bool IsValueCaseSensitive(bool aInHTML) const {
return mValueCaseSensitivity == ValueCaseSensitivity::CaseSensitive ||
(!aInHTML &&
mValueCaseSensitivity == ValueCaseSensitivity::CaseInsensitiveInHTML);
}
nsString mValue;
nsAttrSelector* mNext;
nsCOMPtr<nsIAtom> mLowercaseAttr;
nsCOMPtr<nsIAtom> mCasedAttr;
int32_t mNameSpace;
uint8_t mFunction;
ValueCaseSensitivity mValueCaseSensitivity;
private:
nsAttrSelector* Clone(bool aDeep) const;
nsAttrSelector(const nsAttrSelector& aCopy) = delete;
nsAttrSelector& operator=(const nsAttrSelector& aCopy) = delete;
};
struct nsCSSSelector {
public:
typedef mozilla::CSSPseudoClassType CSSPseudoClassType;
nsCSSSelector(void);
~nsCSSSelector(void);
/** Do a deep clone. Should be used only on the first in the linked list. */
nsCSSSelector* Clone() const { return Clone(true, true); }
void Reset(void);
void SetNameSpace(int32_t aNameSpace);
void SetTag(const nsString& aTag);
void AddID(const nsString& aID);
void AddClass(const nsString& aClass);
void AddPseudoClass(CSSPseudoClassType aType);
void AddPseudoClass(CSSPseudoClassType aType, const char16_t* aString);
void AddPseudoClass(CSSPseudoClassType aType, const int32_t* aIntPair);
// takes ownership of aSelectorList
void AddPseudoClass(CSSPseudoClassType aType,
nsCSSSelectorList* aSelectorList);
void AddAttribute(int32_t aNameSpace, const nsString& aAttr);
void AddAttribute(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunc,
const nsString& aValue,
nsAttrSelector::ValueCaseSensitivity aValueCaseSensitivity);
void SetOperator(char16_t aOperator);
inline bool HasTagSelector() const {
return !!mCasedTag;
}
inline bool IsPseudoElement() const {
return mLowercaseTag && !mCasedTag;
}
// Calculate the specificity of this selector (not including its mNext!).
int32_t CalcWeight() const;
void ToString(nsAString& aString, mozilla::CSSStyleSheet* aSheet,
bool aAppend = false) const;
bool IsRestrictedSelector() const {
return PseudoType() == mozilla::CSSPseudoElementType::NotPseudo;
}
#ifdef DEBUG
nsCString RestrictedSelectorToString() const;
#endif
private:
void AddPseudoClassInternal(nsPseudoClassList *aPseudoClass);
nsCSSSelector* Clone(bool aDeepNext, bool aDeepNegations) const;
void AppendToStringWithoutCombinators(
nsAString& aString,
mozilla::CSSStyleSheet* aSheet,
bool aUseStandardNamespacePrefixes) const;
void AppendToStringWithoutCombinatorsOrNegations(
nsAString& aString,
mozilla::CSSStyleSheet* aSheet,
bool aIsNegated,
bool aUseStandardNamespacePrefixes) const;
// Returns true if this selector can have a namespace specified (which
// happens if and only if the default namespace would apply to this
// selector).
bool CanBeNamespaced(bool aIsNegated) const;
// Calculate the specificity of this selector (not including its mNext
// or its mNegations).
int32_t CalcWeightWithoutNegations() const;
public:
// Get and set the selector's pseudo type
mozilla::CSSPseudoElementType PseudoType() const { return mPseudoType; }
void SetPseudoType(mozilla::CSSPseudoElementType aType) {
mPseudoType = aType;
}
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
// For case-sensitive documents, mLowercaseTag is the same as mCasedTag,
// but in case-insensitive documents (HTML) mLowercaseTag is lowercase.
// Also, for pseudo-elements mCasedTag will be null but mLowercaseTag
// contains their name.
nsCOMPtr<nsIAtom> mLowercaseTag;
nsCOMPtr<nsIAtom> mCasedTag;
nsAtomList* mIDList;
nsAtomList* mClassList;
nsPseudoClassList* mPseudoClassList; // atom for the pseudo, string for
// the argument to functional pseudos
nsAttrSelector* mAttrList;
nsCSSSelector* mNegations;
nsCSSSelector* mNext;
int32_t mNameSpace;
char16_t mOperator;
private:
// The underlying type of CSSPseudoElementType is uint8_t and
// it packs well with mOperator. (char16_t + uint8_t is less than 32bits.)
mozilla::CSSPseudoElementType mPseudoType;
nsCSSSelector(const nsCSSSelector& aCopy) = delete;
nsCSSSelector& operator=(const nsCSSSelector& aCopy) = delete;
};
/**
* A selector list is the unit of selectors that each style rule has.
* For example, "P B, H1 B { ... }" would be a selector list with two
* items (where each |nsCSSSelectorList| object's |mSelectors| has
* an |mNext| for the P or H1). We represent them as linked lists.
*/
class inDOMUtils;
struct nsCSSSelectorList {
nsCSSSelectorList(void);
~nsCSSSelectorList(void);
/**
* Create a new selector and push it onto the beginning of |mSelectors|,
* setting its |mNext| to the current value of |mSelectors|. If there is an
* earlier selector, set its |mOperator| to |aOperator|; else |aOperator|
* must be char16_t(0).
* Returns the new selector.
* The list owns the new selector.
* The caller is responsible for updating |mWeight|.
*/
nsCSSSelector* AddSelector(char16_t aOperator);
/**
* Point |mSelectors| to its |mNext|, and delete the first node in the old
* |mSelectors|.
* Should only be used on a list with more than one selector in it.
*/
void RemoveRightmostSelector();
/**
* Should be used only on the first in the list
*/
void ToString(nsAString& aResult, mozilla::CSSStyleSheet* aSheet);
/**
* Do a deep clone. Should be used only on the first in the list.
*/
nsCSSSelectorList* Clone() const { return Clone(true); }
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
nsCSSSelector* mSelectors;
int32_t mWeight;
nsCSSSelectorList* mNext;
protected:
friend class inDOMUtils;
nsCSSSelectorList* Clone(bool aDeep) const;
private:
nsCSSSelectorList(const nsCSSSelectorList& aCopy) = delete;
nsCSSSelectorList& operator=(const nsCSSSelectorList& aCopy) = delete;
};
// 464bab7a-2fce-4f30-ab44-b7a5f3aae57d
#define NS_CSS_STYLE_RULE_IMPL_CID \
{ 0x464bab7a, 0x2fce, 0x4f30, \
{ 0xab, 0x44, 0xb7, 0xa5, 0xf3, 0xaa, 0xe5, 0x7d } }
namespace mozilla {
namespace css {
class Declaration;
class DOMCSSStyleRule;
class StyleRule final : public Rule
{
public:
StyleRule(nsCSSSelectorList* aSelector,
Declaration *aDeclaration,
uint32_t aLineNumber, uint32_t aColumnNumber);
private:
// for |Clone|
StyleRule(const StyleRule& aCopy);
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_STYLE_RULE_IMPL_CID)
NS_DECL_ISUPPORTS
// null for style attribute
nsCSSSelectorList* Selector() { return mSelector; }
Declaration* GetDeclaration() const { return mDeclaration; }
void SetDeclaration(Declaration* aDecl);
// hooks for DOM rule
void GetCssText(nsAString& aCssText);
void SetCssText(const nsAString& aCssText);
void GetSelectorText(nsAString& aSelectorText);
void SetSelectorText(const nsAString& aSelectorText);
virtual int32_t GetType() const override;
virtual already_AddRefed<Rule> Clone() const override;
virtual nsIDOMCSSRule* GetDOMRule() override;
virtual nsIDOMCSSRule* GetExistingDOMRule() override;
#ifdef DEBUG
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
#endif
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
private:
~StyleRule();
private:
nsCSSSelectorList* mSelector; // null for style attribute
RefPtr<Declaration> mDeclaration;
RefPtr<DOMCSSStyleRule> mDOMRule;
private:
StyleRule& operator=(const StyleRule& aCopy) = delete;
};
NS_DEFINE_STATIC_IID_ACCESSOR(StyleRule, NS_CSS_STYLE_RULE_IMPL_CID)
} // namespace css
} // namespace mozilla
#endif /* mozilla_css_StyleRule_h__ */

View file

@ -0,0 +1,219 @@
/* -*- 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 mozilla_StyleSetHandle_h
#define mozilla_StyleSetHandle_h
#include "mozilla/EventStates.h"
#include "mozilla/RefPtr.h"
#include "mozilla/SheetType.h"
#include "mozilla/StyleBackendType.h"
#include "mozilla/StyleSheet.h"
#include "nsChangeHint.h"
#include "nsCSSPseudoElements.h"
#include "nsTArray.h"
namespace mozilla {
class CSSStyleSheet;
class ServoStyleSet;
namespace dom {
class Element;
} // namespace dom
} // namespace mozilla
class nsIAtom;
class nsIContent;
class nsIDocument;
class nsStyleContext;
class nsStyleSet;
class nsPresContext;
struct TreeMatchContext;
namespace mozilla {
#define SERVO_BIT 0x1
/**
* Smart pointer class that can hold a pointer to either an nsStyleSet
* or a ServoStyleSet.
*/
class StyleSetHandle
{
public:
// We define this Ptr class with a StyleSet API that forwards on to the
// wrapped pointer, rather than putting these methods on StyleSetHandle
// itself, so that we can have StyleSetHandle behave like a smart pointer and
// be dereferenced with operator->.
class Ptr
{
public:
friend class ::mozilla::StyleSetHandle;
bool IsGecko() const { return !IsServo(); }
bool IsServo() const
{
MOZ_ASSERT(mValue, "StyleSetHandle null pointer dereference");
#ifdef MOZ_STYLO
return mValue & SERVO_BIT;
#else
return false;
#endif
}
StyleBackendType BackendType() const
{
return IsGecko() ? StyleBackendType::Gecko :
StyleBackendType::Servo;
}
nsStyleSet* AsGecko()
{
MOZ_ASSERT(IsGecko());
return reinterpret_cast<nsStyleSet*>(mValue);
}
ServoStyleSet* AsServo()
{
MOZ_ASSERT(IsServo());
return reinterpret_cast<ServoStyleSet*>(mValue & ~SERVO_BIT);
}
nsStyleSet* GetAsGecko() { return IsGecko() ? AsGecko() : nullptr; }
ServoStyleSet* GetAsServo() { return IsServo() ? AsServo() : nullptr; }
const nsStyleSet* AsGecko() const
{
return const_cast<Ptr*>(this)->AsGecko();
}
const ServoStyleSet* AsServo() const
{
MOZ_ASSERT(IsServo());
return const_cast<Ptr*>(this)->AsServo();
}
const nsStyleSet* GetAsGecko() const { return IsGecko() ? AsGecko() : nullptr; }
const ServoStyleSet* GetAsServo() const { return IsServo() ? AsServo() : nullptr; }
// These inline methods are defined in StyleSetHandleInlines.h.
inline void Delete();
// Style set interface. These inline methods are defined in
// StyleSetHandleInlines.h and just forward to the underlying
// nsStyleSet or ServoStyleSet. See corresponding comments in
// nsStyleSet.h for descriptions of these methods.
inline void Init(nsPresContext* aPresContext);
inline void BeginShutdown();
inline void Shutdown();
inline bool GetAuthorStyleDisabled() const;
inline nsresult SetAuthorStyleDisabled(bool aStyleDisabled);
inline void BeginUpdate();
inline nsresult EndUpdate();
inline already_AddRefed<nsStyleContext>
ResolveStyleFor(dom::Element* aElement,
nsStyleContext* aParentContext);
inline already_AddRefed<nsStyleContext>
ResolveStyleFor(dom::Element* aElement,
nsStyleContext* aParentContext,
TreeMatchContext& aTreeMatchContext);
inline already_AddRefed<nsStyleContext>
ResolveStyleForText(nsIContent* aTextNode,
nsStyleContext* aParentContext);
inline already_AddRefed<nsStyleContext>
ResolveStyleForOtherNonElement(nsStyleContext* aParentContext);
inline already_AddRefed<nsStyleContext>
ResolvePseudoElementStyle(dom::Element* aParentElement,
mozilla::CSSPseudoElementType aType,
nsStyleContext* aParentContext,
dom::Element* aPseudoElement);
inline already_AddRefed<nsStyleContext>
ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag, nsStyleContext* aParentContext,
uint32_t aFlags = 0);
inline nsresult AppendStyleSheet(SheetType aType, StyleSheet* aSheet);
inline nsresult PrependStyleSheet(SheetType aType, StyleSheet* aSheet);
inline nsresult RemoveStyleSheet(SheetType aType, StyleSheet* aSheet);
inline nsresult ReplaceSheets(SheetType aType,
const nsTArray<RefPtr<StyleSheet>>& aNewSheets);
inline nsresult InsertStyleSheetBefore(SheetType aType,
StyleSheet* aNewSheet,
StyleSheet* aReferenceSheet);
inline int32_t SheetCount(SheetType aType) const;
inline StyleSheet* StyleSheetAt(SheetType aType, int32_t aIndex) const;
inline nsresult RemoveDocStyleSheet(StyleSheet* aSheet);
inline nsresult AddDocStyleSheet(StyleSheet* aSheet, nsIDocument* aDocument);
inline already_AddRefed<nsStyleContext>
ProbePseudoElementStyle(dom::Element* aParentElement,
mozilla::CSSPseudoElementType aType,
nsStyleContext* aParentContext);
inline already_AddRefed<nsStyleContext>
ProbePseudoElementStyle(dom::Element* aParentElement,
mozilla::CSSPseudoElementType aType,
nsStyleContext* aParentContext,
TreeMatchContext& aTreeMatchContext,
dom::Element* aPseudoElement = nullptr);
inline nsRestyleHint HasStateDependentStyle(dom::Element* aElement,
EventStates aStateMask);
inline nsRestyleHint HasStateDependentStyle(
dom::Element* aElement,
mozilla::CSSPseudoElementType aPseudoType,
dom::Element* aPseudoElement,
EventStates aStateMask);
inline void RootStyleContextAdded();
inline void RootStyleContextRemoved();
private:
// Stores a pointer to an nsStyleSet or a ServoStyleSet. The least
// significant bit is 0 for the former, 1 for the latter. This is
// valid as the least significant bit will never be used for a pointer
// value on platforms we care about.
uintptr_t mValue;
};
StyleSetHandle() { mPtr.mValue = 0; }
StyleSetHandle(const StyleSetHandle& aOth) { mPtr.mValue = aOth.mPtr.mValue; }
MOZ_IMPLICIT StyleSetHandle(nsStyleSet* aSet) { *this = aSet; }
MOZ_IMPLICIT StyleSetHandle(ServoStyleSet* aSet) { *this = aSet; }
StyleSetHandle& operator=(nsStyleSet* aStyleSet)
{
MOZ_ASSERT(!(reinterpret_cast<uintptr_t>(aStyleSet) & SERVO_BIT),
"least significant bit shouldn't be set; we use it for state");
mPtr.mValue = reinterpret_cast<uintptr_t>(aStyleSet);
return *this;
}
StyleSetHandle& operator=(ServoStyleSet* aStyleSet)
{
#ifdef MOZ_STYLO
MOZ_ASSERT(!(reinterpret_cast<uintptr_t>(aStyleSet) & SERVO_BIT),
"least significant bit shouldn't be set; we use it for state");
mPtr.mValue =
aStyleSet ? (reinterpret_cast<uintptr_t>(aStyleSet) | SERVO_BIT) : 0;
return *this;
#else
MOZ_CRASH("should not have a ServoStyleSet object when MOZ_STYLO is "
"disabled");
#endif
}
// Make StyleSetHandle usable in boolean contexts.
explicit operator bool() const { return !!mPtr.mValue; }
bool operator!() const { return !mPtr.mValue; }
// Make StyleSetHandle behave like a smart pointer.
Ptr* operator->() { return &mPtr; }
const Ptr* operator->() const { return &mPtr; }
private:
Ptr mPtr;
};
#undef SERVO_BIT
} // namespace mozilla
#endif // mozilla_StyleSetHandle_h

View file

@ -0,0 +1,267 @@
/* -*- 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 mozilla_StyleSetHandleInlines_h
#define mozilla_StyleSetHandleInlines_h
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/ServoStyleSet.h"
#include "nsStyleSet.h"
#define FORWARD_CONCRETE(method_, geckoargs_, servoargs_) \
if (IsGecko()) { \
return AsGecko()->method_ geckoargs_; \
} else { \
return AsServo()->method_ servoargs_; \
}
#define FORWARD(method_, args_) FORWARD_CONCRETE(method_, args_, args_)
namespace mozilla {
void
StyleSetHandle::Ptr::Delete()
{
if (mValue) {
if (IsGecko()) {
delete AsGecko();
} else {
delete AsServo();
}
}
}
void
StyleSetHandle::Ptr::Init(nsPresContext* aPresContext)
{
FORWARD(Init, (aPresContext));
}
void
StyleSetHandle::Ptr::BeginShutdown()
{
FORWARD(BeginShutdown, ());
}
void
StyleSetHandle::Ptr::Shutdown()
{
FORWARD(Shutdown, ());
}
bool
StyleSetHandle::Ptr::GetAuthorStyleDisabled() const
{
FORWARD(GetAuthorStyleDisabled, ());
}
nsresult
StyleSetHandle::Ptr::SetAuthorStyleDisabled(bool aStyleDisabled)
{
FORWARD(SetAuthorStyleDisabled, (aStyleDisabled));
}
void
StyleSetHandle::Ptr::BeginUpdate()
{
FORWARD(BeginUpdate, ());
}
nsresult
StyleSetHandle::Ptr::EndUpdate()
{
FORWARD(EndUpdate, ());
}
// resolve a style context
already_AddRefed<nsStyleContext>
StyleSetHandle::Ptr::ResolveStyleFor(dom::Element* aElement,
nsStyleContext* aParentContext)
{
FORWARD(ResolveStyleFor, (aElement, aParentContext));
}
already_AddRefed<nsStyleContext>
StyleSetHandle::Ptr::ResolveStyleFor(dom::Element* aElement,
nsStyleContext* aParentContext,
TreeMatchContext& aTreeMatchContext)
{
FORWARD(ResolveStyleFor, (aElement, aParentContext, aTreeMatchContext));
}
already_AddRefed<nsStyleContext>
StyleSetHandle::Ptr::ResolveStyleForText(nsIContent* aTextNode,
nsStyleContext* aParentContext)
{
FORWARD(ResolveStyleForText, (aTextNode, aParentContext));
}
already_AddRefed<nsStyleContext>
StyleSetHandle::Ptr::ResolveStyleForOtherNonElement(nsStyleContext* aParentContext)
{
FORWARD(ResolveStyleForOtherNonElement, (aParentContext));
}
already_AddRefed<nsStyleContext>
StyleSetHandle::Ptr::ResolvePseudoElementStyle(dom::Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext,
dom::Element* aPseudoElement)
{
FORWARD(ResolvePseudoElementStyle, (aParentElement, aType, aParentContext,
aPseudoElement));
}
// aFlags is an nsStyleSet flags bitfield
already_AddRefed<nsStyleContext>
StyleSetHandle::Ptr::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag,
nsStyleContext* aParentContext,
uint32_t aFlags)
{
FORWARD(ResolveAnonymousBoxStyle, (aPseudoTag, aParentContext, aFlags));
}
// manage the set of style sheets in the style set
nsresult
StyleSetHandle::Ptr::AppendStyleSheet(SheetType aType, StyleSheet* aSheet)
{
FORWARD_CONCRETE(AppendStyleSheet, (aType, aSheet->AsGecko()),
(aType, aSheet->AsServo()));
}
nsresult
StyleSetHandle::Ptr::PrependStyleSheet(SheetType aType, StyleSheet* aSheet)
{
FORWARD_CONCRETE(PrependStyleSheet, (aType, aSheet->AsGecko()),
(aType, aSheet->AsServo()));
}
nsresult
StyleSetHandle::Ptr::RemoveStyleSheet(SheetType aType, StyleSheet* aSheet)
{
FORWARD_CONCRETE(RemoveStyleSheet, (aType, aSheet->AsGecko()),
(aType, aSheet->AsServo()));
}
nsresult
StyleSetHandle::Ptr::ReplaceSheets(SheetType aType,
const nsTArray<RefPtr<StyleSheet>>& aNewSheets)
{
if (IsGecko()) {
nsTArray<RefPtr<CSSStyleSheet>> newSheets(aNewSheets.Length());
for (auto& sheet : aNewSheets) {
newSheets.AppendElement(sheet->AsGecko());
}
return AsGecko()->ReplaceSheets(aType, newSheets);
} else {
nsTArray<RefPtr<ServoStyleSheet>> newSheets(aNewSheets.Length());
for (auto& sheet : aNewSheets) {
newSheets.AppendElement(sheet->AsServo());
}
return AsServo()->ReplaceSheets(aType, newSheets);
}
}
nsresult
StyleSetHandle::Ptr::InsertStyleSheetBefore(SheetType aType,
StyleSheet* aNewSheet,
StyleSheet* aReferenceSheet)
{
FORWARD_CONCRETE(
InsertStyleSheetBefore,
(aType, aNewSheet->AsGecko(), aReferenceSheet->AsGecko()),
(aType, aReferenceSheet->AsServo(), aReferenceSheet->AsServo()));
}
int32_t
StyleSetHandle::Ptr::SheetCount(SheetType aType) const
{
FORWARD(SheetCount, (aType));
}
StyleSheet*
StyleSetHandle::Ptr::StyleSheetAt(SheetType aType, int32_t aIndex) const
{
FORWARD(StyleSheetAt, (aType, aIndex));
}
nsresult
StyleSetHandle::Ptr::RemoveDocStyleSheet(StyleSheet* aSheet)
{
FORWARD_CONCRETE(RemoveDocStyleSheet, (aSheet->AsGecko()),
(aSheet->AsServo()));
}
nsresult
StyleSetHandle::Ptr::AddDocStyleSheet(StyleSheet* aSheet,
nsIDocument* aDocument)
{
FORWARD_CONCRETE(AddDocStyleSheet, (aSheet->AsGecko(), aDocument),
(aSheet->AsServo(), aDocument));
}
// check whether there is ::before/::after style for an element
already_AddRefed<nsStyleContext>
StyleSetHandle::Ptr::ProbePseudoElementStyle(dom::Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext)
{
FORWARD(ProbePseudoElementStyle, (aParentElement, aType, aParentContext));
}
already_AddRefed<nsStyleContext>
StyleSetHandle::Ptr::ProbePseudoElementStyle(dom::Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext,
TreeMatchContext& aTreeMatchContext,
dom::Element* aPseudoElement)
{
FORWARD(ProbePseudoElementStyle, (aParentElement, aType, aParentContext,
aTreeMatchContext, aPseudoElement));
}
nsRestyleHint
StyleSetHandle::Ptr::HasStateDependentStyle(dom::Element* aElement,
EventStates aStateMask)
{
FORWARD(HasStateDependentStyle, (aElement, aStateMask));
}
nsRestyleHint
StyleSetHandle::Ptr::HasStateDependentStyle(dom::Element* aElement,
CSSPseudoElementType aPseudoType,
dom::Element* aPseudoElement,
EventStates aStateMask)
{
FORWARD(HasStateDependentStyle, (aElement, aPseudoType, aPseudoElement,
aStateMask));
}
void
StyleSetHandle::Ptr::RootStyleContextAdded()
{
if (IsGecko()) {
AsGecko()->RootStyleContextAdded();
} else {
// Not needed.
}
}
void
StyleSetHandle::Ptr::RootStyleContextRemoved()
{
if (IsGecko()) {
RootStyleContextAdded();
} else {
// Not needed.
}
}
} // namespace mozilla
#undef FORWARD
#endif // mozilla_StyleSetHandleInlines_h

329
layout/style/StyleSheet.cpp Normal file
View file

@ -0,0 +1,329 @@
/* -*- 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/StyleSheet.h"
#include "mozilla/dom/CSSRuleList.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/ServoStyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/CSSStyleSheet.h"
#include "nsNullPrincipal.h"
namespace mozilla {
StyleSheet::StyleSheet(StyleBackendType aType, css::SheetParsingMode aParsingMode)
: mDocument(nullptr)
, mOwningNode(nullptr)
, mParsingMode(aParsingMode)
, mType(aType)
, mDisabled(false)
{
}
StyleSheet::StyleSheet(const StyleSheet& aCopy,
nsIDocument* aDocumentToUse,
nsINode* aOwningNodeToUse)
: mTitle(aCopy.mTitle)
, mDocument(aDocumentToUse)
, mOwningNode(aOwningNodeToUse)
, mParsingMode(aCopy.mParsingMode)
, mType(aCopy.mType)
, mDisabled(aCopy.mDisabled)
{
}
// QueryInterface implementation for StyleSheet
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleSheet)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheet)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleSheet)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(StyleSheet)
NS_IMPL_CYCLE_COLLECTING_RELEASE(StyleSheet)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(StyleSheet)
mozilla::dom::CSSStyleSheetParsingMode
StyleSheet::ParsingModeDOM()
{
#define CHECK(X, Y) \
static_assert(static_cast<int>(X) == static_cast<int>(Y), \
"mozilla::dom::CSSStyleSheetParsingMode and mozilla::css::SheetParsingMode should have identical values");
CHECK(mozilla::dom::CSSStyleSheetParsingMode::Agent, css::eAgentSheetFeatures);
CHECK(mozilla::dom::CSSStyleSheetParsingMode::User, css::eUserSheetFeatures);
CHECK(mozilla::dom::CSSStyleSheetParsingMode::Author, css::eAuthorSheetFeatures);
#undef CHECK
return static_cast<mozilla::dom::CSSStyleSheetParsingMode>(mParsingMode);
}
bool
StyleSheet::IsComplete() const
{
return SheetInfo().mComplete;
}
void
StyleSheet::SetComplete()
{
NS_ASSERTION(!IsGecko() || !AsGecko()->mDirty,
"Can't set a dirty sheet complete!");
SheetInfo().mComplete = true;
if (mDocument && !mDisabled) {
// Let the document know
mDocument->BeginUpdate(UPDATE_STYLE);
mDocument->SetStyleSheetApplicableState(this, true);
mDocument->EndUpdate(UPDATE_STYLE);
}
if (mOwningNode && !mDisabled &&
mOwningNode->HasFlag(NODE_IS_IN_SHADOW_TREE) &&
mOwningNode->IsContent()) {
dom::ShadowRoot* shadowRoot = mOwningNode->AsContent()->GetContainingShadow();
shadowRoot->StyleSheetChanged();
}
}
StyleSheetInfo::StyleSheetInfo(CORSMode aCORSMode,
ReferrerPolicy aReferrerPolicy,
const dom::SRIMetadata& aIntegrity)
: mPrincipal(nsNullPrincipal::Create())
, mCORSMode(aCORSMode)
, mReferrerPolicy(aReferrerPolicy)
, mIntegrity(aIntegrity)
, mComplete(false)
#ifdef DEBUG
, mPrincipalSet(false)
#endif
{
if (!mPrincipal) {
NS_RUNTIMEABORT("nsNullPrincipal::Init failed");
}
}
// nsIDOMStyleSheet interface
NS_IMETHODIMP
StyleSheet::GetType(nsAString& aType)
{
aType.AssignLiteral("text/css");
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetDisabled(bool* aDisabled)
{
*aDisabled = Disabled();
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::SetDisabled(bool aDisabled)
{
// DOM method, so handle BeginUpdate/EndUpdate
MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_STYLE, true);
if (IsGecko()) {
AsGecko()->SetEnabled(!aDisabled);
} else {
MOZ_CRASH("stylo: unimplemented SetEnabled");
}
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetOwnerNode(nsIDOMNode** aOwnerNode)
{
nsCOMPtr<nsIDOMNode> ownerNode = do_QueryInterface(GetOwnerNode());
ownerNode.forget(aOwnerNode);
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetHref(nsAString& aHref)
{
if (nsIURI* sheetURI = SheetInfo().mOriginalSheetURI) {
nsAutoCString str;
nsresult rv = sheetURI->GetSpec(str);
NS_ENSURE_SUCCESS(rv, rv);
CopyUTF8toUTF16(str, aHref);
} else {
SetDOMStringToNull(aHref);
}
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetTitle(nsAString& aTitle)
{
aTitle.Assign(mTitle);
return NS_OK;
}
// nsIDOMStyleSheet interface
NS_IMETHODIMP
StyleSheet::GetParentStyleSheet(nsIDOMStyleSheet** aParentStyleSheet)
{
NS_ENSURE_ARG_POINTER(aParentStyleSheet);
NS_IF_ADDREF(*aParentStyleSheet = GetParentStyleSheet());
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetMedia(nsIDOMMediaList** aMedia)
{
NS_ADDREF(*aMedia = Media());
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetOwnerRule(nsIDOMCSSRule** aOwnerRule)
{
NS_IF_ADDREF(*aOwnerRule = GetDOMOwnerRule());
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetCssRules(nsIDOMCSSRuleList** aCssRules)
{
ErrorResult rv;
nsCOMPtr<nsIDOMCSSRuleList> rules =
GetCssRules(*nsContentUtils::SubjectPrincipal(), rv);
rules.forget(aCssRules);
return rv.StealNSResult();
}
NS_IMETHODIMP
StyleSheet::InsertRule(const nsAString& aRule, uint32_t aIndex,
uint32_t* aReturn)
{
ErrorResult rv;
*aReturn =
InsertRule(aRule, aIndex, *nsContentUtils::SubjectPrincipal(), rv);
return rv.StealNSResult();
}
NS_IMETHODIMP
StyleSheet::DeleteRule(uint32_t aIndex)
{
ErrorResult rv;
DeleteRule(aIndex, *nsContentUtils::SubjectPrincipal(), rv);
return rv.StealNSResult();
}
// WebIDL CSSStyleSheet API
#define FORWARD_INTERNAL(method_, args_) \
if (IsServo()) { \
return AsServo()->method_ args_; \
} \
return AsGecko()->method_ args_;
dom::CSSRuleList*
StyleSheet::GetCssRules(nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
return nullptr;
}
FORWARD_INTERNAL(GetCssRulesInternal, (aRv))
}
uint32_t
StyleSheet::InsertRule(const nsAString& aRule, uint32_t aIndex,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
return 0;
}
FORWARD_INTERNAL(InsertRuleInternal, (aRule, aIndex, aRv))
}
void
StyleSheet::DeleteRule(uint32_t aIndex,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
return;
}
FORWARD_INTERNAL(DeleteRuleInternal, (aIndex, aRv))
}
#undef FORWARD_INTERNAL
void
StyleSheet::SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
StyleSheetInfo& info = SheetInfo();
if (aSubjectPrincipal.Subsumes(info.mPrincipal)) {
return;
}
// Allow access only if CORS mode is not NONE
if (GetCORSMode() == CORS_NONE) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
// Now make sure we set the principal of our inner to the subjectPrincipal.
// We do this because we're in a situation where the caller would not normally
// be able to access the sheet, but the sheet has opted in to being read.
// Unfortunately, that means it's also opted in to being _edited_, and if the
// caller now makes edits to the sheet we want the resulting resource loads,
// if any, to look as if they are coming from the caller's principal, not the
// original sheet principal.
//
// That means we need a unique inner, of course. But we don't want to do that
// if we're not complete yet. Luckily, all the callers of this method throw
// anyway if not complete, so we can just do that here too.
if (!info.mComplete) {
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return;
}
WillDirty();
info.mPrincipal = &aSubjectPrincipal;
DidDirty();
}
bool
StyleSheet::AreRulesAvailable(nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
// Rules are not available on incomplete sheets.
if (!SheetInfo().mComplete) {
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return false;
}
//-- Security check: Only scripts whose principal subsumes that of the
// style sheet can access rule collections.
SubjectSubsumesInnerPrincipal(aSubjectPrincipal, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return false;
}
return true;
}
// nsWrapperCache
JSObject*
StyleSheet::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return CSSStyleSheetBinding::Wrap(aCx, this, aGivenProto);
}
} // namespace mozilla

210
layout/style/StyleSheet.h Normal file
View file

@ -0,0 +1,210 @@
/* -*- 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 mozilla_StyleSheet_h
#define mozilla_StyleSheet_h
#include "mozilla/css/SheetParsingMode.h"
#include "mozilla/dom/CSSStyleSheetBinding.h"
#include "mozilla/net/ReferrerPolicy.h"
#include "mozilla/StyleBackendType.h"
#include "mozilla/CORSMode.h"
#include "mozilla/ServoUtils.h"
#include "nsIDOMCSSStyleSheet.h"
#include "nsWrapperCache.h"
class nsIDocument;
class nsINode;
class nsIPrincipal;
class nsMediaList;
namespace mozilla {
class CSSStyleSheet;
class ServoStyleSheet;
struct StyleSheetInfo;
namespace dom {
class CSSRuleList;
class SRIMetadata;
} // namespace dom
/**
* Superclass for data common to CSSStyleSheet and ServoStyleSheet.
*/
class StyleSheet : public nsIDOMCSSStyleSheet
, public nsWrapperCache
{
protected:
StyleSheet(StyleBackendType aType, css::SheetParsingMode aParsingMode);
StyleSheet(const StyleSheet& aCopy,
nsIDocument* aDocumentToUse,
nsINode* aOwningNodeToUse);
virtual ~StyleSheet() {}
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(StyleSheet)
void SetOwningNode(nsINode* aOwningNode)
{
mOwningNode = aOwningNode;
}
css::SheetParsingMode ParsingMode() { return mParsingMode; }
mozilla::dom::CSSStyleSheetParsingMode ParsingModeDOM();
// The document this style sheet is associated with. May be null
nsIDocument* GetDocument() const { return mDocument; }
/**
* Whether the sheet is complete.
*/
bool IsComplete() const;
void SetComplete();
MOZ_DECL_STYLO_METHODS(CSSStyleSheet, ServoStyleSheet)
// Whether the sheet is for an inline <style> element.
inline bool IsInline() const;
inline nsIURI* GetSheetURI() const;
/* Get the URI this sheet was originally loaded from, if any. Can
return null */
inline nsIURI* GetOriginalURI() const;
inline nsIURI* GetBaseURI() const;
/**
* SetURIs must be called on all sheets before parsing into them.
* SetURIs may only be called while the sheet is 1) incomplete and 2)
* has no rules in it
*/
inline void SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI,
nsIURI* aBaseURI);
/**
* Whether the sheet is applicable. A sheet that is not applicable
* should never be inserted into a style set. A sheet may not be
* applicable for a variety of reasons including being disabled and
* being incomplete.
*/
inline bool IsApplicable() const;
inline bool HasRules() const;
// style sheet owner info
nsIDocument* GetOwningDocument() const { return mDocument; }
inline void SetOwningDocument(nsIDocument* aDocument);
nsINode* GetOwnerNode() const { return mOwningNode; }
inline StyleSheet* GetParentSheet() const;
inline void AppendStyleSheet(StyleSheet* aSheet);
// Principal() never returns a null pointer.
inline nsIPrincipal* Principal() const;
/**
* SetPrincipal should be called on all sheets before parsing into them.
* This can only be called once with a non-null principal. Calling this with
* a null pointer is allowed and is treated as a no-op.
*/
inline void SetPrincipal(nsIPrincipal* aPrincipal);
// Get this style sheet's CORS mode
inline CORSMode GetCORSMode() const;
// Get this style sheet's Referrer Policy
inline net::ReferrerPolicy GetReferrerPolicy() const;
// Get this style sheet's integrity metadata
inline void GetIntegrity(dom::SRIMetadata& aResult) const;
inline size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
#ifdef DEBUG
inline void List(FILE* aOut = stdout, int32_t aIndex = 0) const;
#endif
// WebIDL StyleSheet API
// The XPCOM GetType is fine for WebIDL.
// The XPCOM GetHref is fine for WebIDL
// GetOwnerNode is defined above.
inline StyleSheet* GetParentStyleSheet() const;
// The XPCOM GetTitle is fine for WebIDL.
virtual nsMediaList* Media() = 0;
bool Disabled() const { return mDisabled; }
// The XPCOM SetDisabled is fine for WebIDL.
// WebIDL CSSStyleSheet API
virtual nsIDOMCSSRule* GetDOMOwnerRule() const = 0;
dom::CSSRuleList* GetCssRules(nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv);
uint32_t InsertRule(const nsAString& aRule, uint32_t aIndex,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv);
void DeleteRule(uint32_t aIndex,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv);
// WebIDL miscellaneous bits
inline dom::ParentObject GetParentObject() const;
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
// nsIDOMStyleSheet interface
NS_IMETHOD GetType(nsAString& aType) final;
NS_IMETHOD GetDisabled(bool* aDisabled) final;
NS_IMETHOD SetDisabled(bool aDisabled) final;
NS_IMETHOD GetOwnerNode(nsIDOMNode** aOwnerNode) final;
NS_IMETHOD GetParentStyleSheet(nsIDOMStyleSheet** aParentStyleSheet) final;
NS_IMETHOD GetHref(nsAString& aHref) final;
NS_IMETHOD GetTitle(nsAString& aTitle) final;
NS_IMETHOD GetMedia(nsIDOMMediaList** aMedia) final;
// nsIDOMCSSStyleSheet
NS_IMETHOD GetOwnerRule(nsIDOMCSSRule** aOwnerRule) final;
NS_IMETHOD GetCssRules(nsIDOMCSSRuleList** aCssRules) final;
NS_IMETHOD InsertRule(const nsAString& aRule, uint32_t aIndex,
uint32_t* aReturn) final;
NS_IMETHOD DeleteRule(uint32_t aIndex) final;
// Changes to sheets should be inside of a WillDirty-DidDirty pair.
// However, the calls do not need to be matched; it's ok to call
// WillDirty and then make no change and skip the DidDirty call.
inline void WillDirty();
inline void DidDirty();
private:
// Get a handle to the various stylesheet bits which live on the 'inner' for
// gecko stylesheets and live on the StyleSheet for Servo stylesheets.
inline StyleSheetInfo& SheetInfo();
inline const StyleSheetInfo& SheetInfo() const;
// Check if the rules are available for read and write.
// It does the security check as well as whether the rules have been
// completely loaded. aRv will have an exception set if this function
// returns false.
bool AreRulesAvailable(nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv);
protected:
// Return success if the subject principal subsumes the principal of our
// inner, error otherwise. This will also succeed if the subject has
// UniversalXPConnect or if access is allowed by CORS. In the latter case,
// it will set the principal of the inner to the subject principal.
void SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv);
nsString mTitle;
nsIDocument* mDocument; // weak ref; parents maintain this for their children
nsINode* mOwningNode; // weak ref
// mParsingMode controls access to nonstandard style constructs that
// are not safe for use on the public Web but necessary in UA sheets
// and/or useful in user sheets.
css::SheetParsingMode mParsingMode;
const StyleBackendType mType;
bool mDisabled;
};
} // namespace mozilla
#endif // mozilla_StyleSheet_h

View file

@ -0,0 +1,52 @@
/* -*- 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 mozilla_StyleSheetInfo_h
#define mozilla_StyleSheetInfo_h
#include "mozilla/dom/SRIMetadata.h"
#include "mozilla/net/ReferrerPolicy.h"
#include "mozilla/CORSMode.h"
#include "nsIURI.h"
namespace mozilla {
class CSSStyleSheet;
} // namespace mozilla
class nsCSSRuleProcessor;
class nsIPrincipal;
namespace mozilla {
/**
* Struct for data common to CSSStyleSheetInner and ServoStyleSheet.
*/
struct StyleSheetInfo
{
typedef net::ReferrerPolicy ReferrerPolicy;
StyleSheetInfo(CORSMode aCORSMode,
ReferrerPolicy aReferrerPolicy,
const dom::SRIMetadata& aIntegrity);
nsCOMPtr<nsIURI> mSheetURI; // for error reports, etc.
nsCOMPtr<nsIURI> mOriginalSheetURI; // for GetHref. Can be null.
nsCOMPtr<nsIURI> mBaseURI; // for resolving relative URIs
nsCOMPtr<nsIPrincipal> mPrincipal;
CORSMode mCORSMode;
// The Referrer Policy of a stylesheet is used for its child sheets, so it is
// stored here.
ReferrerPolicy mReferrerPolicy;
dom::SRIMetadata mIntegrity;
bool mComplete;
#ifdef DEBUG
bool mPrincipalSet;
#endif
};
} // namespace mozilla
#endif // mozilla_StyleSheetInfo_h

View file

@ -0,0 +1,176 @@
/* -*- 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 mozilla_StyleSheetInlines_h
#define mozilla_StyleSheetInlines_h
#include "mozilla/StyleSheetInfo.h"
#include "mozilla/ServoStyleSheet.h"
#include "mozilla/CSSStyleSheet.h"
namespace mozilla {
MOZ_DEFINE_STYLO_METHODS(StyleSheet, CSSStyleSheet, ServoStyleSheet)
StyleSheetInfo&
StyleSheet::SheetInfo()
{
if (IsServo()) {
return AsServo()->mSheetInfo;
}
return *AsGecko()->mInner;
}
const StyleSheetInfo&
StyleSheet::SheetInfo() const
{
if (IsServo()) {
return AsServo()->mSheetInfo;
}
return *AsGecko()->mInner;
}
bool
StyleSheet::IsInline() const
{
return !SheetInfo().mOriginalSheetURI;
}
nsIURI*
StyleSheet::GetSheetURI() const
{
return SheetInfo().mSheetURI;
}
nsIURI*
StyleSheet::GetOriginalURI() const
{
return SheetInfo().mOriginalSheetURI;
}
nsIURI*
StyleSheet::GetBaseURI() const
{
return SheetInfo().mBaseURI;
}
void
StyleSheet::SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI,
nsIURI* aBaseURI)
{
NS_PRECONDITION(aSheetURI && aBaseURI, "null ptr");
StyleSheetInfo& info = SheetInfo();
MOZ_ASSERT(!HasRules() && !info.mComplete,
"Can't call SetURIs on sheets that are complete or have rules");
info.mSheetURI = aSheetURI;
info.mOriginalSheetURI = aOriginalSheetURI;
info.mBaseURI = aBaseURI;
}
bool
StyleSheet::IsApplicable() const
{
return !mDisabled && SheetInfo().mComplete;
}
bool
StyleSheet::HasRules() const
{
MOZ_STYLO_FORWARD(HasRules, ())
}
void
StyleSheet::SetOwningDocument(nsIDocument* aDocument)
{
MOZ_STYLO_FORWARD(SetOwningDocument, (aDocument))
}
StyleSheet*
StyleSheet::GetParentSheet() const
{
MOZ_STYLO_FORWARD(GetParentSheet, ())
}
StyleSheet*
StyleSheet::GetParentStyleSheet() const
{
return GetParentSheet();
}
dom::ParentObject
StyleSheet::GetParentObject() const
{
if (mOwningNode) {
return dom::ParentObject(mOwningNode);
}
return dom::ParentObject(GetParentSheet());
}
void
StyleSheet::AppendStyleSheet(StyleSheet* aSheet)
{
MOZ_STYLO_FORWARD_CONCRETE(AppendStyleSheet,
(aSheet->AsGecko()), (aSheet->AsServo()))
}
nsIPrincipal*
StyleSheet::Principal() const
{
return SheetInfo().mPrincipal;
}
void
StyleSheet::SetPrincipal(nsIPrincipal* aPrincipal)
{
StyleSheetInfo& info = SheetInfo();
NS_PRECONDITION(!info.mPrincipalSet, "Should only set principal once");
if (aPrincipal) {
info.mPrincipal = aPrincipal;
#ifdef DEBUG
info.mPrincipalSet = true;
#endif
}
}
CORSMode
StyleSheet::GetCORSMode() const
{
return SheetInfo().mCORSMode;
}
net::ReferrerPolicy
StyleSheet::GetReferrerPolicy() const
{
return SheetInfo().mReferrerPolicy;
}
void
StyleSheet::GetIntegrity(dom::SRIMetadata& aResult) const
{
aResult = SheetInfo().mIntegrity;
}
size_t
StyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
MOZ_STYLO_FORWARD(SizeOfIncludingThis, (aMallocSizeOf))
}
#ifdef DEBUG
void
StyleSheet::List(FILE* aOut, int32_t aIndex) const
{
MOZ_STYLO_FORWARD(List, (aOut, aIndex))
}
#endif
void StyleSheet::WillDirty() { MOZ_STYLO_FORWARD(WillDirty, ()) }
void StyleSheet::DidDirty() { MOZ_STYLO_FORWARD(DidDirty, ()) }
}
#endif // mozilla_StyleSheetInlines_h

View file

@ -0,0 +1,121 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 mozilla_StyleStructContext_h
#define mozilla_StyleStructContext_h
#include "CounterStyleManager.h"
#include "mozilla/LookAndFeel.h"
#include "nsPresContext.h"
class nsDeviceContext;
/**
* Construction context for style structs.
*
* Certain Gecko style structs have historically taken an nsPresContext
* argument in their constructor, which is used to compute various things. This
* makes Gecko style structs document-specific (which Servo style structs are
* not), and means that the initial values for style-structs cannot be shared
* across the entire runtime (as is the case in Servo).
*
* We'd like to remove this constraint so that Gecko can get the benefits of the
* Servo model, and so that Gecko aligns better with the Servo style system when
* using it. Unfortunately, this may require a fair amount of work, especially
* related to text zoom.
*
* So as an intermediate step, we define a reduced API set of "things that are
* needed when constructing style structs". This just wraps and forwards to an
* nsPresContext in the Gecko case, and returns some default not-always-correct
* values in the Servo case. We can then focus on reducing this API surface to
* zero, at which point this can be removed.
*
* We don't put the type in namespace mozilla, since we expect it to be
* temporary, and the namespacing would clutter up nsStyleStruct.h.
*/
#ifdef MOZ_STYLO
#define SERVO_DEFAULT(default_val) { if (!mPresContext) { return default_val; } }
#else
#define SERVO_DEFAULT(default_val) { MOZ_ASSERT(mPresContext); }
#endif
class StyleStructContext {
public:
MOZ_IMPLICIT StyleStructContext(nsPresContext* aPresContext)
: mPresContext(aPresContext) { MOZ_ASSERT(aPresContext); }
static StyleStructContext ServoContext() { return StyleStructContext(); }
// XXXbholley: Need to make text zoom work with stylo. This probably means
// moving the zoom handling out of computed values and into a post-
// computation.
float TextZoom()
{
SERVO_DEFAULT(1.0);
return mPresContext->TextZoom();
}
const nsFont* GetDefaultFont(uint8_t aFontID)
{
// NB: The servo case only differ from the default case in terms of which
// font pref cache gets used. The distinction is probably unnecessary.
SERVO_DEFAULT(mozilla::StaticPresData::Get()->
GetDefaultFont(aFontID, GetLanguageFromCharset()));
return mPresContext->GetDefaultFont(aFontID, GetLanguageFromCharset());
}
uint32_t GetBidi()
{
SERVO_DEFAULT(0);
return mPresContext->GetBidi();
}
int32_t AppUnitsPerDevPixel();
nscoord DevPixelsToAppUnits(int32_t aPixels)
{
return NSIntPixelsToAppUnits(aPixels, AppUnitsPerDevPixel());
}
typedef mozilla::LookAndFeel LookAndFeel;
nscolor DefaultColor()
{
SERVO_DEFAULT(LookAndFeel::GetColor(LookAndFeel::eColorID_WindowForeground,
NS_RGB(0x00, 0x00, 0x00)));
return mPresContext->DefaultColor();
}
mozilla::CounterStyle* BuildCounterStyle(const nsSubstring& aName)
{
SERVO_DEFAULT(mozilla::CounterStyleManager::GetBuiltinStyle(NS_STYLE_LIST_STYLE_DISC));
return mPresContext->CounterStyleManager()->BuildCounterStyle(aName);
}
nsIAtom* GetLanguageFromCharset() const
{
SERVO_DEFAULT(nsGkAtoms::x_western);
return mPresContext->GetLanguageFromCharset();
}
already_AddRefed<nsIAtom> GetContentLanguage() const
{
SERVO_DEFAULT(do_AddRef(nsGkAtoms::x_western));
return mPresContext->GetContentLanguage();
}
private:
nsDeviceContext* DeviceContext()
{
SERVO_DEFAULT(HackilyFindSomeDeviceContext());
return mPresContext->DeviceContext();
}
nsDeviceContext* HackilyFindSomeDeviceContext();
StyleStructContext() : mPresContext(nullptr) {}
nsPresContext* mPresContext;
};
#undef SERVO_DEFAULT
#endif // mozilla_StyleStructContext_h

View file

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/*
This CSS stylesheet defines the rules to be applied to ImageDocuments that
are top level (e.g. not iframes).
*/
@media not print {
body {
margin: 0;
}
img {
text-align: center;
position: absolute;
margin: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
img.overflowingVertical {
/* If we're overflowing vertically, we need to set margin-top to
0. Otherwise we'll end up trying to vertically center, and end
up cutting off the top part of the image. */
margin-top: 0;
}
.completeRotation {
transition: transform 0.3s ease 0s;
}
}
img {
image-orientation: from-image;
}

View file

@ -0,0 +1,32 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/*
This CSS stylesheet defines the rules to be applied to VideoDocuments that
are top level (e.g. not iframes).
*/
body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
video {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
max-width: 100%;
max-height: 100%;
-moz-user-select: none;
}
video:focus {
outline-width: 0;
}

View file

@ -0,0 +1,343 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
@namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */
*|*::-moz-canvas {
cursor: text;
}
*|*:-moz-read-write :-moz-read-only {
-moz-user-select: all;
}
*|*:-moz-read-only > :-moz-read-write {
/* override the above -moz-user-select: all rule. */
-moz-user-select: -moz-text;
}
input:-moz-read-write > .anonymous-div:-moz-read-only,
textarea:-moz-read-write > .anonymous-div:-moz-read-only {
-moz-user-select: text;
}
/* Use default arrow over objects with size that
are selected when clicked on.
Override the browser's pointer cursor over links
*/
img:-moz-read-write, img:-moz-read-write[usemap], area:-moz-read-write,
object:-moz-read-write, object:-moz-read-write[usemap],
applet:-moz-read-write, hr:-moz-read-write, button:-moz-read-write,
select:-moz-read-write,
a:-moz-read-write:link img, a:-moz-read-write:visited img,
a:-moz-read-write:active img, a:-moz-read-write:-moz-only-whitespace[name] {
cursor: default;
}
*|*:any-link:-moz-read-write {
cursor: text;
}
/* Prevent clicking on links from going to link */
a:link:-moz-read-write img, a:visited:-moz-read-write img,
a:active:-moz-read-write img {
-moz-user-input: none;
}
/* We suppress user/author's prefs for link underline,
so we must set explicitly. This isn't good!
*/
a:link:-moz-read-write {
text-decoration: underline -moz-anchor-decoration;
color: -moz-hyperlinktext;
}
/* Allow double-clicks on these widgets to open properties dialogs
XXX except when the widget has disabled attribute */
*|*:-moz-read-write > input:-moz-read-only,
*|*:-moz-read-write > button:-moz-read-only,
*|*:-moz-read-write > textarea:-moz-read-only {
-moz-user-select: all;
-moz-user-input: auto !important;
-moz-user-focus: none !important;
}
/* XXX Still need a better way of blocking other events to these widgets */
select:-moz-read-write,
*|*:-moz-read-write > input:disabled,
*|*:-moz-read-write > input[type="checkbox"],
*|*:-moz-read-write > input[type="radio"],
*|*:-moz-read-write > input[type="file"],
input[contenteditable="true"]:disabled,
input[contenteditable="true"][type="checkbox"],
input[contenteditable="true"][type="radio"],
input[contenteditable="true"][type="file"] {
-moz-user-select: all;
-moz-user-input: none !important;
-moz-user-focus: none !important;
}
/* emulation of non-standard HTML <marquee> tag */
marquee:-moz-read-write {
-moz-binding: url('chrome://xbl-marquee/content/xbl-marquee.xml#marquee-horizontal-editable');
}
marquee[direction="up"]:-moz-read-write, marquee[direction="down"]:-moz-read-write {
-moz-binding: url('chrome://xbl-marquee/content/xbl-marquee.xml#marquee-vertical-editable');
}
*|*:-moz-read-write > input[type="hidden"],
input[contenteditable="true"][type="hidden"] {
border: 1px solid black !important;
visibility: visible !important;
}
label:-moz-read-write {
-moz-user-select: all;
}
*|*::-moz-display-comboboxcontrol-frame {
-moz-user-select: text;
}
option:-moz-read-write {
-moz-user-select: text;
}
/* the following rules are for Image Resizing */
span[\_moz_anonclass="mozResizer"] {
width: 5px;
height: 5px;
position: absolute;
border: 1px black solid;
background-color: white;
-moz-user-select: none;
z-index: 2147483646; /* max value -1 for this property */
}
/* we can't use :active below */
span[\_moz_anonclass="mozResizer"][\_moz_activated],
span[\_moz_anonclass="mozResizer"]:hover {
background-color: black;
}
span[\_moz_anonclass="mozResizer"].hidden,
span[\_moz_anonclass="mozResizingShadow"].hidden,
img[\_moz_anonclass="mozResizingShadow"].hidden,
span[\_moz_anonclass="mozGrabber"].hidden,
span[\_moz_anonclass="mozResizingInfo"].hidden,
a[\_moz_anonclass="mozTableRemoveRow"].hidden,
a[\_moz_anonclass="mozTableRemoveColumn"].hidden {
display: none !important;
}
span[\_moz_anonclass="mozResizer"][anonlocation="nw"] {
cursor: nw-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="n"] {
cursor: n-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="ne"] {
cursor: ne-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="w"] {
cursor: w-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="e"] {
cursor: e-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="sw"] {
cursor: sw-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="s"] {
cursor: s-resize;
}
span[\_moz_anonclass="mozResizer"][anonlocation="se"] {
cursor: se-resize;
}
span[\_moz_anonclass="mozResizingShadow"],
img[\_moz_anonclass="mozResizingShadow"] {
outline: thin dashed black;
-moz-user-select: none;
opacity: 0.5;
position: absolute;
z-index: 2147483647; /* max value for this property */
}
span[\_moz_anonclass="mozResizingInfo"] {
font-family: sans-serif;
font-size: x-small;
color: black;
background-color: #d0d0d0;
border: ridge 2px #d0d0d0;
padding: 2px;
position: absolute;
z-index: 2147483647; /* max value for this property */
}
img[\_moz_resizing] {
outline: thin solid black;
}
*[\_moz_abspos] {
outline: silver ridge 2px;
z-index: 2147483645 !important; /* max value -2 for this property */
}
*[\_moz_abspos="white"] {
background-color: white !important;
}
*[\_moz_abspos="black"] {
background-color: black !important;
}
span[\_moz_anonclass="mozGrabber"] {
outline: ridge 2px silver;
padding: 2px;
position: absolute;
width: 12px;
height: 12px;
background-image: url("resource://gre/res/grabber.gif");
background-repeat: no-repeat;
background-position: center center;
-moz-user-select: none;
cursor: move;
}
/* INLINE TABLE EDITING */
a[\_moz_anonclass="mozTableAddColumnBefore"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0px !important;
width: 4px;
height: 8px;
background-image: url("resource://gre/res/table-add-column-before.gif");
background-repeat: no-repeat;
background-position: center center;
-moz-user-select: none;
-moz-user-focus: none !important;
}
a[\_moz_anonclass="mozTableAddColumnBefore"]:hover {
background-image: url("resource://gre/res/table-add-column-before-hover.gif");
}
a[\_moz_anonclass="mozTableAddColumnBefore"]:active {
background-image: url("resource://gre/res/table-add-column-before-active.gif");
}
a[\_moz_anonclass="mozTableAddColumnAfter"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0px !important;
width: 4px;
height: 8px;
background-image: url("resource://gre/res/table-add-column-after.gif");
background-repeat: no-repeat;
background-position: center center;
-moz-user-select: none;
-moz-user-focus: none !important;
}
a[\_moz_anonclass="mozTableAddColumnAfter"]:hover {
background-image: url("resource://gre/res/table-add-column-after-hover.gif");
}
a[\_moz_anonclass="mozTableAddColumnAfter"]:active {
background-image: url("resource://gre/res/table-add-column-after-active.gif");
}
a[\_moz_anonclass="mozTableRemoveColumn"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0px !important;
width: 8px;
height: 8px;
background-image: url("resource://gre/res/table-remove-column.gif");
background-repeat: no-repeat;
background-position: center center;
-moz-user-select: none;
-moz-user-focus: none !important;
}
a[\_moz_anonclass="mozTableRemoveColumn"]:hover {
background-image: url("resource://gre/res/table-remove-column-hover.gif");
}
a[\_moz_anonclass="mozTableRemoveColumn"]:active {
background-image: url("resource://gre/res/table-remove-column-active.gif");
}
a[\_moz_anonclass="mozTableAddRowBefore"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0px !important;
width: 8px;
height: 4px;
background-image: url("resource://gre/res/table-add-row-before.gif");
background-repeat: no-repeat;
background-position: center center;
-moz-user-select: none;
-moz-user-focus: none !important;
}
a[\_moz_anonclass="mozTableAddRowBefore"]:hover {
background-image: url("resource://gre/res/table-add-row-before-hover.gif");
}
a[\_moz_anonclass="mozTableAddRowBefore"]:active {
background-image: url("resource://gre/res/table-add-row-before-active.gif");
}
a[\_moz_anonclass="mozTableAddRowAfter"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0px !important;
width: 8px;
height: 4px;
background-image: url("resource://gre/res/table-add-row-after.gif");
background-repeat: no-repeat;
background-position: center center;
-moz-user-select: none;
-moz-user-focus: none !important;
}
a[\_moz_anonclass="mozTableAddRowAfter"]:hover {
background-image: url("resource://gre/res/table-add-row-after-hover.gif");
}
a[\_moz_anonclass="mozTableAddRowAfter"]:active {
background-image: url("resource://gre/res/table-add-row-after-active.gif");
}
a[\_moz_anonclass="mozTableRemoveRow"] {
position: absolute;
z-index: 2147483647; /* max value for this property */
text-decoration: none !important;
border: none 0px !important;
width: 8px;
height: 8px;
background-image: url("resource://gre/res/table-remove-row.gif");
background-repeat: no-repeat;
background-position: center center;
-moz-user-select: none;
-moz-user-focus: none !important;
}
a[\_moz_anonclass="mozTableRemoveRow"]:hover {
background-image: url("resource://gre/res/table-remove-row-hover.gif");
}
a[\_moz_anonclass="mozTableRemoveRow"]:active {
background-image: url("resource://gre/res/table-remove-row-active.gif");
}

View file

@ -0,0 +1,84 @@
/* ----------------------------------
* SWITCHES
* ---------------------------------- */
label.pack-switch {
display: inline-block;
vertical-align: middle;
width: 100%;
height: 5rem;
position: relative;
background: none;
}
label.pack-switch span {
float: left;
font-size: 1.8rem;
color: #333;
padding: 1rem 0 0;
height: 6rem;
line-height: 3rem;
box-sizing: border-box;
display: block;
}
label.pack-switch input {
margin: 0;
opacity: 0;
position: absolute;
top: 0;
left: 0;
}
label.pack-switch input:checked ~ span:after {
background-position: center bottom;
}
/* ----------------------------------
* ON/OFF SWITCHES
* ---------------------------------- */
label.pack-switch input ~ span:after {
content: '';
position: absolute;
right: 0;
top: 50%;
width: 6rem;
margin: -1.4rem 0 0;
height: 2.7rem;
border-radius: 1.35rem;
overflow: hidden;
background: #e6e6e6 url(images/background_off.png) no-repeat -3.2rem 0 / 9.2rem 2.7rem;
transition: background 0.2s ease;
}
/* switch: 'ON' state */
label.pack-switch input:checked ~ span:after {
background: #e6e6e6 url(images/background.png) no-repeat 0 0 / 9.2rem 2.7rem;
}
/* switch: disabled state */
label.pack-switch input:disabled ~ span:after {
opacity: 0.4;
}
label.pack-switch input.uninit ~ span:after {
transition: none;
}
/******************************************************************************
* Right-To-Left tweaks
*/
html[dir="rtl"] label.pack-switch input {
left: auto;
right: 0;
}
html[dir="rtl"] label.pack-switch input ~ span:after {
left: 0;
right: auto;
}
html[dir="rtl"] label.pack-switch input ~ span:after {
background-position: 0;
}

View file

@ -0,0 +1,124 @@
<!DOCTYPE html>
<!--
This is a slightly minimised, modified and self-contained version of
gaia_switch/examples/index.html from the Gaia repository.
-->
<script>
'use strict';
(function(exports) {
/**
* ComponentUtils is a utility which allows us to use web components earlier
* than we should be able to by polyfilling and fixing platform deficiencies.
*/
var ComponentUtils = {
/**
* Injects a style.css into both the shadow root and outside the shadow
* root so we can style projected content. Bug 992249.
*/
style: function(baseUrl) {
var style = document.createElement('style');
style.setAttribute('scoped', '');
var url = baseUrl + '1017798-1.css';
style.innerHTML = '@import url(' + url + ');';
this.appendChild(style);
if (!this.shadowRoot) {
return;
}
// The setTimeout is necessary to avoid missing @import styles
// when appending two stylesheets. Bug 1003294.
setTimeout(function nextTick() {
this.shadowRoot.appendChild(style.cloneNode(true));
}.bind(this));
}
};
exports.ComponentUtils = ComponentUtils;
}(window));
</script>
<script>
'use strict';
/* global ComponentUtils */
window.GaiaSwitch = (function(win) {
// Extend from the HTMLElement prototype
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
var shadow = this.createShadowRoot();
this._template = template.content.cloneNode(true);
this._input = this._template.querySelector('input[type="checkbox"]');
var checked = this.getAttribute('checked');
if (checked !== null) {
this._input.checked = true;
}
shadow.appendChild(this._template);
ComponentUtils.style.call(this, '');
};
/**
* Proxy the checked property to the input element.
*/
Object.defineProperty( proto, 'checked', {
get: function() {
return this._input.checked;
},
set: function(value) {
this._input.checked = value;
}
});
/**
* Proxy the name property to the input element.
*/
Object.defineProperty( proto, 'name', {
get: function() {
return this.getAttribute('name');
},
set: function(value) {
this.setAttribute('name', value);
}
});
// HACK: Create a <template> in memory at runtime.
// When the custom-element is created we clone
// this template and inject into the shadow-root.
// Prior to this we would have had to copy/paste
// the template into the <head> of every app that
// wanted to use <gaia-switch>, this would make
// markup changes complicated, and could lead to
// things getting out of sync. This is a short-term
// hack until we can import entire custom-elements
// using HTML Imports (bug 877072).
var template = document.createElement('template');
template.innerHTML = '<label id="switch-label" class="pack-switch">' +
'<input type="checkbox">' +
'<span><content select="label"></content></span>' +
'</label>';
// Register and return the constructor
return document.registerElement('gaia-switch', { prototype: proto });
})(window);
</script>
<body>
<section>
<gaia-switch>
<label>With a label</label>
</gaia-switch>
</section>
<script>
window.onload = function() {
document.querySelector('gaia-switch').checked = true;
};
</script>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script>
function boom()
{
document.documentElement.style.animation = "137438953471s bounce";
document.documentElement.offsetHeight;
document.documentElement.style.animationIterationCount = "infinite";
}
</script>
</head>
<body onload="boom();"></body>
</html>

View file

@ -0,0 +1,33 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>International Herald Tribune</title>
<script type="text/javascript">
function displayFix() {
document.getElementById("bodyNode").style.display = "block";
}
</script>
<style type="text/css">
#clippingsContainer {overflow:auto;}
#menuSearch {position:absolute;}
</style>
</head>
<body id="bodyNode" onload="displayFix()">
<div>
<div id="menuSearch"><input type="text"></div>
<div id="clippingsContainer"></div>
</div>
<table>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
</table>
</body></html>

Some files were not shown because too many files have changed in this diff Show more