Skip to content

Commit

Permalink
Bug 1107378 - Part 2: Make the CSS Parser call out to the unprefixing…
Browse files Browse the repository at this point in the history
… service, when it detects a vendor-prefixed property name (if pref is enabled). r=dbaron
  • Loading branch information
rmottola committed Aug 6, 2019
1 parent c860167 commit 1fe4e48
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 3 deletions.
196 changes: 193 additions & 3 deletions layout/style/nsCSSParser.cpp
Expand Up @@ -38,6 +38,7 @@
#include "nsIMediaList.h"
#include "nsStyleUtil.h"
#include "nsIPrincipal.h"
#include "nsICSSUnprefixingService.h"
#include "prprf.h"
#include "nsContentUtils.h"
#include "nsAutoPtr.h"
Expand All @@ -56,6 +57,7 @@ typedef nsCSSProps::KTableValue KTableValue;

// pref-backed bool values (hooked up in nsCSSParser::Startup)
static bool sOpentypeSVGEnabled;
static bool sUnprefixingServiceEnabled;

const uint32_t
nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
Expand Down Expand Up @@ -420,6 +422,103 @@ class CSSParserImpl {
nsIURI* aSheetURI, nsIURI* aBaseURI,
nsIPrincipal* aSheetPrincipal);
void ReleaseScanner(void);

/**
* This is a RAII class which behaves like an "AutoRestore<>" for our parser
* input state. When instantiated, this class saves the current parser input
* state (in a CSSParserInputState object), and it restores the parser to
* that state when destructed, unless "DoNotRestore()" has been called.
*/
class MOZ_STACK_CLASS nsAutoCSSParserInputStateRestorer {
public:
explicit nsAutoCSSParserInputStateRestorer(CSSParserImpl* aParser
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mParser(aParser),
mShouldRestore(true)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
mParser->SaveInputState(mSavedState);
}

void DoNotRestore()
{
mShouldRestore = false;
}

~nsAutoCSSParserInputStateRestorer()
{
if (mShouldRestore) {
mParser->RestoreSavedInputState(mSavedState);
}
}

private:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
CSSParserImpl* mParser;
CSSParserInputState mSavedState;
bool mShouldRestore;
};

