Bug 1132743: Only allow CSS Unprefixing Service to be activated for hosts on a small, hardcoded whitelist. r=dbaron f=bz

--HG--
rename : layout/style/test/test_unprefixing_service.html => layout/style/test/unprefixing_service_iframe.html
This commit is contained in:
Daniel Holbert 2015-03-13 13:15:09 -07:00
Родитель d38aa64d52
Коммит 908a699328
12 изменённых файлов: 718 добавлений и 208 удалений

Просмотреть файл

@ -20,7 +20,7 @@ interface nsIContentSecurityPolicy;
[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
{
/**
@ -230,6 +230,15 @@ interface nsIPrincipal : nsISerializable
* 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();
};
/**

Просмотреть файл

@ -318,6 +318,12 @@ nsNullPrincipal::GetBaseDomain(nsACString& aBaseDomain)
return mURI->GetPath(aBaseDomain);
}
bool
nsNullPrincipal::IsOnCSSUnprefixingWhitelist()
{
return false;
}
/**
* nsISerializable implementation
*/

Просмотреть файл

@ -14,12 +14,14 @@
#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"
@ -31,6 +33,8 @@
using namespace mozilla;
static bool gIsWhitelistingTestDomains = false;
// XXXdholbert Add to InitializeStatics():
static bool gCodeBasePrincipalSupport = false;
static bool gIsObservingCodeBasePrincipalSupport = false;
@ -126,6 +130,15 @@ NS_IMPL_CI_INTERFACE_GETTER(nsPrincipal,
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)
@ -608,6 +621,145 @@ nsPrincipal::GetAppStatus()
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]";
@ -823,6 +975,15 @@ 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)
{

Просмотреть файл

@ -66,6 +66,7 @@ public:
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
@ -102,6 +103,11 @@ public:
*/
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;
@ -110,6 +116,7 @@ public:
bool mCodebaseImmutable;
bool mDomainImmutable;
bool mInitialized;
mozilla::Maybe<bool> mIsOnCSSUnprefixingWhitelist; // Lazily-computed
protected:
virtual ~nsPrincipal();
@ -149,6 +156,7 @@ public:
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

Просмотреть файл

@ -201,6 +201,13 @@ nsSystemPrincipal::GetBaseDomain(nsACString& aBaseDomain)
return NS_OK;
}
bool
nsSystemPrincipal::IsOnCSSUnprefixingWhitelist()
{
// chrome stylesheets should not be fed to the CSS Unprefixing Service.
return false;
}
//////////////////////////////////////////
// Methods implementing nsISerializable //
//////////////////////////////////////////

Просмотреть файл

@ -30,6 +30,7 @@
#include "nsGkAtoms.h"
#include "nsImageFrame.h"
#include "nsLayoutStylesheetCache.h"
#include "nsPrincipal.h"
#include "nsRange.h"
#include "nsRegion.h"
#include "nsRepeatService.h"
@ -270,6 +271,7 @@ nsLayoutStatics::Initialize()
nsIPresShell::InitializeStatics();
TouchManager::InitializeStatics();
nsRefreshDriver::InitializeStatics();
nsPrincipal::InitializeStatics();
nsCORSListenerProxy::Startup();

Просмотреть файл

@ -6639,11 +6639,12 @@ bool
CSSParserImpl::ShouldUseUnprefixingService()
{
if (!sUnprefixingServiceEnabled) {
// Unprefixing is globally disabled.
return false;
}
// XXXdholbert Bug 1132743: Check if stylesheet URI is on fixlist here.
return true;
// Unprefixing enabled; see if our principal is whitelisted for unprefixing.
return mSheetPrincipal && mSheetPrincipal->IsOnCSSUnprefixingWhitelist();
}
bool

Просмотреть файл

@ -224,6 +224,9 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 775227 # b2g(times out,
[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]

Просмотреть файл

@ -7,232 +7,80 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1107378
<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";
SimpleTest.waitForExplicitFinish();
/** Test for the CSS Unprefixing Service (Bug 1107378) **/
/**
* 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.
*/
function getComputedStyleWrapper(elem, prop)
const IFRAME_TESTFILE = "unprefixing_service_iframe.html";
// This function gets invoked when our iframe finishes a given round of testing.
function startNextTest()
{
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.
// -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" },
// -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" },
// 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)
{
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();
function begin()
{
// Before we start loading things in iframes, set up postMessage handler.
registerPostMessageListener(startNextTest);
// First, test with unprefixing disabled (by default for now):
testWithUnprefixingDisabled();
// Turn on prefs & start the first test!
SpecialPowers.pushPrefEnv(
{ set: [[PREF_UNPREFIXING_SERVICE, true],
[PREF_INCLUDE_TEST_DOMAINS, true]]},
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);
begin();
</script>
</pre>

Просмотреть файл

@ -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>

Просмотреть файл

@ -0,0 +1,250 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Helper file for testing CSS Unprefixing Service</title>
<script type="text/javascript" src="property_database.js"></script>
</head>
<body>
<div id="content"></div>
<script type="application/javascript;version=1.7">
"use strict";
/** Helper file for testing the CSS Unprefixing Service **/
// 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",
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.
// -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" },
// -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" },
// 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" },
];
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 = '';
expectedValueInComputedStyle = // initial computed style:
getComputedStyleWrapper(elem, aTestcase.targetPropName);
} else {
expectedValueInDOMStyle = aTestcase.targetPropVal;
expectedValueInComputedStyle = aTestcase.targetPropVal;
}
elem.setAttribute("style", aTestcase.decl);
// Check specified style for fixup:
is(elem.style[aTestcase.targetPropName], expectedValueInDOMStyle,
"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 CSS Unprefixing Service produced expected result " +
"in computed value of property '" + aTestcase.targetPropName + "' " +
"when given decl '" + aTestcase.decl + "'");
}
elem.setAttribute("style", "");
}
// Function used to quickly test that unprefixing is off:
function testUnprefixingDisabled()
{
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 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,
"'display:-webkit-box' shouldn't affect computed 'display' " +
"when CSS Unprefixing Service is inactive");
}
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"}, "*");
}
startTest();
</script>
</body>
</html>

Просмотреть файл

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