Bug 1306887 - keep SourceMap response header on CSS style sheets; r?heycam, bz
When loading a style sheet, if the SourceMap (or legacy X-SourceMap)
response header was seen, record it and make it available to chrome
scripts.
MozReview-Commit-ID: 3wtUADzgrI3
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -10605,16 +10605,26 @@ nsContentUtils::GenerateTabId()
}
/* static */ bool
nsContentUtils::GetUserIsInteracting()
{
return UserInteractionObserver::sUserActive;
}
+/* static */ bool
+nsContentUtils::GetSourceMapURL(nsIHttpChannel* aChannel, nsACString& aResult)
+{
+ nsresult rv = aChannel->GetResponseHeader(NS_LITERAL_CSTRING("SourceMap"), aResult);
+ if (NS_FAILED(rv)) {
+ rv = aChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), aResult);
+ }
+ return NS_SUCCEEDED(rv);
+}
+
static const char* kUserInteractionInactive = "user-interaction-inactive";
static const char* kUserInteractionActive = "user-interaction-active";
void
nsContentUtils::UserInteractionObserver::Init()
{
// Listen for the observer messages from EventStateManager which are telling
// us whether or not the user is interacting.
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -3077,16 +3077,26 @@ public:
/**
* Checks if the passed-in name should override an existing name on the
* window. Values which should not override include: "", "_blank", "_top",
* "_parent" and "_self".
*/
static bool IsOverridingWindowName(const nsAString& aName);
+ /**
+ * If there is a SourceMap (higher precedence) or X-SourceMap (lower
+ * precedence) response header in |aChannel|, set |aResult| to the
+ * header's value and return true. Otherwise, return false.
+ *
+ * @param aChannel The HTTP channel
+ * @param aResult The string result.
+ */
+ static bool GetSourceMapURL(nsIHttpChannel* aChannel, nsACString& aResult);
+
private:
static bool InitializeEventTable();
static nsresult EnsureStringBundle(PropertiesFile aFile);
static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
nsIPrincipal* aPrincipal);
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -2876,21 +2876,17 @@ ScriptLoader::PrepareLoadedRequest(Scrip
if (httpChannel) {
bool requestSucceeded;
rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
if (NS_SUCCEEDED(rv) && !requestSucceeded) {
return NS_ERROR_NOT_AVAILABLE;
}
nsAutoCString sourceMapURL;
- rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("SourceMap"), sourceMapURL);
- if (NS_FAILED(rv)) {
- rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), sourceMapURL);
- }
- if (NS_SUCCEEDED(rv)) {
+ if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
aRequest->mHasSourceMapURL = true;
aRequest->mSourceMapURL = NS_ConvertUTF8toUTF16(sourceMapURL);
}
if (httpChannel->GetIsTrackingResource()) {
aRequest->SetIsTracking();
}
}
--- a/dom/webidl/StyleSheet.webidl
+++ b/dom/webidl/StyleSheet.webidl
@@ -19,9 +19,16 @@ interface StyleSheet {
[Pure]
readonly attribute StyleSheet? parentStyleSheet;
[Pure]
readonly attribute DOMString? title;
[Constant]
readonly attribute MediaList media;
[Pure]
attribute boolean disabled;
+ // If a SourceMap or X-SourceMap response header is seen, this is
+ // the value. If both are seen, SourceMap is preferred. If neither
+ // is seen, this will be an empty string. Because this relies on
+ // the HTTP response, it can change if checked before the response
+ // is available -- which is why it is not [Constant].
+ [ChromeOnly, Pure]
+ readonly attribute DOMString sourceMapURL;
};
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -886,16 +886,21 @@ SheetLoadData::OnStreamComplete(nsIUnich
if (httpChannel) {
bool requestSucceeded;
result = httpChannel->GetRequestSucceeded(&requestSucceeded);
if (NS_SUCCEEDED(result) && !requestSucceeded) {
LOG((" Load returned an error page"));
mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
return NS_OK;
}
+
+ nsAutoCString sourceMapURL;
+ if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
+ mSheet->SetSourceMapURL(NS_ConvertUTF8toUTF16(sourceMapURL));
+ }
}
nsAutoCString contentType;
if (channel) {
channel->GetContentType(contentType);
}
// In standards mode, a style sheet must have one of these MIME
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -254,16 +254,17 @@ StyleSheetInfo::StyleSheetInfo(StyleShee
, mBaseURI(aCopy.mBaseURI)
, mPrincipal(aCopy.mPrincipal)
, mCORSMode(aCopy.mCORSMode)
, mReferrerPolicy(aCopy.mReferrerPolicy)
, mIntegrity(aCopy.mIntegrity)
, mComplete(aCopy.mComplete)
, mFirstChild() // We don't rebuild the child because we're making a copy
// without children.
+ , mSourceMapURL(aCopy.mSourceMapURL)
#ifdef DEBUG
, mPrincipalSet(aCopy.mPrincipalSet)
#endif
{
AddSheet(aPrimarySheet);
}
StyleSheetInfo::~StyleSheetInfo()
@@ -501,16 +502,28 @@ StyleSheet::GetCssRules(nsIPrincipal& aS
ErrorResult& aRv)
{
if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
return nullptr;
}
FORWARD_INTERNAL(GetCssRulesInternal, ())
}
+void
+StyleSheet::GetSourceMapURL(nsAString& aSourceMapURL)
+{
+ aSourceMapURL = mInner->mSourceMapURL;
+}
+
+void
+StyleSheet::SetSourceMapURL(const nsAString& aSourceMapURL)
+{
+ mInner->mSourceMapURL = aSourceMapURL;
+}
+
css::Rule*
StyleSheet::GetDOMOwnerRule() const
{
return mOwnerRule;
}
uint32_t
StyleSheet::InsertRule(const nsAString& aRule, uint32_t aIndex,
--- a/layout/style/StyleSheet.h
+++ b/layout/style/StyleSheet.h
@@ -208,16 +208,18 @@ public:
// The XPCOM GetType is fine for WebIDL.
// The XPCOM GetHref is fine for WebIDL
// GetOwnerNode is defined above.
inline StyleSheet* GetParentStyleSheet() const;
// The XPCOM GetTitle is fine for WebIDL.
dom::MediaList* Media();
bool Disabled() const { return mDisabled; }
// The XPCOM SetDisabled is fine for WebIDL.
+ void GetSourceMapURL(nsAString& aTitle);
+ void SetSourceMapURL(const nsAString& aSourceMapURL);
// WebIDL CSSStyleSheet API
// Can't be inline because we can't include ImportRule here. And can't be
// called GetOwnerRule because that would be ambiguous with the ImportRule
// version.
css::Rule* GetDOMOwnerRule() const;
dom::CSSRuleList* GetCssRules(nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv);
--- a/layout/style/StyleSheetInfo.h
+++ b/layout/style/StyleSheetInfo.h
@@ -56,16 +56,21 @@ struct StyleSheetInfo
// Pointer to start of linked list of child sheets. This is all fundamentally
// broken, because each of the child sheets has a unique parent... We can
// only hope (and currently this is the case) that any time page JS can get
// its hands on a child sheet that means we've already ensured unique infos
// throughout its parent chain and things are good.
RefPtr<StyleSheet> mFirstChild;
AutoTArray<StyleSheet*, 8> mSheets;
+ // If a SourceMap or X-SourceMap response header is seen, this is
+ // the value. If both are seen, SourceMap is preferred. If neither
+ // is seen, this will be an empty string.
+ nsString mSourceMapURL;
+
#ifdef DEBUG
bool mPrincipalSet;
#endif
};
} // namespace mozilla
#endif // mozilla_StyleSheetInfo_h
--- a/layout/style/test/browser.ini
+++ b/layout/style/test/browser.ini
@@ -1,8 +1,14 @@
[DEFAULT]
support-files =
bug453896_iframe.html
media_queries_iframe.html
newtab_share_rule_processors.html
+ mapped.css
+ mapped.css^headers^
+ mapped2.css
+ mapped2.css^headers^
+ sourcemap_css.html
[browser_bug453896.js]
[browser_newtab_share_rule_processors.js]
+[browser_sourcemap.js]
new file mode 100644
--- /dev/null
+++ b/layout/style/test/browser_sourcemap.js
@@ -0,0 +1,30 @@
+add_task(async function() {
+ let uri = "http://example.com/browser/layout/style/test/sourcemap_css.html";
+ info(`URI is ${uri}`);
+
+ await BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: uri
+ }, async function(browser) {
+ await ContentTask.spawn(browser, null, function* () {
+ let seenSheets = 0;
+
+ for (let i = 0; i < content.document.styleSheets.length; ++i) {
+ let sheet = content.document.styleSheets[i];
+
+ info(`Checking ${sheet.href}`);
+ if (/mapped\.css/.test(sheet.href)) {
+ is(sheet.sourceMapURL, "mapped.css.map", "X-SourceMap header took effect");
+ seenSheets |= 1;
+ } else if (/mapped2\.css/.test(sheet.href)) {
+ is(sheet.sourceMapURL, "mapped2.css.map", "SourceMap header took effect");
+ seenSheets |= 2;
+ } else {
+ ok(false, "sheet does not have source map URL");
+ }
+ }
+
+ is(seenSheets, 3, "seen all source-mapped sheets");
+ });
+ });
+});
new file mode 100644
--- /dev/null
+++ b/layout/style/test/mapped.css
@@ -0,0 +1,3 @@
+div {
+ color: #f06;
+}
new file mode 100644
--- /dev/null
+++ b/layout/style/test/mapped.css^headers^
@@ -0,0 +1,1 @@
+X-SourceMap: mapped.css.map
new file mode 100644
--- /dev/null
+++ b/layout/style/test/mapped2.css
@@ -0,0 +1,3 @@
+span {
+ color: #f06;
+}
new file mode 100644
--- /dev/null
+++ b/layout/style/test/mapped2.css^headers^
@@ -0,0 +1,2 @@
+SourceMap: mapped2.css.map
+X-SourceMap: ignored.css.map
new file mode 100644
--- /dev/null
+++ b/layout/style/test/sourcemap_css.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for bug 1306887</title>
+ <link rel="stylesheet" type="text/css" href="mapped.css"/>
+ <link rel="stylesheet" type="text/css" href="mapped2.css"/>
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1306887">Mozilla Bug 1306887</a>
+ </body>
+</html>