зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1399911 - preserve sourceURL comment directive on style sheets; r=bz,heycam
In addition to the sourceMappingURL comment, there is a second special comment, "sourceURL", that can be used to set the "display name" of a style sheet for developer tools. This name is also used as the base URL for the source-map URL resolution algorithm. sourceURL is described here: https://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/ This patch changes Firefox to record this URL, if specified, and to expose it (chrome-only) vai StyleSheet.webidl. MozReview-Commit-ID: 7NwXsOf7nbY --HG-- extra : rebase_source : bd5d25b4d44f5f220a4624db346edbc4236c9886
This commit is contained in:
Родитель
fcc8fcf709
Коммит
916228aee7
|
@ -39,4 +39,10 @@ interface StyleSheet {
|
|||
// then this is an empty string.
|
||||
[ChromeOnly, Pure]
|
||||
readonly attribute DOMString sourceMapURL;
|
||||
// The source URL for this style sheet. If the style sheet has the
|
||||
// special "# sourceURL=" comment, then this is the URL specified
|
||||
// there. If no such comment is found, then this is the empty
|
||||
// string.
|
||||
[ChromeOnly, Pure]
|
||||
readonly attribute DOMString sourceURL;
|
||||
};
|
||||
|
|
|
@ -68,6 +68,8 @@ SERVO_BINDING_FUNC(Servo_StyleSheet_SizeOfIncludingThis, size_t,
|
|||
RawServoStyleSheetContentsBorrowed sheet)
|
||||
SERVO_BINDING_FUNC(Servo_StyleSheet_GetSourceMapURL, void,
|
||||
RawServoStyleSheetContentsBorrowed sheet, nsAString* result)
|
||||
SERVO_BINDING_FUNC(Servo_StyleSheet_GetSourceURL, void,
|
||||
RawServoStyleSheetContentsBorrowed sheet, nsAString* result)
|
||||
// We'd like to return `OriginFlags` here, but bindgen bitfield enums don't
|
||||
// work as return values with the Linux 32-bit ABI at the moment because
|
||||
// they wrap the value in a struct.
|
||||
|
|
|
@ -225,6 +225,10 @@ ServoStyleSheet::ParseSheet(css::Loader* aLoader,
|
|||
Servo_StyleSheet_GetSourceMapURL(Inner()->mContents, &sourceMapURL);
|
||||
SetSourceMapURLFromComment(sourceMapURL);
|
||||
|
||||
nsString sourceURL;
|
||||
Servo_StyleSheet_GetSourceURL(Inner()->mContents, &sourceURL);
|
||||
SetSourceURL(sourceURL);
|
||||
|
||||
Inner()->mURLData = extraData.forget();
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -261,6 +261,7 @@ StyleSheetInfo::StyleSheetInfo(StyleSheetInfo& aCopy,
|
|||
// without children.
|
||||
, mSourceMapURL(aCopy.mSourceMapURL)
|
||||
, mSourceMapURLFromComment(aCopy.mSourceMapURLFromComment)
|
||||
, mSourceURL(aCopy.mSourceURL)
|
||||
#ifdef DEBUG
|
||||
, mPrincipalSet(aCopy.mPrincipalSet)
|
||||
#endif
|
||||
|
@ -530,6 +531,18 @@ StyleSheet::SetSourceMapURLFromComment(const nsAString& aSourceMapURLFromComment
|
|||
mInner->mSourceMapURLFromComment = aSourceMapURLFromComment;
|
||||
}
|
||||
|
||||
void
|
||||
StyleSheet::GetSourceURL(nsAString& aSourceURL)
|
||||
{
|
||||
aSourceURL = mInner->mSourceURL;
|
||||
}
|
||||
|
||||
void
|
||||
StyleSheet::SetSourceURL(const nsAString& aSourceURL)
|
||||
{
|
||||
mInner->mSourceURL = aSourceURL;
|
||||
}
|
||||
|
||||
css::Rule*
|
||||
StyleSheet::GetDOMOwnerRule() const
|
||||
{
|
||||
|
|
|
@ -216,6 +216,8 @@ public:
|
|||
void GetSourceMapURL(nsAString& aTitle);
|
||||
void SetSourceMapURL(const nsAString& aSourceMapURL);
|
||||
void SetSourceMapURLFromComment(const nsAString& aSourceMapURLFromComment);
|
||||
void GetSourceURL(nsAString& aSourceURL);
|
||||
void SetSourceURL(const nsAString& aSourceURL);
|
||||
|
||||
// WebIDL CSSStyleSheet API
|
||||
// Can't be inline because we can't include ImportRule here. And can't be
|
||||
|
|
|
@ -70,6 +70,9 @@ struct StyleSheetInfo
|
|||
// so that the value does not overwrite any value that might have
|
||||
// come from a response header.
|
||||
nsString mSourceMapURLFromComment;
|
||||
// This stores any source URL that might have been seen in a comment
|
||||
// in the style sheet.
|
||||
nsString mSourceURL;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mPrincipalSet;
|
||||
|
|
|
@ -1648,6 +1648,7 @@ CSSParserImpl::ParseSheet(const nsAString& aInput,
|
|||
}
|
||||
|
||||
mSheet->SetSourceMapURLFromComment(scanner.GetSourceMapURL());
|
||||
mSheet->SetSourceURL(scanner.GetSourceURL());
|
||||
ReleaseScanner();
|
||||
|
||||
mParsingMode = css::eAuthorSheetFeatures;
|
||||
|
|
|
@ -542,29 +542,52 @@ nsCSSScanner::SkipWhitespace()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the given text appears at the current offset in the buffer,
|
||||
* advance over the text and return true. Otherwise, return false.
|
||||
* mLength is the number of characters in mDirective.
|
||||
*/
|
||||
bool
|
||||
nsCSSScanner::CheckCommentDirective(const nsAString& aDirective)
|
||||
{
|
||||
nsDependentSubstring text(&mBuffer[mOffset], &mBuffer[mCount]);
|
||||
|
||||
if (StringBeginsWith(text, aDirective)) {
|
||||
Advance(aDirective.Length());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip over one CSS comment starting at the current read position.
|
||||
*/
|
||||
void
|
||||
nsCSSScanner::SkipComment()
|
||||
{
|
||||
static const char sourceMappingURLDirective[] = "# sourceMappingURL=";
|
||||
// Note that these do not start with "#" or "@" -- that is handled
|
||||
// separately, below.
|
||||
static NS_NAMED_LITERAL_STRING(kSourceMappingURLDirective, " sourceMappingURL=");
|
||||
static NS_NAMED_LITERAL_STRING(kSourceURLDirective, " sourceURL=");
|
||||
|
||||
MOZ_ASSERT(Peek() == '/' && Peek(1) == '*', "should not have been called");
|
||||
Advance(2);
|
||||
// Look in each comment for a source map directive; using a simple
|
||||
// state machine. The states are:
|
||||
// * sourceMapIndex >= 0 means that we're still looking for the
|
||||
// directive and expect the next character to be at that index of
|
||||
// sourceMappingURLDirective.
|
||||
// As a special case, when sourceMapIndex == 0, '@' is also recognized.
|
||||
// * sourceMapIndex < 0 means that we don't need to look for the
|
||||
// directive any more -- whether it was found or not.
|
||||
// * copying == true means that the directive was found and we're
|
||||
// copying characters into mSourceMapURL. This stops at the first
|
||||
// whitespace, or at the end of the comment.
|
||||
int sourceMapIndex = 0;
|
||||
bool copying = false;
|
||||
|
||||
// If we saw one of the directives, this will be non-NULL and will
|
||||
// point to the string into which the URL will be written.
|
||||
nsString* directive = nullptr;
|
||||
if (Peek() == '#' || Peek() == '@') {
|
||||
// Check for the comment directives.
|
||||
Advance();
|
||||
if (CheckCommentDirective(kSourceMappingURLDirective)) {
|
||||
mSourceMapURL.Truncate();
|
||||
directive = &mSourceMapURL;
|
||||
} else if (CheckCommentDirective(kSourceURLDirective)) {
|
||||
mSourceURL.Truncate();
|
||||
directive = &mSourceURL;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int32_t ch = Peek();
|
||||
if (ch < 0) {
|
||||
|
@ -573,22 +596,6 @@ nsCSSScanner::SkipComment()
|
|||
SetEOFCharacters(eEOFCharacters_Asterisk | eEOFCharacters_Slash);
|
||||
return;
|
||||
}
|
||||
if (sourceMapIndex >= 0) {
|
||||
if ((sourceMapIndex == 0 && ch == '@') || ch == sourceMappingURLDirective[sourceMapIndex]) {
|
||||
++sourceMapIndex;
|
||||
if (sourceMappingURLDirective[sourceMapIndex] == '\0') {
|
||||
sourceMapIndex = -1;
|
||||
mSourceMapURL.Truncate();
|
||||
copying = true;
|
||||
Advance();
|
||||
// Make sure we don't copy out the '=' by falling through.
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Did not see the directive.
|
||||
sourceMapIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '*') {
|
||||
Advance();
|
||||
|
@ -605,20 +612,20 @@ nsCSSScanner::SkipComment()
|
|||
Advance();
|
||||
return;
|
||||
}
|
||||
if (copying) {
|
||||
mSourceMapURL.Append('*');
|
||||
if (directive != nullptr) {
|
||||
directive->Append('*');
|
||||
}
|
||||
} else if (IsVertSpace(ch)) {
|
||||
AdvanceLine();
|
||||
// Done with the directive, so stop copying.
|
||||
copying = false;
|
||||
directive = nullptr;
|
||||
} else if (IsWhitespace(ch)) {
|
||||
Advance();
|
||||
// Done with the directive, so stop copying.
|
||||
copying = false;
|
||||
directive = nullptr;
|
||||
} else {
|
||||
if (copying) {
|
||||
mSourceMapURL.Append(ch);
|
||||
if (directive != nullptr) {
|
||||
directive->Append(ch);
|
||||
}
|
||||
Advance();
|
||||
}
|
||||
|
|
|
@ -235,6 +235,9 @@ class nsCSSScanner {
|
|||
const nsAString& GetSourceMapURL() const
|
||||
{ return mSourceMapURL; }
|
||||
|
||||
const nsAString& GetSourceURL() const
|
||||
{ return mSourceURL; }
|
||||
|
||||
// Get the text of the line containing the first character of
|
||||
// the most recently processed token.
|
||||
nsDependentSubstring GetCurrentLine() const;
|
||||
|
@ -330,6 +333,7 @@ protected:
|
|||
void AdvanceLine();
|
||||
|
||||
void SkipWhitespace();
|
||||
bool CheckCommentDirective(const nsAString& aDirective);
|
||||
void SkipComment();
|
||||
|
||||
bool GatherEscape(nsString& aOutput, bool aInString);
|
||||
|
@ -366,6 +370,7 @@ protected:
|
|||
bool mSeenVariableReference;
|
||||
|
||||
nsString mSourceMapURL;
|
||||
nsString mSourceURL;
|
||||
};
|
||||
|
||||
// Token for the grid-template-areas micro-syntax
|
||||
|
|
|
@ -14,3 +14,4 @@ support-files =
|
|||
skip-if = stylo # Gecko-specific test
|
||||
[browser_sourcemap.js]
|
||||
[browser_sourcemap_comment.js]
|
||||
[browser_sourceurl_comment.js]
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
add_task(async function() {
|
||||
// Test text and expected results.
|
||||
let test_cases = [
|
||||
["/*# sourceURL=here*/", "here"],
|
||||
["/*# sourceURL=here */", "here"],
|
||||
["/*@ sourceURL=here*/", "here"],
|
||||
["/*@ sourceURL=there*/ /*# sourceURL=here*/", "here"],
|
||||
["/*# sourceURL=here there */", "here"],
|
||||
|
||||
["/*# sourceURL= here */", ""],
|
||||
["/*# sourceURL=*/", ""],
|
||||
["/*# sourceUR=here */", ""],
|
||||
["/*! sourceURL=here */", ""],
|
||||
["/*# sourceURL = here */", ""],
|
||||
["/* # sourceURL=here */", ""],
|
||||
];
|
||||
|
||||
let page = "<!DOCTYPE HTML>\n<html>\n<head>\n";
|
||||
for (let i = 0; i < test_cases.length; ++i) {
|
||||
page += `<style type="text/css"> #x${i} { color: red; }${test_cases[i][0]}</style>\n`;
|
||||
}
|
||||
page += "</head><body>some text</body></html>";
|
||||
|
||||
let uri = "data:text/html;base64," + btoa(page);
|
||||
info(`URI is ${uri}`);
|
||||
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: uri
|
||||
}, async function(browser) {
|
||||
await ContentTask.spawn(browser, test_cases, function* (tests) {
|
||||
for (let i = 0; i < content.document.styleSheets.length; ++i) {
|
||||
let sheet = content.document.styleSheets[i];
|
||||
|
||||
info(`Checking sheet #${i}`);
|
||||
is(sheet.sourceURL, tests[i][1], `correct source URL for sheet ${i}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
Загрузка…
Ссылка в новой задаче