From 6c68aef211af4f8086c4ac5edf36d1b9cd4406aa Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Thu, 28 Apr 2011 10:21:37 -0700 Subject: [PATCH] Add support for regexp() function in @-moz-document rule. (Bug 398962) r=bzbarsky --- content/base/public/nsContentUtils.h | 19 ++++ content/base/src/nsContentUtils.cpp | 34 +++++++ .../html/content/src/nsHTMLInputElement.cpp | 36 +------ content/html/content/src/nsHTMLInputElement.h | 17 ---- .../en-US/chrome/layout/css.properties | 1 + layout/style/nsCSSParser.cpp | 18 +++- layout/style/nsCSSRules.cpp | 17 +++- layout/style/nsCSSRules.h | 3 +- layout/style/test/chrome/Makefile.in | 8 ++ .../test/chrome/moz_document_helper.html | 2 + .../test/chrome/test_moz_document_rules.html | 99 +++++++++++++++++++ 11 files changed, 198 insertions(+), 56 deletions(-) create mode 100644 layout/style/test/chrome/moz_document_helper.html create mode 100644 layout/style/test/chrome/test_moz_document_rules.html diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index bbaaecc7244f..cbeed4f7dc58 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -1777,6 +1777,25 @@ public: FindInternalContentViewer(const char* aType, ContentViewerType* aLoaderType = nsnull); + /** + * This helper method returns true if the aPattern pattern matches aValue. + * aPattern should not contain leading and trailing slashes (/). + * The pattern has to match the entire value not just a subset. + * aDocument must be a valid pointer (not null). + * + * This is following the HTML5 specification: + * http://dev.w3.org/html5/spec/forms.html#attr-input-pattern + * + * WARNING: This method mutates aPattern and aValue! + * + * @param aValue the string to check. + * @param aPattern the string defining the pattern. + * @param aDocument the owner document of the element. + * @result whether the given string is matches the pattern. + */ + static PRBool IsPatternMatching(nsAString& aValue, nsAString& aPattern, + nsIDocument* aDocument); + /** * Calling this adds support for * ontouch* event handler DOM attributes. diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 60f5c369ee0b..fd93d929a248 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -6552,3 +6552,37 @@ nsContentUtils::FindInternalContentViewer(const char* aType, return NULL; } + +// static +PRBool +nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern, + nsIDocument* aDocument) +{ + NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)"); + NS_ENSURE_TRUE(aDocument->GetScriptGlobalObject(), PR_TRUE); + + JSContext* ctx = (JSContext*) aDocument->GetScriptGlobalObject()-> + GetContext()->GetNativeContext(); + NS_ENSURE_TRUE(ctx, PR_TRUE); + + JSAutoRequest ar(ctx); + + // The pattern has to match the entire value. + aPattern.Insert(NS_LITERAL_STRING("^(?:"), 0); + aPattern.Append(NS_LITERAL_STRING(")$")); + + JSObject* re = JS_NewUCRegExpObjectNoStatics(ctx, reinterpret_cast + (aPattern.BeginWriting()), + aPattern.Length(), 0); + NS_ENSURE_TRUE(re, PR_TRUE); + + jsval rval = JSVAL_NULL; + size_t idx = 0; + JSBool res; + + res = JS_ExecuteRegExpNoStatics(ctx, re, reinterpret_cast + (aValue.BeginWriting()), + aValue.Length(), &idx, JS_TRUE, &rval); + + return res == JS_FALSE || rval != JSVAL_NULL; +} diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp index 0b0ebff0abf0..bbc1f4523c9c 100644 --- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -3813,7 +3813,7 @@ nsHTMLInputElement::HasPatternMismatch() const return PR_FALSE; } - return !IsPatternMatching(value, pattern, doc); + return !nsContentUtils::IsPatternMatching(value, pattern, doc); } void @@ -4095,40 +4095,6 @@ nsHTMLInputElement::IsValidEmailAddress(const nsAString& aValue) return PR_TRUE; } -//static -PRBool -nsHTMLInputElement::IsPatternMatching(nsAString& aValue, nsAString& aPattern, - nsIDocument* aDocument) -{ - NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)"); - NS_ENSURE_TRUE(aDocument->GetScriptGlobalObject(), PR_TRUE); - - JSContext* ctx = (JSContext*) aDocument->GetScriptGlobalObject()-> - GetContext()->GetNativeContext(); - NS_ENSURE_TRUE(ctx, PR_TRUE); - - JSAutoRequest ar(ctx); - - // The pattern has to match the entire value. - aPattern.Insert(NS_LITERAL_STRING("^(?:"), 0); - aPattern.Append(NS_LITERAL_STRING(")$")); - - JSObject* re = JS_NewUCRegExpObjectNoStatics(ctx, reinterpret_cast - (aPattern.BeginWriting()), - aPattern.Length(), 0); - NS_ENSURE_TRUE(re, PR_TRUE); - - jsval rval = JSVAL_NULL; - size_t idx = 0; - JSBool res; - - res = JS_ExecuteRegExpNoStatics(ctx, re, reinterpret_cast - (aValue.BeginWriting()), - aValue.Length(), &idx, JS_TRUE, &rval); - - return res == JS_FALSE || rval != JSVAL_NULL; -} - NS_IMETHODIMP_(PRBool) nsHTMLInputElement::IsSingleLineTextControl() const { diff --git a/content/html/content/src/nsHTMLInputElement.h b/content/html/content/src/nsHTMLInputElement.h index dde9ae485d1f..ac084f39bab3 100644 --- a/content/html/content/src/nsHTMLInputElement.h +++ b/content/html/content/src/nsHTMLInputElement.h @@ -368,23 +368,6 @@ protected: */ static PRBool IsValidEmailAddressList(const nsAString& aValue); - /** - * This helper method returns true if the aPattern pattern matches aValue. - * aPattern should not contain leading and trailing slashes (/). - * The pattern has to match the entire value not just a subset. - * aDocument must be a valid pointer (not null). - * - * This is following the HTML5 specification: - * http://dev.w3.org/html5/spec/forms.html#attr-input-pattern - * - * @param aValue the string to check. - * @param aPattern the string defining the pattern. - * @param aDocument the owner document of the element. - * @result whether the given string is matches the pattern. - */ - static PRBool IsPatternMatching(nsAString& aValue, nsAString& aPattern, - nsIDocument* aDocument); - // Helper method nsresult SetValueInternal(const nsAString& aValue, PRBool aUserInput, diff --git a/dom/locales/en-US/chrome/layout/css.properties b/dom/locales/en-US/chrome/layout/css.properties index eb7b5659325f..f5eb5e28d77d 100644 --- a/dom/locales/en-US/chrome/layout/css.properties +++ b/dom/locales/en-US/chrome/layout/css.properties @@ -58,6 +58,7 @@ PEGroupRuleEOF=end of @media or @-moz-document rule PEGroupRuleNestedAtRule=%1$S rule not allowed within @media or @-moz-document rule. PEMozDocRuleBadFunc=Expected url(), url-prefix(), or domain() in @-moz-document rule but found '%1$S'. PEMozDocRuleNotURI=Expected URI in @-moz-document rule but found '%1$S'. +PEMozDocRuleNotString=Expected string in @-moz-document rule regexp() function but found '%1$S'. PEAtNSPrefixEOF=namespace prefix in @namespace rule PEAtNSURIEOF=namespace URI in @namespace rule PEAtNSUnexpected=Unexpected token within @namespace: '%1$S'. diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index bb7c9265afdf..47eb784104ad 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -2116,7 +2116,8 @@ CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData) !(eCSSToken_URL == mToken.mType || (eCSSToken_Function == mToken.mType && (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") || - mToken.mIdent.LowerCaseEqualsLiteral("domain"))))) { + mToken.mIdent.LowerCaseEqualsLiteral("domain") || + mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) { REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc); delete urls; return PR_FALSE; @@ -2126,6 +2127,21 @@ CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData) if (mToken.mType == eCSSToken_URL) { cur->func = css::DocumentRule::eURL; CopyUTF16toUTF8(mToken.mIdent, cur->url); + } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) { + // regexp() is different from url-prefix() and domain() (but + // probably the way they *should* have been* in that it requires a + // string argument, and doesn't try to behave like url(). + cur->func = css::DocumentRule::eRegExp; + GetToken(PR_TRUE); + // copy before we know it's valid (but before ExpectSymbol changes + // mToken.mIdent) + CopyUTF16toUTF8(mToken.mIdent, cur->url); + if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', PR_TRUE)) { + REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString); + SkipUntil(')'); + delete urls; + return PR_FALSE; + } } else { if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) { cur->func = css::DocumentRule::eURLPrefix; diff --git a/layout/style/nsCSSRules.cpp b/layout/style/nsCSSRules.cpp index 262dfd893593..6d0b9d7f45a8 100644 --- a/layout/style/nsCSSRules.cpp +++ b/layout/style/nsCSSRules.cpp @@ -947,6 +947,8 @@ DocumentRule::List(FILE* out, PRInt32 aIndent) const break; case eDomain: str.AppendLiteral("domain(\""); + case eRegExp: + str.AppendLiteral("regexp(\""); break; } nsCAutoString escapedURL(url->url); @@ -998,6 +1000,9 @@ DocumentRule::GetCssText(nsAString& aCssText) case eDomain: aCssText.AppendLiteral("domain("); break; + case eRegExp: + aCssText.AppendLiteral("regexp("); + break; } nsStyleUtil::AppendEscapedCSSString(NS_ConvertUTF8toUTF16(url->url), aCssText); @@ -1049,9 +1054,10 @@ DocumentRule::DeleteRule(PRUint32 aIndex) // GroupRule interface /* virtual */ PRBool DocumentRule::UseForPresentation(nsPresContext* aPresContext, - nsMediaQueryResultCacheKey& aKey) + nsMediaQueryResultCacheKey& aKey) { - nsIURI *docURI = aPresContext->Document()->GetDocumentURI(); + nsIDocument *doc = aPresContext->Document(); + nsIURI *docURI = doc->GetDocumentURI(); nsCAutoString docURISpec; if (docURI) docURI->GetSpec(docURISpec); @@ -1080,6 +1086,13 @@ DocumentRule::UseForPresentation(nsPresContext* aPresContext, return PR_TRUE; } } break; + case eRegExp: { + NS_ConvertUTF8toUTF16 spec(docURISpec); + NS_ConvertUTF8toUTF16 regex(url->url); + if (nsContentUtils::IsPatternMatching(spec, regex, doc)) { + return PR_TRUE; + } + } break; } } diff --git a/layout/style/nsCSSRules.h b/layout/style/nsCSSRules.h index 6e8d74ad9ee8..742b26bd65c8 100644 --- a/layout/style/nsCSSRules.h +++ b/layout/style/nsCSSRules.h @@ -156,7 +156,8 @@ public: enum Function { eURL, eURLPrefix, - eDomain + eDomain, + eRegExp }; struct URL { diff --git a/layout/style/test/chrome/Makefile.in b/layout/style/test/chrome/Makefile.in index 735e0c029310..482fa6b1b588 100644 --- a/layout/style/test/chrome/Makefile.in +++ b/layout/style/test/chrome/Makefile.in @@ -49,8 +49,16 @@ _CHROME_FILES = \ bug535806-html.html \ bug535806-xul.xul \ test_hover.html \ + test_moz_document_rules.html \ hover_helper.html \ $(NULL) +_TEST_FILES = \ + moz_document_helper.html \ + $(NULL) + libs:: $(_CHROME_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) + +libs:: $(_TEST_FILES) + $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) diff --git a/layout/style/test/chrome/moz_document_helper.html b/layout/style/test/chrome/moz_document_helper.html new file mode 100644 index 000000000000..8b331b19e07a --- /dev/null +++ b/layout/style/test/chrome/moz_document_helper.html @@ -0,0 +1,2 @@ + +
diff --git a/layout/style/test/chrome/test_moz_document_rules.html b/layout/style/test/chrome/test_moz_document_rules.html new file mode 100644 index 000000000000..485eeebe5917 --- /dev/null +++ b/layout/style/test/chrome/test_moz_document_rules.html @@ -0,0 +1,99 @@ + + + + Test for @-moz-document rules + + + + + + +Mozilla Bug 398962 + +
+
+
+ +