☠☠ backed out by 2d2f995703ac ☠ ☠ | |
author | Daniel Holbert <dholbert@cs.stanford.edu> |
Fri, 13 Mar 2015 13:15:09 -0700 | |
changeset 233632 | 720842726906d72ddcdeb7d06ceaea2fc28a3036 |
parent 233631 | 6725719f1d36196c90c817ccc8306280502aff46 |
child 233633 | 4e00f10f5a2d37316a1083ff8854ab6d70de1664 |
push id | 56897 |
push user | dholbert@mozilla.com |
push date | Fri, 13 Mar 2015 20:16:43 +0000 |
treeherder | mozilla-inbound@4e00f10f5a2d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | dbaron |
bugs | 1132743 |
milestone | 39.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/caps/nsIPrincipal.idl +++ b/caps/nsIPrincipal.idl @@ -15,17 +15,17 @@ struct JSPrincipals; interface nsIURI; interface nsIContentSecurityPolicy; [ptr] native JSContext(JSContext); [ptr] native JSPrincipals(JSPrincipals); [ptr] native PrincipalArray(nsTArray<nsCOMPtr<nsIPrincipal> >); -[scriptable, builtinclass, uuid(204555e7-04ad-4cc8-9f0e-840615cc43e8)] +[scriptable, builtinclass, uuid(264fe8ca-c382-11e4-95a6-782bcbaebb28)] interface nsIPrincipal : nsISerializable { /** * Returns whether the other principal is equivalent to this principal. * Principals are considered equal if they are the same principal, or * they have the same origin. */ boolean equals(in nsIPrincipal other); @@ -225,16 +225,25 @@ interface nsIPrincipal : nsISerializable */ [infallible] readonly attribute boolean unknownAppId; /** * Returns true iff this principal is a null principal (corresponding to an * unknown, hence assumed minimally privileged, security context). */ [infallible] readonly attribute boolean isNullPrincipal; + + /** + * Returns true if this principal's origin is recognized as being on the + * whitelist of sites that can use the CSS Unprefixing Service. + * + * (This interface provides a trivial implementation, just returning false; + * subclasses can implement something more complex as-needed.) + */ + [noscript,notxpcom,nostdcall] bool IsOnCSSUnprefixingWhitelist(); }; /** * If nsSystemPrincipal is too risky to use, but we want a principal to access * more than one origin, nsExpandedPrincipals letting us define an array of * principals it subsumes. So script with an nsExpandedPrincipals will gain * same origin access when at least one of its principals it contains gained * sameorigin acccess. An nsExpandedPrincipal will be subsumed by the system
--- a/caps/nsNullPrincipal.cpp +++ b/caps/nsNullPrincipal.cpp @@ -313,16 +313,22 @@ nsNullPrincipal::GetIsNullPrincipal(bool NS_IMETHODIMP nsNullPrincipal::GetBaseDomain(nsACString& aBaseDomain) { // For a null principal, we use our unique uuid as the base domain. return mURI->GetPath(aBaseDomain); } +bool +nsNullPrincipal::IsOnCSSUnprefixingWhitelist() +{ + return false; +} + /** * nsISerializable implementation */ NS_IMETHODIMP nsNullPrincipal::Read(nsIObjectInputStream* aStream) { // Note - nsNullPrincipal use NS_GENERIC_FACTORY_CONSTRUCTOR_INIT, which means // that the Init() method has already been invoked by the time we deserialize.
--- a/caps/nsPrincipal.cpp +++ b/caps/nsPrincipal.cpp @@ -9,33 +9,37 @@ #include "mozIThirdPartyUtil.h" #include "nscore.h" #include "nsScriptSecurityManager.h" #include "nsString.h" #include "nsReadableUtils.h" #include "pratom.h" #include "nsIURI.h" #include "nsJSPrincipals.h" +#include "nsIEffectiveTLDService.h" #include "nsIObjectInputStream.h" #include "nsIObjectOutputStream.h" #include "nsIClassInfoImpl.h" #include "nsIProtocolHandler.h" #include "nsError.h" #include "nsIContentSecurityPolicy.h" +#include "nsNetCID.h" #include "jswrapper.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/Preferences.h" #include "mozilla/HashFunctions.h" #include "nsIAppsService.h" #include "mozIApplication.h" using namespace mozilla; +static bool gIsWhitelistingTestDomains = false; +// XXXdholbert Add to InitializeStatics(): static bool gCodeBasePrincipalSupport = false; static bool gIsObservingCodeBasePrincipalSupport = false; static bool URIIsImmutable(nsIURI* aURI) { nsCOMPtr<nsIMutable> mutableObj(do_QueryInterface(aURI)); bool isMutable; return @@ -121,16 +125,25 @@ NS_IMPL_QUERY_INTERFACE_CI(nsPrincipal, nsIPrincipal, nsISerializable) NS_IMPL_CI_INTERFACE_GETTER(nsPrincipal, nsIPrincipal, nsISerializable) NS_IMPL_ADDREF_INHERITED(nsPrincipal, nsBasePrincipal) NS_IMPL_RELEASE_INHERITED(nsPrincipal, nsBasePrincipal) +// Called at startup: +/* static */ void +nsPrincipal::InitializeStatics() +{ + Preferences::AddBoolVarCache( + &gIsWhitelistingTestDomains, + "layout.css.unprefixing-service.include-test-domains"); +} + nsPrincipal::nsPrincipal() : mAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID) , mInMozBrowser(false) , mCodebaseImmutable(false) , mDomainImmutable(false) , mInitialized(false) { } @@ -603,16 +616,155 @@ nsPrincipal::GetAppStatus() { if (mAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) { NS_WARNING("Asking for app status on a principal with an unknown app id"); return nsIPrincipal::APP_STATUS_NOT_INSTALLED; } return nsScriptSecurityManager::AppStatusForPrincipal(this); } +// Helper-function to indicate whether the CSS Unprefixing Service +// whitelist should include dummy domains that are only intended for +// use in testing. (Controlled by a pref.) +static inline bool +IsWhitelistingTestDomains() +{ + return gIsWhitelistingTestDomains; +} + +// Checks if the given URI's host is on our "full domain" whitelist +// (i.e. if it's an exact match against a domain that needs unprefixing) +static bool +IsOnFullDomainWhitelist(nsIURI* aURI) +{ + nsAutoCString hostStr; + nsresult rv = aURI->GetHost(hostStr); + NS_ENSURE_SUCCESS(rv, false); + + // NOTE: This static whitelist is expected to be short. If that changes, + // we should consider a different representation; e.g. hash-set, prefix tree. + static const nsLiteralCString sFullDomainsOnWhitelist[] = { + // 0th entry only active when testing: + NS_LITERAL_CSTRING("test1.example.org"), + NS_LITERAL_CSTRING("map.baidu.com"), + NS_LITERAL_CSTRING("music.baidu.com"), + NS_LITERAL_CSTRING("3g.163.com"), + NS_LITERAL_CSTRING("3glogo.gtimg.com"), // for 3g.163.com + NS_LITERAL_CSTRING("info.3g.qq.com"), // for 3g.qq.com + NS_LITERAL_CSTRING("3gimg.qq.com"), // for 3g.qq.com + NS_LITERAL_CSTRING("img.m.baidu.com"), // for [shucheng|ks].baidu.com + NS_LITERAL_CSTRING("m.mogujie.com"), + NS_LITERAL_CSTRING("touch.qunar.com"), + }; + static const size_t sNumFullDomainsOnWhitelist = + MOZ_ARRAY_LENGTH(sFullDomainsOnWhitelist); + + // Skip 0th (dummy) entry in whitelist, unless a pref is enabled. + const size_t firstWhitelistIdx = IsWhitelistingTestDomains() ? 0 : 1; + + for (size_t i = firstWhitelistIdx; i < sNumFullDomainsOnWhitelist; ++i) { + if (hostStr == sFullDomainsOnWhitelist[i]) { + return true; + } + } + return false; +} + +// Checks if the given URI's host is on our "base domain" whitelist +// (i.e. if it's a subdomain of some host that we've whitelisted as needing +// unprefixing for all its subdomains) +static bool +IsOnBaseDomainWhitelist(nsIURI* aURI) +{ + static const nsLiteralCString sBaseDomainsOnWhitelist[] = { + // 0th entry only active when testing: + NS_LITERAL_CSTRING("test2.example.org"), + NS_LITERAL_CSTRING("tbcdn.cn"), // for m.taobao.com + NS_LITERAL_CSTRING("dpfile.com"), // for m.dianping.com + NS_LITERAL_CSTRING("hao123img.com"), // for hao123.com + }; + static const size_t sNumBaseDomainsOnWhitelist = + MOZ_ARRAY_LENGTH(sBaseDomainsOnWhitelist); + + nsCOMPtr<nsIEffectiveTLDService> tldService = + do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); + + if (tldService) { + // Skip 0th test-entry in whitelist, unless the testing pref is enabled. + const size_t firstWhitelistIdx = IsWhitelistingTestDomains() ? 0 : 1; + + // Right now, the test base-domain "test2.example.org" is the only entry in + // its whitelist with a nonzero "depth". So we'll only bother going beyond + // 0 depth (to 1) if that entry is enabled. (No point in slowing down the + // normal codepath, for the benefit of a disabled test domain.) If we add a + // "real" base-domain with a depth of >= 1 to our whitelist, we can get rid + // of this conditional & just make this a static variable. + const uint32_t maxSubdomainDepth = IsWhitelistingTestDomains() ? 1 : 0; + + for (uint32_t subdomainDepth = 0; + subdomainDepth <= maxSubdomainDepth; ++subdomainDepth) { + + // Get the base domain (to depth |subdomainDepth|) from passed-in URI: + nsAutoCString baseDomainStr; + nsresult rv = tldService->GetBaseDomain(aURI, subdomainDepth, + baseDomainStr); + if (NS_FAILED(rv)) { + // aURI doesn't have |subdomainDepth| levels of subdomains. If we got + // here without a match yet, then aURI is not on our whitelist. + return false; + } + + // Compare the base domain against each entry in our whitelist: + for (size_t i = firstWhitelistIdx; i < sNumBaseDomainsOnWhitelist; ++i) { + if (baseDomainStr == sBaseDomainsOnWhitelist[i]) { + return true; + } + } + } + } + + return false; +} + +// The actual (non-cached) implementation of IsOnCSSUnprefixingWhitelist(): +static bool +IsOnCSSUnprefixingWhitelistImpl(nsIURI* aURI) +{ + // Check scheme, so we can drop any non-HTTP/HTTPS URIs right away + nsAutoCString schemeStr; + nsresult rv = aURI->GetScheme(schemeStr); + NS_ENSURE_SUCCESS(rv, false); + + // Only proceed if scheme is "http" or "https" + if (!(StringBeginsWith(schemeStr, NS_LITERAL_CSTRING("http")) && + (schemeStr.Length() == 4 || + (schemeStr.Length() == 5 && schemeStr[4] == 's')))) { + return false; + } + + return (IsOnFullDomainWhitelist(aURI) || + IsOnBaseDomainWhitelist(aURI)); +} + + +bool +nsPrincipal::IsOnCSSUnprefixingWhitelist() +{ + if (mIsOnCSSUnprefixingWhitelist.isNothing()) { + // Value not cached -- perform our lazy whitelist-check. + // (NOTE: If our URI is mutable, we just assume it's not on the whitelist, + // since our caching strategy won't work. This isn't expected to be common.) + mIsOnCSSUnprefixingWhitelist.emplace( + mCodebaseImmutable && + IsOnCSSUnprefixingWhitelistImpl(mCodebase)); + } + + return *mIsOnCSSUnprefixingWhitelist; +} + /************************************************************************************************************************/ static const char EXPANDED_PRINCIPAL_SPEC[] = "[Expanded Principal]"; NS_IMPL_CLASSINFO(nsExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY, NS_EXPANDEDPRINCIPAL_CID) NS_IMPL_QUERY_INTERFACE_CI(nsExpandedPrincipal, nsIPrincipal, @@ -818,16 +970,25 @@ nsExpandedPrincipal::GetIsNullPrincipal( } NS_IMETHODIMP nsExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain) { return NS_ERROR_NOT_AVAILABLE; } +bool +nsExpandedPrincipal::IsOnCSSUnprefixingWhitelist() +{ + // CSS Unprefixing Whitelist is a per-origin thing; doesn't really make sense + // for an expanded principal. (And probably shouldn't be needed.) + return false; +} + + void nsExpandedPrincipal::GetScriptLocation(nsACString& aStr) { // Is that a good idea to list it's principals? aStr.Assign(EXPANDED_PRINCIPAL_SPEC); } #ifdef DEBUG
--- a/caps/nsPrincipal.h +++ b/caps/nsPrincipal.h @@ -61,16 +61,17 @@ public: NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) MOZ_OVERRIDE; NS_IMETHOD GetJarPrefix(nsACString& aJarPrefix) MOZ_OVERRIDE; NS_IMETHOD GetAppStatus(uint16_t* aAppStatus) MOZ_OVERRIDE; NS_IMETHOD GetAppId(uint32_t* aAppStatus) MOZ_OVERRIDE; NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement) MOZ_OVERRIDE; NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId) MOZ_OVERRIDE; NS_IMETHOD GetIsNullPrincipal(bool* aIsNullPrincipal) MOZ_OVERRIDE; NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) MOZ_OVERRIDE; + virtual bool IsOnCSSUnprefixingWhitelist() MOZ_OVERRIDE; #ifdef DEBUG virtual void dumpImpl() MOZ_OVERRIDE; #endif nsPrincipal(); // Init() must be called before the principal is in a usable state. nsresult Init(nsIURI* aCodebase, @@ -97,24 +98,30 @@ public: } /** * Computes the puny-encoded origin of aURI. */ static nsresult GetOriginForURI(nsIURI* aURI, char **aOrigin); + /** + * Called at startup to setup static data, e.g. about:config pref-observers. + */ + static void InitializeStatics(); + nsCOMPtr<nsIURI> mDomain; nsCOMPtr<nsIURI> mCodebase; uint32_t mAppId; bool mInMozBrowser; // If mCodebaseImmutable is true, mCodebase is non-null and immutable bool mCodebaseImmutable; bool mDomainImmutable; bool mInitialized; + mozilla::Maybe<bool> mIsOnCSSUnprefixingWhitelist; // Lazily-computed protected: virtual ~nsPrincipal(); /** * Returns the app status of the principal based on mAppId and mInMozBrowser. */ uint16_t GetAppStatus(); @@ -144,16 +151,17 @@ public: NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) MOZ_OVERRIDE; NS_IMETHOD GetJarPrefix(nsACString& aJarPrefix) MOZ_OVERRIDE; NS_IMETHOD GetAppStatus(uint16_t* aAppStatus) MOZ_OVERRIDE; NS_IMETHOD GetAppId(uint32_t* aAppStatus) MOZ_OVERRIDE; NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement) MOZ_OVERRIDE; NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId) MOZ_OVERRIDE; NS_IMETHOD GetIsNullPrincipal(bool* aIsNullPrincipal) MOZ_OVERRIDE; NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) MOZ_OVERRIDE; + virtual bool IsOnCSSUnprefixingWhitelist() MOZ_OVERRIDE; #ifdef DEBUG virtual void dumpImpl() MOZ_OVERRIDE; #endif virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE; private: nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;
--- a/caps/nsSystemPrincipal.cpp +++ b/caps/nsSystemPrincipal.cpp @@ -196,16 +196,23 @@ nsSystemPrincipal::GetIsNullPrincipal(bo NS_IMETHODIMP nsSystemPrincipal::GetBaseDomain(nsACString& aBaseDomain) { // No base domain for chrome. return NS_OK; } +bool +nsSystemPrincipal::IsOnCSSUnprefixingWhitelist() +{ + // chrome stylesheets should not be fed to the CSS Unprefixing Service. + return false; +} + ////////////////////////////////////////// // Methods implementing nsISerializable // ////////////////////////////////////////// NS_IMETHODIMP nsSystemPrincipal::Read(nsIObjectInputStream* aStream) { // no-op: CID is sufficient to identify the mSystemPrincipal singleton
--- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -25,16 +25,17 @@ #include "nsDOMClassInfo.h" #include "nsEditorEventListener.h" #include "mozilla/EventListenerManager.h" #include "nsFrame.h" #include "nsGlobalWindow.h" #include "nsGkAtoms.h" #include "nsImageFrame.h" #include "nsLayoutStylesheetCache.h" +#include "nsPrincipal.h" #include "nsRange.h" #include "nsRegion.h" #include "nsRepeatService.h" #include "nsFloatManager.h" #include "nsSprocketLayout.h" #include "nsStackLayout.h" #include "nsStyleSet.h" #include "nsTextControlFrame.h" @@ -265,16 +266,17 @@ nsLayoutStatics::Initialize() nsContentSink::InitializeStatics(); nsHtml5Module::InitializeStatics(); mozilla::dom::FallbackEncoding::Initialize(); nsLayoutUtils::Initialize(); nsIPresShell::InitializeStatics(); TouchManager::InitializeStatics(); nsRefreshDriver::InitializeStatics(); + nsPrincipal::InitializeStatics(); nsCORSListenerProxy::Startup(); NS_SealStaticAtomTable(); nsWindowMemoryReporter::Init(); SVGElementFactory::Init();
--- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -6634,21 +6634,22 @@ CSSParserImpl::LookupKeywordPrefixAware( return keyword; } bool CSSParserImpl::ShouldUseUnprefixingService() { if (!sUnprefixingServiceEnabled) { - return false; - } - - // XXXdholbert Bug 1132743: Check if stylesheet URI is on fixlist here. - return true; + // Unprefixing is globally disabled. + return false; + } + + // Unprefixing enabled; see if our principal is whitelisted for unprefixing. + return mSheetPrincipal && mSheetPrincipal->IsOnCSSUnprefixingWhitelist(); } bool CSSParserImpl::ParsePropertyWithUnprefixingService( const nsAString& aPropertyName, css::Declaration* aDeclaration, uint32_t aFlags, bool aMustCallValueAppended,
--- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -219,16 +219,19 @@ skip-if = buildapp == 'b2g' || toolkit = [test_transitions_dynamic_changes.html] [test_transitions_bug537151.html] [test_unclosed_parentheses.html] [test_units_angle.html] [test_units_frequency.html] [test_units_length.html] [test_units_time.html] [test_unprefixing_service.html] +support-files = unprefixing_service_iframe.html unprefixing_service_utils.js +[test_unprefixing_service_prefs.html] +support-files = unprefixing_service_iframe.html unprefixing_service_utils.js [test_value_cloning.html] skip-if = (toolkit == 'gonk' && debug) || toolkit == 'android' #bug 775227 #debug-only failure; timed out [test_value_computation.html] skip-if = (toolkit == 'gonk' && debug) || toolkit == 'android' #debug-only failure [test_value_storage.html] skip-if = (toolkit == 'gonk' && debug) #debug-only failure [test_variable_serialization_computed.html] [test_variable_serialization_specified.html]
--- a/layout/style/test/test_unprefixing_service.html +++ b/layout/style/test/test_unprefixing_service.html @@ -2,239 +2,87 @@ <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1107378 --> <head> <meta charset="utf-8"> <title>Test for Bug 1107378</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <script type="text/javascript" src="property_database.js"></script> + <script type="application/javascript;version=1.7" src="unprefixing_service_utils.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1107378">Mozilla Bug 1107378</a> <div id="display"> - <div id="content"> - </div> + <iframe id="testIframe"></iframe> </div> <pre id="test"> <script type="application/javascript;version=1.7"> "use strict"; - -/** Test for the CSS Unprefixing Service (Bug 1107378) **/ - -function getComputedStyleWrapper(elem, prop) -{ - return window.getComputedStyle(elem, null).getPropertyValue(prop); -} - -const gTestcases = [ - { decl: "-webkit-box-flex:5", - targetPropName: "flex-grow", - targetPropVal: "5" }, - - /* If author happens to specify modern flexbox style after prefixed style, - make sure the modern stuff is preserved. */ - { decl: "-webkit-box-flex:4;flex-grow:6", - targetPropName: "flex-grow", - targetPropVal: "6" }, - - /* Tests for handling !important: */ - { decl: "-webkit-box-flex:3!important;", - targetPropName: "flex-grow", - targetPropVal: "3" }, - { decl: "-webkit-box-flex:2!important;flex-grow:1", - targetPropName: "flex-grow", - targetPropVal: "2" }, - - { decl: "-webkit-box-flex:1!important bogusText;", - targetPropName: "flex-grow" - /* invalid syntax --> no target prop-val. */ - }, - - // Make sure we handle weird capitalization in property & value, too: - { decl: "-WEBKIT-BoX-aLign: baSELine", - targetPropName: "align-items", - targetPropVal: "baseline" }, - - { decl: "display:-webkit-box", - targetPropName: "display", - targetPropVal: "flex" }, - - { decl: "display:-webkit-box; display:-moz-box;", - targetPropName: "display", - targetPropVal: "flex" }, - - { decl: "display:-webkit-foobar; display:-moz-box;", - targetPropName: "display", - targetPropVal: "-moz-box" }, - - // -webkit-box-align: baseline | center | end | start | stretch - // ...maps to: - // align-items: baseline | center | flex-end | flex-start | stretch - { decl: "-webkit-box-align: baseline", - targetPropName: "align-items", - targetPropVal: "baseline" }, - { decl: "-webkit-box-align: center", - targetPropName: "align-items", - targetPropVal: "center" }, - { decl: "-webkit-box-align: end", - targetPropName: "align-items", - targetPropVal: "flex-end" }, - { decl: "-webkit-box-align: start", - targetPropName: "align-items", - targetPropVal: "flex-start" }, - { decl: "-webkit-box-align: stretch", - targetPropName: "align-items", - targetPropVal: "stretch" }, - - // -webkit-box-direction is not supported, because it's unused & would be - // complicated to support. See note in CSSUnprefixingService.js for more. +SimpleTest.waitForExplicitFinish(); - // -webkit-box-ordinal-group: <number> maps directly to "order". - { decl: "-webkit-box-ordinal-group: 2", - targetPropName: "order", - targetPropVal: "2" }, - { decl: "-webkit-box-ordinal-group: 6000", - targetPropName: "order", - targetPropVal: "6000" }, - - // -webkit-box-orient: horizontal | inline-axis | vertical | block-axis - // ...maps to: - // flex-direction: row | row | column | column - { decl: "-webkit-box-orient: horizontal", - targetPropName: "flex-direction", - targetPropVal: "row" }, - { decl: "-webkit-box-orient: inline-axis", - targetPropName: "flex-direction", - targetPropVal: "row" }, - { decl: "-webkit-box-orient: vertical", - targetPropName: "flex-direction", - targetPropVal: "column" }, - { decl: "-webkit-box-orient: block-axis", - targetPropName: "flex-direction", - targetPropVal: "column" }, - - // -webkit-box-pack: start | center | end | justify - // ... maps to: - // justify-content: flex-start | center | flex-end | space-between - { decl: "-webkit-box-pack: start", - targetPropName: "justify-content", - targetPropVal: "flex-start" }, - { decl: "-webkit-box-pack: center", - targetPropName: "justify-content", - targetPropVal: "center" }, - { decl: "-webkit-box-pack: end", - targetPropName: "justify-content", - targetPropVal: "flex-end" }, - { decl: "-webkit-box-pack: justify", - targetPropName: "justify-content", - targetPropVal: "space-between" }, +/** + * This test checks that unprefixing is enabled for whitelisted domains, and + * that it's disabled for non-whitelisted domains. + * + * We do this using an iframe, in which we load a test file at a test domain, + * and we have the iframe report back to us (using postMessage) about + * whether unprefixing is working. + * + * High-level overview of the process here: + * - First, we tweak prefs to enable unprefixing & enable the test-only + * entries in our unprefixing whitelist. + * - The rest of this test is driven by the "startNextTest()" method. + * This method pops a hostname to test and loads a URL from that host + * in the iframe. + * - We then listen for test-results from the iframe, using the postMessage + * handler in unprefixing_service_utils.js. + * - When the iframe indicates that it's done, we call "startNextTest()" + * again to pop the next host & load *that* in the iframe. + * - When nothing remains to be popped, we're done. + */ - // -webkit-transform: <transform> maps directly to "transform" - { decl: "-webkit-transform: matrix(1, 2, 3, 4, 5, 6)", - targetPropName: "transform", - targetPropVal: "matrix(1, 2, 3, 4, 5, 6)" }, - - // -webkit-transition: <property> maps directly to "transition" - { decl: "-webkit-transition: width 1s linear 2s", - targetPropName: "transition", - targetPropVal: "width 1s linear 2s" }, - - // -webkit-transition **with** -webkit-prefixed property in value. - { decl: "-webkit-transition: -webkit-transform 1s linear 2s", - targetPropName: "transition", - targetPropVal: "transform 1s linear 2s" }, - // (Re-test to check that it sets the "transition-property" subproperty.) - { decl: "-webkit-transition: -webkit-transform 1s linear 2s", - targetPropName: "transition-property", - targetPropVal: "transform" }, +const IFRAME_TESTFILE = "unprefixing_service_iframe.html"; - // Same as previous test, except with "-webkit-transform" in the - // middle of the value instead of at the beginning (still valid): - { decl: "-webkit-transition: 1s -webkit-transform linear 2s", - targetPropName: "transition", - targetPropVal: "transform 1s linear 2s" }, - { decl: "-webkit-transition: 1s -webkit-transform linear 2s", - targetPropName: "transition-property", - targetPropVal: "transform" }, -]; - -// The main test function. -// aFlexboxTestcase is an entry from the list in flexbox_layout_testcases.js -function runOneTest(aTestcase) +// This function gets invoked when our iframe finishes a given round of testing. +function startNextTest() { - let elem = document.getElementById("content"); - - let expectedValueInDOMStyle; - let expectedValueInComputedStyle; - if (typeof(aTestcase.targetPropVal) == 'undefined') { - expectedValueInDOMStyle = ''; - expectedValueInComputedStyle = // initial computed style: - getComputedStyleWrapper(elem, aTestcase.targetPropName); - } else { - expectedValueInDOMStyle = aTestcase.targetPropVal; - expectedValueInComputedStyle = aTestcase.targetPropVal; + // Test the next whitelisted host, if any remain. + if (gWhitelistedHosts.length > 0) { + let host = gWhitelistedHosts.pop(); + info("Verifying that CSS Unprefixing Service is active, " + + "at whitelisted test-host '" + host + "'"); + testHost(host, true); + return; } - elem.setAttribute("style", aTestcase.decl); - - // Check specified style for fixup: - is(elem.style[aTestcase.targetPropName], expectedValueInDOMStyle, - "Checking if unprefixing service produced expected result " + - "in elem.style['" + aTestcase.targetPropName + "'] " + - "when given decl '" + aTestcase.decl + "'"); - - // Check computed style for fixup: - // (only for longhand properties; shorthands aren't in computed style) - if (gCSSProperties[aTestcase.targetPropName].type == CSS_TYPE_LONGHAND) { - let computedValue = getComputedStyleWrapper(elem, aTestcase.targetPropName); - is(computedValue, expectedValueInComputedStyle, - "Checking if unprefixing service produced expected result " + - "in computed value of property '" + aTestcase.targetPropName + "' " + - "when given decl '" + aTestcase.decl + "'"); + // Test the next not-whitelisted host, if any remain. + if (gNotWhitelistedHosts.length > 0) { + let host = gNotWhitelistedHosts.pop(); + info("Verifying that CSS Unprefixing Service is inactive, " + + "at non-whitelisted test-host '" + host + "'"); + testHost(host, false); + return; } - elem.setAttribute("style", ""); -} - -function testWithUnprefixingDisabled() -{ - // Sanity-check that -webkit-prefixed properties are rejected, when - // pref is disabled: - let elem = document.getElementById("content"); - let initialFlexGrow = getComputedStyleWrapper(elem, "flex-grow"); - elem.setAttribute("style", "-webkit-box-flex:5"); - is(getComputedStyleWrapper(elem, "flex-grow"), initialFlexGrow, - "-webkit-box-flex shouldn't affect 'flex-grow' " + - "when unprefixing pref is disabled"); - - let initialDisplay = getComputedStyleWrapper(elem, "display"); - elem.setAttribute("style", "display:-webkit-box"); - is(getComputedStyleWrapper(elem, "display"), initialDisplay, - "-webkit-box-flex shouldn't affect 'display' " + - "when unprefixing pref is disabled"); -} - -function testWithUnprefixingEnabled() -{ - gTestcases.forEach(runOneTest); + // Both arrays empty --> we're done. SimpleTest.finish(); } -SimpleTest.waitForExplicitFinish(); - -// First, test with unprefixing disabled (by default for now): -testWithUnprefixingDisabled(); +function begin() +{ + // Before we start loading things in iframes, set up postMessage handler. + registerPostMessageListener(startNextTest); -// ...and then test with it enabled. -// XXXdholbert in bug 1132743, we'll be restricting unprefixing to only happen -// on a "fixlist" of domains. We'll need to run this test from a predetermined -// fake mochitest-domain, and include that domain in the "fixlist". -SpecialPowers.pushPrefEnv( - { set: [["layout.css.unprefixing-service.enabled", true]] }, - testWithUnprefixingEnabled); + // Turn on prefs & start the first test! + SpecialPowers.pushPrefEnv( + { set: [[PREF_UNPREFIXING_SERVICE, true], + [PREF_INCLUDE_TEST_DOMAINS, true]]}, + startNextTest); +} + +begin(); </script> </pre> </body> </html>
new file mode 100644 --- /dev/null +++ b/layout/style/test/test_unprefixing_service_prefs.html @@ -0,0 +1,128 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1132743 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1132743</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript;version=1.7" src="unprefixing_service_utils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1132743">Mozilla Bug 1132743</a> +<div id="display"> + <iframe id="testIframe"></iframe> +</div> +<pre id="test"> +<script type="application/javascript;version=1.7"> +"use strict"; +SimpleTest.waitForExplicitFinish(); + +/** + * This test checks that our CSS unprefixing prefs are effective. + * + * We do this using an iframe, in which we load a test file at a test domain + * (whose whitelist-status depends on a pref), and we have the iframe report + * back to us (using postMessage) about whether unprefixing is working. + * + * High-level overview of the process here (starting with begin()): + * - First, we ensure that the pref... + * "layout.css.unprefixing-service.include-test-domains" + * ...is *unset* by default. (No point exposing it in about:config). + * - Then, we test that (as a result of this pref being unset) the + * unprefixing service is *inactive* at our test-domain, by default. + * - Then, via a series of calls to "startNextTest()"/"testHost()", we re-test + * the same test-domain with a variety of pref configurations, to ensure + * that unprefixing only happens there when we've preffed on the service + * *and* we've enabled the testing entries in the whiteslist. + */ + +const IFRAME_TESTFILE = "unprefixing_service_iframe.html"; + +// Just test the first host in our known-whitelisted-hosts list. +const WHITELISTED_TEST_HOST = gWhitelistedHosts[0]; + +// Configurations of our prefs to test. +// Each is a 3-entry array, whose entries mean: +// (1) should we enable the CSS Unprefixing Service pref? +// (2) should we enable the "include test domains in whitelist" pref? +// (3) in this pref-configuration, should we expect to see unprefixing active +// on our whitelisted test-domain? +// +// As you can see, the only configuration which should produce unprefixing +// activity is when *both* prefs are enabled. +let gTestConfigs = [ + [false, false, false], + [false, true, false], + [true, false, false], + [true, true, true], +]; + +// Test that a particular configuration of prefs will activate or inactivate +// the CSS unprefixing service, for styles loaded from WHITELISTED_TEST_HOST. +// aTestConfig is described above, in documentation for gTestConfigs. +function testConfig(aTestConfig) +{ + if (aTestConfig.length != 3) { + ok(false, "bug in test; need 3 entries. see gTestConfigs documentation"); + } + + info("Verifying that CSS Unprefixing Service is " + + (aTestConfig[2] ? "active" : "inactive") + + " at test host, with prefs: " + + PREF_UNPREFIXING_SERVICE + "=" + aTestConfig[0] + ", " + + PREF_INCLUDE_TEST_DOMAINS + "=" + aTestConfig[1]); + + SpecialPowers.pushPrefEnv( + { set: + [[PREF_UNPREFIXING_SERVICE, aTestConfig[0]], + [PREF_INCLUDE_TEST_DOMAINS, aTestConfig[1]]] + }, + function() { + testHost(WHITELISTED_TEST_HOST, aTestConfig[2]); + }); +} + +// This function gets invoked when our iframe finishes a given round of testing. +function startNextTest() +{ + if (gTestConfigs.length > 0) { + // Grab the next test-config, and kick off a test for it. + testConfig(gTestConfigs.pop()); + return; + } + + // Array empty --> we're done. + SimpleTest.finish(); +} + +function begin() +{ + // First, check that PREF_INCLUDE_TEST_DOMAINS is unset: + try { + let val = SpecialPowers.getBoolPref(PREF_INCLUDE_TEST_DOMAINS); + ok(false, "The test pref '" + PREF_INCLUDE_TEST_DOMAINS + + "' should be unspecified by default"); + } catch(e) { /* Good, we threw; pref is unset. */ } + + // Before we start loading things in iframes, set up postMessage handler. + registerPostMessageListener(startNextTest); + + // To kick things off, we don't set any prefs; we just test the default state + // (which should have the "include test domains" pref implicitly disabled, & + // hence unprefixing should end up being disabled in our iframe). Subsequent + // tests are kicked off via postMessage-triggered calls to startNextTest(), + // which will tweak prefs and re-test. + info("Verifying that CSS Unprefixing Service is inactive at test host, " + + "with default pref configuration"); + testHost(WHITELISTED_TEST_HOST, false); +} + +begin(); + +</script> +</pre> +</body> +</html>
copy from layout/style/test/test_unprefixing_service.html copy to layout/style/test/unprefixing_service_iframe.html --- a/layout/style/test/test_unprefixing_service.html +++ b/layout/style/test/unprefixing_service_iframe.html @@ -1,37 +1,25 @@ <!DOCTYPE HTML> <html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1107378 ---> <head> <meta charset="utf-8"> - <title>Test for Bug 1107378</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <title>Helper file for testing CSS Unprefixing Service</title> <script type="text/javascript" src="property_database.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1107378">Mozilla Bug 1107378</a> -<div id="display"> - <div id="content"> - </div> -</div> -<pre id="test"> +<div id="content"></div> + <script type="application/javascript;version=1.7"> "use strict"; -/** Test for the CSS Unprefixing Service (Bug 1107378) **/ +/** Helper file for testing the CSS Unprefixing Service **/ -function getComputedStyleWrapper(elem, prop) -{ - return window.getComputedStyle(elem, null).getPropertyValue(prop); -} - +// Testcases for CSS Unprefixing service, with the (string) declaration that +// should be unprefixed, and the expected resulting property name & value: const gTestcases = [ { decl: "-webkit-box-flex:5", targetPropName: "flex-grow", targetPropVal: "5" }, /* If author happens to specify modern flexbox style after prefixed style, make sure the modern stuff is preserved. */ { decl: "-webkit-box-flex:4;flex-grow:6", @@ -154,18 +142,45 @@ const gTestcases = [ { decl: "-webkit-transition: 1s -webkit-transform linear 2s", targetPropName: "transition", targetPropVal: "transform 1s linear 2s" }, { decl: "-webkit-transition: 1s -webkit-transform linear 2s", targetPropName: "transition-property", targetPropVal: "transform" }, ]; -// The main test function. -// aFlexboxTestcase is an entry from the list in flexbox_layout_testcases.js +function getComputedStyleWrapper(elem, prop) +{ + return window.getComputedStyle(elem, null).getPropertyValue(prop); +} + +// Shims for "is()" and "ok()", which defer to parent window using postMessage: +function is(aActual, aExpected, aDesc) +{ + // Add URL to description: + aDesc += " (iframe url: '" + window.location + "')"; + + window.parent.postMessage({type: "is", + actual: aActual, + expected: aExpected, + desc: aDesc}, "*"); +} + +function ok(aCondition, aDesc) +{ + // Add URL to description: + aDesc += " (iframe url: '" + window.location + "')"; + + window.parent.postMessage({type: "ok", + condition: aCondition, + desc: aDesc}, "*"); +} + +// Main test function to use, to test a given unprefixed CSS property. +// The argument aTestcase should be an entry from gTestcases above. function runOneTest(aTestcase) { let elem = document.getElementById("content"); let expectedValueInDOMStyle; let expectedValueInComputedStyle; if (typeof(aTestcase.targetPropVal) == 'undefined') { expectedValueInDOMStyle = ''; @@ -175,66 +190,61 @@ function runOneTest(aTestcase) expectedValueInDOMStyle = aTestcase.targetPropVal; expectedValueInComputedStyle = aTestcase.targetPropVal; } elem.setAttribute("style", aTestcase.decl); // Check specified style for fixup: is(elem.style[aTestcase.targetPropName], expectedValueInDOMStyle, - "Checking if unprefixing service produced expected result " + + "Checking if CSS Unprefixing Service produced expected result " + "in elem.style['" + aTestcase.targetPropName + "'] " + "when given decl '" + aTestcase.decl + "'"); // Check computed style for fixup: // (only for longhand properties; shorthands aren't in computed style) if (gCSSProperties[aTestcase.targetPropName].type == CSS_TYPE_LONGHAND) { let computedValue = getComputedStyleWrapper(elem, aTestcase.targetPropName); is(computedValue, expectedValueInComputedStyle, - "Checking if unprefixing service produced expected result " + + "Checking if CSS Unprefixing Service produced expected result " + "in computed value of property '" + aTestcase.targetPropName + "' " + "when given decl '" + aTestcase.decl + "'"); } elem.setAttribute("style", ""); } -function testWithUnprefixingDisabled() +// Function used to quickly test that unprefixing is off: +function testUnprefixingDisabled() { - // Sanity-check that -webkit-prefixed properties are rejected, when - // pref is disabled: let elem = document.getElementById("content"); + let initialFlexGrow = getComputedStyleWrapper(elem, "flex-grow"); elem.setAttribute("style", "-webkit-box-flex:5"); is(getComputedStyleWrapper(elem, "flex-grow"), initialFlexGrow, - "-webkit-box-flex shouldn't affect 'flex-grow' " + - "when unprefixing pref is disabled"); + "'-webkit-box-flex' shouldn't affect computed 'flex-grow' " + + "when CSS Unprefixing Service is inactive"); let initialDisplay = getComputedStyleWrapper(elem, "display"); elem.setAttribute("style", "display:-webkit-box"); is(getComputedStyleWrapper(elem, "display"), initialDisplay, - "-webkit-box-flex shouldn't affect 'display' " + - "when unprefixing pref is disabled"); -} - -function testWithUnprefixingEnabled() -{ - gTestcases.forEach(runOneTest); - SimpleTest.finish(); + "'display:-webkit-box' shouldn't affect computed 'display' " + + "when CSS Unprefixing Service is inactive"); } -SimpleTest.waitForExplicitFinish(); - -// First, test with unprefixing disabled (by default for now): -testWithUnprefixingDisabled(); +function startTest() +{ + if (window.location.hash === "#expectEnabled") { + gTestcases.forEach(runOneTest); + } else if (window.location.hash === "#expectDisabled") { + testUnprefixingDisabled(); + } else { + ok(false, + "Need a recognized 'window.location.hash' to indicate expectation. " + + "Got: '" + window.location.hash + "'"); + } + window.parent.postMessage({type: "testComplete"}, "*"); +} -// ...and then test with it enabled. -// XXXdholbert in bug 1132743, we'll be restricting unprefixing to only happen -// on a "fixlist" of domains. We'll need to run this test from a predetermined -// fake mochitest-domain, and include that domain in the "fixlist". -SpecialPowers.pushPrefEnv( - { set: [["layout.css.unprefixing-service.enabled", true]] }, - testWithUnprefixingEnabled); - +startTest(); </script> -</pre> </body> </html>
new file mode 100644 --- /dev/null +++ b/layout/style/test/unprefixing_service_utils.js @@ -0,0 +1,87 @@ +/* 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/. */ + +// Shared data & functionality used in tests for CSS Unprefixing Service. + +// Whitelisted hosts: +// (per implementation of nsPrincipal::IsOnCSSUnprefixingWhitelist()) +let gWhitelistedHosts = [ + // test1.example.org is on the whitelist. + "test1.example.org", + // test2.example.org is on the "allow all subdomains" whitelist. + "test2.example.org", + "sub1.test2.example.org", + "sub2.test2.example.org" +]; + +// *NOT* whitelisted hosts: +let gNotWhitelistedHosts = [ + // Though test1.example.org is on the whitelist, its subdomains are not. + "sub1.test1.example.org", + // mochi.test is not on the whitelist. + "mochi.test:8888" +]; + +// Names of prefs: +const PREF_UNPREFIXING_SERVICE = + "layout.css.unprefixing-service.enabled"; +const PREF_INCLUDE_TEST_DOMAINS = + "layout.css.unprefixing-service.include-test-domains"; + +// Helper-function to make unique URLs in testHost(): +let gCounter = 0; +function getIncreasingCounter() { + return gCounter++; +} + +// This function tests a particular host in our iframe. +// @param aHost The host to be tested +// @param aExpectEnabled Should we expect unprefixing to be enabled for host? +function testHost(aHost, aExpectEnabled) { + // Build the URL: + let url = window.location.protocol; // "http:" or "https:" + url += "//"; + url += aHost; + + // Append the path-name, up to the actual filename (the final "/"): + const re = /(.*\/).*/; + url += window.location.pathname.replace(re, "$1"); + url += IFRAME_TESTFILE; + // In case this is the same URL as last time, we add "?N" for some unique N, + // to make each URL different, so that the iframe actually (re)loads: + url += "?" + getIncreasingCounter(); + // We give the URL a #suffix to indicate to the test whether it should expect + // that unprefixing is enabled or disabled: + url += (aExpectEnabled ? "#expectEnabled" : "#expectDisabled"); + + let iframe = document.getElementById("testIframe"); + iframe.contentWindow.location = url; + // The iframe will report its results back via postMessage. + // Our caller had better have set up a postMessage listener. +} + +// Register a postMessage() handler, to allow our cross-origin iframe to +// communicate back to the main page's mochitest functionality. +// The handler expects postMessage to be called with an object like: +// { type: ["is"|"ok"|"testComplete"], ... } +// The "is" and "ok" types will trigger the corresponding function to be +// called in the main page, with named arguments provided in the payload. +// The "testComplete" type will trigger the passed-in aTestCompleteCallback +// function to be invoked (e.g. to advance to the next testcase, or to finish +// the overall test, as-appropriate). +function registerPostMessageListener(aTestCompleteCallback) { + let receiveMessage = function(event) { + if (event.data.type === "is") { + is(event.data.actual, event.data.expected, event.data.desc); + } else if (event.data.type === "ok") { + ok(event.data.condition, event.data.desc); + } else if (event.data.type === "testComplete") { + aTestCompleteCallback(); + } else { + ok(false, "unrecognized data in postMessage call"); + } + }; + + window.addEventListener("message", receiveMessage, false); +}