/**
* This is a RAII class which creates a temporary nsCSSScanner for the given
* string, and reconfigures aParser to use *that* scanner instead of its
* existing scanner, until we go out of scope. (This allows us to rewrite
* a portion of a stylesheet using a temporary string, and switch to parsing
* that rewritten section, and then resume parsing the original stylesheet.)
*
* aParser must have a non-null nsCSSScanner (which we'll be temporarily
* replacing) and ErrorReporter (which this class will co-opt for the
* temporary parser). While we're in scope, we also suppress error reporting,
* so it doesn't really matter which reporter we use. We suppress reporting
* because this class is only used with CSS that is synthesized & didn't
* come directly from an author, and it would be confusing if we reported
* syntax errors for CSS that an author didn't provide.
*
* XXXdholbert we could also change this & report errors, if needed. Might
* want to customize the error reporting somehow though.
*/
class MOZ_STACK_CLASS nsAutoScannerChanger {
public:
nsAutoScannerChanger(CSSParserImpl* aParser,
const nsAString& aStringToScan
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mParser(aParser),
mOriginalScanner(aParser->mScanner),
mStringScanner(aStringToScan, 0),
mParserStateRestorer(aParser),
mErrorSuppresser(aParser)
{
MOZ_ASSERT(mOriginalScanner,
"Shouldn't use nsAutoScannerChanger unless we already "
"have a scanner");
MOZ_GUARD_OBJECT_NOTIFIER_INIT;

// Set & setup the new scanner:
mParser->mScanner = &mStringScanner;
mStringScanner.SetErrorReporter(mParser->mReporter);

// We might've had push-back on our original scanner (and if we did,
// that fact is saved via mParserStateRestorer). But we don't have
// push-back in mStringScanner, so clear that flag.
mParser->mHavePushBack = false;
}

~nsAutoScannerChanger()
{
// Restore original scanner. All other cleanup is done by RAII members.
mParser->mScanner = mOriginalScanner;
}

private:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
CSSParserImpl* mParser;
nsCSSScanner *mOriginalScanner;
nsCSSScanner mStringScanner;
nsAutoCSSParserInputStateRestorer mParserStateRestorer;
nsAutoSuppressErrors mErrorSuppresser;
};


bool IsSVGMode() const {
return mScanner->IsSVGMode();
}
Expand Down Expand Up @@ -609,8 +708,10 @@ class CSSParserImpl {
bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator);

enum {
eParseDeclaration_InBraces = 1 << 0,
eParseDeclaration_AllowImportant = 1 << 1
eParseDeclaration_InBraces = 1 << 0,
eParseDeclaration_AllowImportant = 1 << 1,
// The declaration we're parsing was generated by the CSSUnprefixingService:
eParseDeclaration_FromUnprefixingSvc = 1 << 2
};
enum nsCSSContextType {
eCSSContext_General,
Expand All @@ -625,6 +726,14 @@ class CSSParserImpl {
bool* aChanged,
nsCSSContextType aContext = eCSSContext_General);

bool ShouldUseUnprefixingService();
bool ParsePropertyWithUnprefixingService(const nsAString& aPropertyName,
css::Declaration* aDeclaration,
uint32_t aFlags,
bool aMustCallValueAppended,
bool* aChanged,
nsCSSContextType aContext);

bool ParseProperty(nsCSSProperty aPropID);
bool ParsePropertyByFunction(nsCSSProperty aPropID);
bool ParseSingleValueProperty(nsCSSValue& aValue,
Expand Down Expand Up @@ -6476,6 +6585,73 @@ CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs)
}
#endif

bool
CSSParserImpl::ShouldUseUnprefixingService()
{
if (!sUnprefixingServiceEnabled) {
return false;
}

// XXXdholbert Bug 1132743: Check if stylesheet URI is on fixlist here.
return true;
}

bool
CSSParserImpl::ParsePropertyWithUnprefixingService(
const nsAString& aPropertyName,
css::Declaration* aDeclaration,
uint32_t aFlags,
bool aMustCallValueAppended,
bool* aChanged,
nsCSSContextType aContext)
{
MOZ_ASSERT(ShouldUseUnprefixingService(),
"Caller should've checked ShouldUseUnprefixingService()");

nsCOMPtr<nsICSSUnprefixingService> unprefixingSvc =
do_GetService(NS_CSSUNPREFIXINGSERVICE_CONTRACTID);
NS_ENSURE_TRUE(unprefixingSvc, false);

// Save the state so we can jump back to this spot if our unprefixing fails
// (so we can behave as if we didn't even try to unprefix).
nsAutoCSSParserInputStateRestorer parserStateBeforeTryingToUnprefix(this);

// Caller has already parsed the first half of the declaration --
// aPropertyName and the ":". Now, we record the rest of the CSS declaration
// (the part after ':') into rightHalfOfDecl. (This is the property value,
// plus anything else up to the end of the declaration -- maybe "!important",
// maybe trailing junk characters, maybe a semicolon, maybe a trailing "}".)
bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
nsAutoString rightHalfOfDecl;
mScanner->StartRecording();
SkipDeclaration(checkForBraces);
mScanner->StopRecording(rightHalfOfDecl);

// Try to unprefix:
bool success;
nsAutoString unprefixedDecl;
nsresult rv =
unprefixingSvc->GenerateUnprefixedDeclaration(aPropertyName,
rightHalfOfDecl,
unprefixedDecl, &success);
if (NS_FAILED(rv) || !success) {
return false;
}

// Attempt to parse the unprefixed declaration:
nsAutoScannerChanger scannerChanger(this, unprefixedDecl);
success = ParseDeclaration(aDeclaration,
aFlags | eParseDeclaration_FromUnprefixingSvc,
aMustCallValueAppended, aChanged, aContext);
if (success) {
// We succeeded, so we'll leave the parser pointing at the end of
// the declaration; don't restore it to the pre-recording position.
parserStateBeforeTryingToUnprefix.DoNotRestore();
}

return success;
}

//----------------------------------------------------------------------

bool
Expand Down Expand Up @@ -6565,7 +6741,19 @@ CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
(aContext == eCSSContext_Page &&
!nsCSSProps::PropHasFlags(propID,
CSS_PROPERTY_APPLIES_TO_PAGE_RULE))) { // unknown property
if (!NonMozillaVendorIdentifier(propertyName)) {
if (NonMozillaVendorIdentifier(propertyName)) {
if (!mInSupportsCondition &&
aContext == eCSSContext_General &&
!(aFlags & eParseDeclaration_FromUnprefixingSvc) && // no recursion
ShouldUseUnprefixingService()) {
if (ParsePropertyWithUnprefixingService(propertyName,
aDeclaration, aFlags,
aMustCallValueAppended,
aChanged, aContext)) {
return true;
}
}
} else {
REPORT_UNEXPECTED_P(PEUnknownProperty, propertyName);
REPORT_UNEXPECTED(PEDeclDropped);
OUTPUT_ERROR();
Expand Down Expand Up @@ -15196,6 +15384,8 @@ nsCSSParser::Startup()
{
Preferences::AddBoolVarCache(&sOpentypeSVGEnabled,
"gfx.font_rendering.opentype_svg.enabled");
Preferences::AddBoolVarCache(&sUnprefixingServiceEnabled,
"layout.css.unprefixing-service.enabled");
}

nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
Expand Down
4 changes: 4 additions & 0 deletions modules/libpref/init/all.js
Expand Up @@ -2483,6 +2483,10 @@ pref("layout.css.prefixes.animations", true);
pref("layout.css.prefixes.box-sizing", true);
pref("layout.css.prefixes.font-features", true);

// Is the CSS Unprefixing Service enabled? (This service emulates support
// for certain vendor-prefixed properties & values, for sites on a "fixlist".)
pref("layout.css.unprefixing-service.enabled", false);

// Is support for the :scope selector enabled?
pref("layout.css.scope-pseudo.enabled", true);

Expand Down

0 comments on commit 1fe4e48

Please sign in to comment.