--- 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);
+}