Bug 1354989 - Avoid pivoting via UTF-16 when loading CSS in the Stylo mode. r=jdm,SimonSapin

MozReview-Commit-ID: Llt29dvB4Io

--HG--
extra : rebase_source : 3ae51dc8beff3fb19e9318a6c7c30c9ab08a5b57
This commit is contained in:
Henri Sivonen 2017-08-29 16:01:42 +03:00
parent 7d07e19274
commit 3c8567b60d
23 changed files with 804 additions and 456 deletions

View File

@ -9997,7 +9997,8 @@ NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
} // namespace
void
nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset,
nsDocument::PreloadStyle(nsIURI* uri,
const Encoding* aEncoding,
const nsAString& aCrossOriginAttr,
const ReferrerPolicy aReferrerPolicy,
const nsAString& aIntegrity)
@ -10006,11 +10007,14 @@ nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset,
nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
// Charset names are always ASCII.
CSSLoader()->LoadSheet(uri, true, NodePrincipal(),
NS_LossyConvertUTF16toASCII(charset),
CSSLoader()->LoadSheet(uri,
true,
NodePrincipal(),
aEncoding,
obs,
Element::StringToCORSMode(aCrossOriginAttr),
aReferrerPolicy, aIntegrity);
aReferrerPolicy,
aIntegrity);
}
nsresult

View File

@ -836,7 +836,8 @@ public:
virtual void MaybePreconnect(nsIURI* uri,
mozilla::CORSMode aCORSMode) override;
virtual void PreloadStyle(nsIURI* uri, const nsAString& charset,
virtual void PreloadStyle(nsIURI* uri,
const mozilla::Encoding* aEncoding,
const nsAString& aCrossOriginAttr,
ReferrerPolicy aReferrerPolicy,
const nsAString& aIntegrity) override;

View File

@ -2464,7 +2464,8 @@ public:
* parser if and when the parser is merged with libgklayout. aCrossOriginAttr
* should be a void string if the attr is not present.
*/
virtual void PreloadStyle(nsIURI* aURI, const nsAString& aCharset,
virtual void PreloadStyle(nsIURI* aURI,
const mozilla::Encoding* aEncoding,
const nsAString& aCrossOriginAttr,
ReferrerPolicyEnum aReferrerPolicy,
const nsAString& aIntegrity) = 0;

View File

@ -182,22 +182,18 @@ SRICheck::IntegrityMetadata(const nsAString& aMetadataList,
/* static */ nsresult
SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata,
nsIUnicharStreamLoader* aLoader,
const nsAString& aString,
nsIChannel* aChannel,
const nsACString& aBytes,
const nsACString& aSourceFileURI,
nsIConsoleReportCollector* aReporter)
{
NS_ENSURE_ARG_POINTER(aLoader);
NS_ENSURE_ARG_POINTER(aReporter);
nsCOMPtr<nsIChannel> channel;
aLoader->GetChannel(getter_AddRefs(channel));
if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) {
nsAutoCString requestURL;
nsCOMPtr<nsIURI> originalURI;
if (channel &&
NS_SUCCEEDED(channel->GetOriginalURI(getter_AddRefs(originalURI))) &&
if (aChannel &&
NS_SUCCEEDED(aChannel->GetOriginalURI(getter_AddRefs(originalURI))) &&
originalURI) {
originalURI->GetAsciiSpec(requestURL);
}
@ -205,14 +201,11 @@ SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata,
}
SRICheckDataVerifier verifier(aMetadata, aSourceFileURI, aReporter);
nsresult rv;
nsDependentCString rawBuffer;
rv = aLoader->GetRawBuffer(rawBuffer);
NS_ENSURE_SUCCESS(rv, rv);
rv = verifier.Update(rawBuffer.Length(), (const uint8_t*)rawBuffer.get());
nsresult rv =
verifier.Update(aBytes.Length(), (const uint8_t*)aBytes.BeginReading());
NS_ENSURE_SUCCESS(rv, rv);
return verifier.Verify(aMetadata, channel, aSourceFileURI, aReporter);
return verifier.Verify(aMetadata, aChannel, aSourceFileURI, aReporter);
}
//////////////////////////////////////////////////////////////

View File

@ -11,7 +11,6 @@
#include "nsICryptoHash.h"
class nsIChannel;
class nsIUnicharStreamLoader;
class nsIConsoleReportCollector;
namespace mozilla {
@ -39,8 +38,8 @@ public:
* must prevent the resource from loading.
*/
static nsresult VerifyIntegrity(const SRIMetadata& aMetadata,
nsIUnicharStreamLoader* aLoader,
const nsAString& aString,
nsIChannel* aChannel,
const nsACString& aBytes,
const nsACString& aSourceFileURI,
nsIConsoleReportCollector* aReporter);
};

View File

@ -151,7 +151,7 @@ nsXBLResourceLoader::LoadResources(nsIContent* aBoundElement)
}
else
{
rv = cssLoader->LoadSheet(url, false, docPrincipal, EmptyCString(), this);
rv = cssLoader->LoadSheet(url, false, docPrincipal, nullptr, this);
if (NS_SUCCEEDED(rv))
++mPendingSheets;
}

View File

@ -3738,10 +3738,8 @@ XULDocument::AddPrototypeSheets()
nsCOMPtr<nsIURI> uri = sheets[i];
RefPtr<StyleSheet> incompleteSheet;
rv = CSSLoader()->LoadSheet(uri,
mCurrentPrototype->DocumentPrincipal(),
EmptyCString(), this,
&incompleteSheet);
rv = CSSLoader()->LoadSheet(
uri, mCurrentPrototype->DocumentPrincipal(), this, &incompleteSheet);
// XXXldb We need to prevent bogus sheets from being held in the
// prototype's list, but until then, don't propagate the failure

View File

@ -2839,8 +2839,8 @@ HTMLEditor::ReplaceStyleSheet(const nsAString& aURL)
nsresult rv = NS_NewURI(getter_AddRefs(uaURI), aURL);
NS_ENSURE_SUCCESS(rv, rv);
return ps->GetDocument()->CSSLoader()->
LoadSheet(uaURI, false, nullptr, EmptyCString(), this);
return ps->GetDocument()->CSSLoader()->LoadSheet(
uaURI, false, nullptr, nullptr, this);
}
NS_IMETHODIMP

View File

@ -88,6 +88,14 @@ mozilla_encoding_decode_to_nscstring_without_bom_handling(
nsACString const* src,
nsACString* dst);
nsresult
mozilla_encoding_decode_from_slice_to_nscstring_without_bom_handling(
mozilla::Encoding const* encoding,
uint8_t const* src,
size_t src_len,
nsACString* dst,
size_t already_validated);
nsresult
mozilla_encoding_decode_to_nscstring_without_bom_handling_and_without_replacement(
mozilla::Encoding const* encoding,
@ -552,6 +560,41 @@ public:
this, bytes, out);
}
/**
* Decode complete input to `nsACString` _without BOM handling_ and
* with malformed sequences replaced with the REPLACEMENT CHARACTER when
* the entire input is available as a single buffer (i.e. the end of the
* buffer marks the end of the stream) _asserting that a number of bytes
* from the start are already known to be valid UTF-8_.
*
* The use case for this method is avoiding copying when dealing with
* input that has a UTF-8 BOM. _When in doubt, do not use this method._
*
* When invoked on `UTF_8`, this method implements the (non-streaming
* version of) the _UTF-8 decode without BOM_
* (https://encoding.spec.whatwg.org/#utf-8-decode-without-bom) spec concept.
*
* Returns `NS_ERROR_OUT_OF_MEMORY` upon OOM, `NS_OK_HAD_REPLACEMENTS`
* if there were malformed sequences (that were replaced with the
* REPLACEMENT CHARACTER) and `NS_OK` otherwise.
*
* _Note:_ It is wrong to use this when the input buffer represents only
* a segment of the input instead of the whole input. Use
* `NewDecoderWithoutBOMHandling()` when decoding segmented input.
*
* # Safety
*
* The first `aAlreadyValidated` bytes of `aBytes` _must_ be valid UTF-8.
* `aBytes` _must not_ alias the buffer (if any) of `aOut`.
*/
inline nsresult DecodeWithoutBOMHandling(Span<const uint8_t> aBytes,
nsACString& aOut,
size_t aAlreadyValidated) const
{
return mozilla_encoding_decode_from_slice_to_nscstring_without_bom_handling(
this, aBytes.Elements(), aBytes.Length(), &aOut, aAlreadyValidated);
}
/**
* Decode complete input to `nsAString` _without BOM handling_ and
* _with malformed sequences treated as fatal_ when the entire input is

View File

@ -421,6 +421,11 @@ pub fn decode_to_nscstring_without_bom_handling(encoding: &'static Encoding,
decode_from_slice_to_nscstring_without_bom_handling(encoding, src, dst, valid_up_to)
}
#[no_mangle]
pub unsafe extern "C" fn mozilla_encoding_decode_from_slice_to_nscstring_without_bom_handling(encoding: *const Encoding, src: *const u8, src_len: usize, dst: *mut nsACString, already_validated: usize) -> nsresult {
decode_from_slice_to_nscstring_without_bom_handling(&*encoding, slice::from_raw_parts(src, src_len), &mut *dst, already_validated)
}
fn decode_from_slice_to_nscstring_without_bom_handling(encoding: &'static Encoding,
src: &[u8],
dst: &mut nsACString,

View File

@ -53,6 +53,7 @@
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/ConsoleReportCollector.h"
#include "mozilla/ServoUtils.h"
#include "mozilla/css/StreamLoader.h"
#ifdef MOZ_XUL
#include "nsXULPrototypeCache.h"
@ -68,6 +69,9 @@
using namespace mozilla::dom;
// 1024 bytes is specified in https://drafts.csswg.org/css-syntax/
#define SNIFFING_BUFFER_SIZE 1024
/**
* OVERALL ARCHITECTURE
*
@ -97,162 +101,6 @@ using namespace mozilla::dom;
namespace mozilla {
namespace css {
/*********************************************
* Data needed to properly load a stylesheet *
*********************************************/
static_assert(eAuthorSheetFeatures == 0 &&
eUserSheetFeatures == 1 &&
eAgentSheetFeatures == 2,
"sheet parsing mode constants won't fit "
"in SheetLoadData::mParsingMode");
class SheetLoadData final : public nsIRunnable,
public nsIUnicharStreamLoaderObserver,
public nsIThreadObserver
{
protected:
virtual ~SheetLoadData(void);
public:
// Data for loading a sheet linked from a document
SheetLoadData(Loader* aLoader,
const nsAString& aTitle,
nsIURI* aURI,
StyleSheet* aSheet,
nsIStyleSheetLinkingElement* aOwningElement,
bool aIsAlternate,
nsICSSLoaderObserver* aObserver,
nsIPrincipal* aLoaderPrincipal,
nsINode* aRequestingNode);
// Data for loading a sheet linked from an @import rule
SheetLoadData(Loader* aLoader,
nsIURI* aURI,
StyleSheet* aSheet,
SheetLoadData* aParentData,
nsICSSLoaderObserver* aObserver,
nsIPrincipal* aLoaderPrincipal,
nsINode* aRequestingNode);
// Data for loading a non-document sheet
SheetLoadData(Loader* aLoader,
nsIURI* aURI,
StyleSheet* aSheet,
bool aSyncLoad,
bool aUseSystemPrincipal,
const nsCString& aCharset,
nsICSSLoaderObserver* aObserver,
nsIPrincipal* aLoaderPrincipal,
nsINode* aRequestingNode);
already_AddRefed<nsIURI> GetReferrerURI();
void ScheduleLoadEventIfNeeded(nsresult aStatus);
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSITHREADOBSERVER
NS_DECL_NSIUNICHARSTREAMLOADEROBSERVER
// Hold a ref to the CSSLoader so we can call back to it to let it
// know the load finished
RefPtr<Loader> mLoader;
// Title needed to pull datas out of the pending datas table when
// the preferred title is changed
nsString mTitle;
// Charset we decided to use for the sheet
nsCString mCharset;
// URI we're loading. Null for inline sheets
nsCOMPtr<nsIURI> mURI;
// Should be 1 for non-inline sheets.
uint32_t mLineNumber;
// The sheet we're loading data for
RefPtr<StyleSheet> mSheet;
// Linked list of datas for the same URI as us
SheetLoadData* mNext; // strong ref
// Load data for the sheet that @import-ed us if we were @import-ed
// during the parse
RefPtr<SheetLoadData> mParentData;
// Number of sheets we @import-ed that are still loading
uint32_t mPendingChildren;
// mSyncLoad is true when the load needs to be synchronous -- right
// now only for LoadSheetSync and children of sync loads.
bool mSyncLoad : 1;
// mIsNonDocumentSheet is true if the load was triggered by LoadSheetSync or
// LoadSheet or an @import from such a sheet. Non-document sheet loads can
// proceed even if we have no document.
bool mIsNonDocumentSheet : 1;
// mIsLoading is true from the moment we are placed in the loader's
// "loading datas" table (right after the async channel is opened)
// to the moment we are removed from said table (due to the load
// completing or being cancelled).
bool mIsLoading : 1;
// mIsCancelled is set to true when a sheet load is stopped by
// Stop() or StopLoadingSheet() (which was removed in Bug 556446).
// SheetLoadData::OnStreamComplete() checks this to avoid parsing
// sheets that have been cancelled and such.
bool mIsCancelled : 1;
// mMustNotify is true if the load data is being loaded async and
// the original function call that started the load has returned.
// This applies only to observer notifications; load/error events
// are fired for any SheetLoadData that has a non-null
// mOwningElement.
bool mMustNotify : 1;
// mWasAlternate is true if the sheet was an alternate when the load data was
// created.
bool mWasAlternate : 1;
// mUseSystemPrincipal is true if the system principal should be used for
// this sheet, no matter what the channel principal is. Only true for sync
// loads.
bool mUseSystemPrincipal : 1;
// If true, this SheetLoadData is being used as a way to handle
// async observer notification for an already-complete sheet.
bool mSheetAlreadyComplete : 1;
// This is the element that imported the sheet. Needed to get the
// charset set on it and to fire load/error events.
nsCOMPtr<nsIStyleSheetLinkingElement> mOwningElement;
// The observer that wishes to be notified of load completion
nsCOMPtr<nsICSSLoaderObserver> mObserver;
// The principal that identifies who started loading us.
nsCOMPtr<nsIPrincipal> mLoaderPrincipal;
// The node that identifies who started loading us.
nsCOMPtr<nsINode> mRequestingNode;
// The charset to use if the transport and sheet don't indicate one.
// May be empty. Must be empty if mOwningElement is non-null.
nsCString mCharsetHint;
// The status our load ended up with; this determines whether we
// should fire error events or load events. This gets initialized
// by ScheduleLoadEventIfNeeded, and is only used after that has
// been called.
MOZ_INIT_OUTSIDE_CTOR nsresult mStatus;
private:
void FireLoadEvent(nsIThreadInternal* aThread);
};
#include "mozilla/Logging.h"
static mozilla::LazyLogModule sCssLoaderLog("nsCSSLoader");
@ -301,25 +149,27 @@ SheetLoadData::SheetLoadData(Loader* aLoader,
nsICSSLoaderObserver* aObserver,
nsIPrincipal* aLoaderPrincipal,
nsINode* aRequestingNode)
: mLoader(aLoader),
mTitle(aTitle),
mURI(aURI),
mLineNumber(1),
mSheet(aSheet),
mNext(nullptr),
mPendingChildren(0),
mSyncLoad(false),
mIsNonDocumentSheet(false),
mIsLoading(false),
mIsCancelled(false),
mMustNotify(false),
mWasAlternate(aIsAlternate),
mUseSystemPrincipal(false),
mSheetAlreadyComplete(false),
mOwningElement(aOwningElement),
mObserver(aObserver),
mLoaderPrincipal(aLoaderPrincipal),
mRequestingNode(aRequestingNode)
: mLoader(aLoader)
, mTitle(aTitle)
, mEncoding(nullptr)
, mURI(aURI)
, mLineNumber(1)
, mSheet(aSheet)
, mNext(nullptr)
, mPendingChildren(0)
, mSyncLoad(false)
, mIsNonDocumentSheet(false)
, mIsLoading(false)
, mIsCancelled(false)
, mMustNotify(false)
, mWasAlternate(aIsAlternate)
, mUseSystemPrincipal(false)
, mSheetAlreadyComplete(false)
, mOwningElement(aOwningElement)
, mObserver(aObserver)
, mLoaderPrincipal(aLoaderPrincipal)
, mRequestingNode(aRequestingNode)
, mPreloadEncoding(nullptr)
{
NS_PRECONDITION(mLoader, "Must have a loader!");
}
@ -331,25 +181,27 @@ SheetLoadData::SheetLoadData(Loader* aLoader,
nsICSSLoaderObserver* aObserver,
nsIPrincipal* aLoaderPrincipal,
nsINode* aRequestingNode)
: mLoader(aLoader),
mURI(aURI),
mLineNumber(1),
mSheet(aSheet),
mNext(nullptr),
mParentData(aParentData),
mPendingChildren(0),
mSyncLoad(false),
mIsNonDocumentSheet(false),
mIsLoading(false),
mIsCancelled(false),
mMustNotify(false),
mWasAlternate(false),
mUseSystemPrincipal(false),
mSheetAlreadyComplete(false),
mOwningElement(nullptr),
mObserver(aObserver),
mLoaderPrincipal(aLoaderPrincipal),
mRequestingNode(aRequestingNode)
: mLoader(aLoader)
, mEncoding(nullptr)
, mURI(aURI)
, mLineNumber(1)
, mSheet(aSheet)
, mNext(nullptr)
, mParentData(aParentData)
, mPendingChildren(0)
, mSyncLoad(false)
, mIsNonDocumentSheet(false)
, mIsLoading(false)
, mIsCancelled(false)
, mMustNotify(false)
, mWasAlternate(false)
, mUseSystemPrincipal(false)
, mSheetAlreadyComplete(false)
, mOwningElement(nullptr)
, mObserver(aObserver)
, mLoaderPrincipal(aLoaderPrincipal)
, mRequestingNode(aRequestingNode)
, mPreloadEncoding(nullptr)
{
NS_PRECONDITION(mLoader, "Must have a loader!");
if (mParentData) {
@ -368,29 +220,30 @@ SheetLoadData::SheetLoadData(Loader* aLoader,
StyleSheet* aSheet,
bool aSyncLoad,
bool aUseSystemPrincipal,
const nsCString& aCharset,
const Encoding* aPreloadEncoding,
nsICSSLoaderObserver* aObserver,
nsIPrincipal* aLoaderPrincipal,
nsINode* aRequestingNode)
: mLoader(aLoader),
mURI(aURI),
mLineNumber(1),
mSheet(aSheet),
mNext(nullptr),
mPendingChildren(0),
mSyncLoad(aSyncLoad),
mIsNonDocumentSheet(true),
mIsLoading(false),
mIsCancelled(false),
mMustNotify(false),
mWasAlternate(false),
mUseSystemPrincipal(aUseSystemPrincipal),
mSheetAlreadyComplete(false),
mOwningElement(nullptr),
mObserver(aObserver),
mLoaderPrincipal(aLoaderPrincipal),
mRequestingNode(aRequestingNode),
mCharsetHint(aCharset)
: mLoader(aLoader)
, mEncoding(nullptr)
, mURI(aURI)
, mLineNumber(1)
, mSheet(aSheet)
, mNext(nullptr)
, mPendingChildren(0)
, mSyncLoad(aSyncLoad)
, mIsNonDocumentSheet(true)
, mIsLoading(false)
, mIsCancelled(false)
, mMustNotify(false)
, mWasAlternate(false)
, mUseSystemPrincipal(aUseSystemPrincipal)
, mSheetAlreadyComplete(false)
, mOwningElement(nullptr)
, mObserver(aObserver)
, mLoaderPrincipal(aLoaderPrincipal)
, mRequestingNode(aRequestingNode)
, mPreloadEncoding(aPreloadEncoding)
{
NS_PRECONDITION(mLoader, "Must have a loader!");
NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
@ -645,123 +498,90 @@ static bool GetCharsetFromData(const char* aStyleSheetData,
return false;
}
NS_IMETHODIMP
SheetLoadData::OnDetermineCharset(nsIUnicharStreamLoader* aLoader,
nsISupports* aContext,
nsACString const& aSegment,
nsACString& aCharset)
NotNull<const Encoding*>
SheetLoadData::DetermineNonBOMEncoding(nsACString const& aSegment,
nsIChannel* aChannel)
{
NS_PRECONDITION(!mOwningElement || mCharsetHint.IsEmpty(),
"Can't have element _and_ charset hint");
LOG_URI("SheetLoadData::OnDetermineCharset for '%s'", mURI);
// The precedence is (per CSS3 Syntax 2012-11-08 ED):
// BOM
// Channel
// @charset rule
// charset attribute on the referrer
// encoding of the referrer
// UTF-8
aCharset.Truncate();
const Encoding* encoding;
size_t bomLength;
Tie(encoding, bomLength) = Encoding::ForBOM(aSegment);
Unused << bomLength;
if (encoding) {
encoding->Name(aCharset);
// aCharset is now either "UTF-16BE", "UTF-16BE" or "UTF-8"
// which will swallow the BOM.
mCharset.Assign(aCharset);
LOG((" Setting from BOM to: %s", PromiseFlatCString(aCharset).get()));
return NS_OK;
}
nsAutoCString label;
nsCOMPtr<nsIChannel> channel;
nsAutoCString specified;
aLoader->GetChannel(getter_AddRefs(channel));
if (channel) {
channel->GetContentCharset(specified);
encoding = Encoding::ForLabel(specified);
// Check HTTP
if (aChannel && NS_SUCCEEDED(aChannel->GetContentCharset(label))) {
encoding = Encoding::ForLabel(label);
if (encoding) {
encoding->Name(aCharset);
mCharset.Assign(aCharset);
LOG((" Setting from HTTP to: %s", PromiseFlatCString(aCharset).get()));
return NS_OK;
return WrapNotNull(encoding);
}
}
if (GetCharsetFromData(aSegment.BeginReading(),
aSegment.Length(),
specified)) {
encoding = Encoding::ForLabel(specified);
// Check @charset
auto sniffingLength = aSegment.Length();
if (sniffingLength > SNIFFING_BUFFER_SIZE) {
sniffingLength = SNIFFING_BUFFER_SIZE;
}
if (GetCharsetFromData(aSegment.BeginReading(), sniffingLength, label)) {
encoding = Encoding::ForLabel(label);
if (encoding == UTF_16BE_ENCODING || encoding == UTF_16LE_ENCODING) {
return UTF_8_ENCODING;
}
if (encoding) {
encoding->Name(aCharset);
if (encoding == UTF_16BE_ENCODING ||
encoding == UTF_16LE_ENCODING) {
// Be consistent with HTML <meta> handling in face of impossibility.
// When the @charset rule itself evidently was not UTF-16-encoded,
// it saying UTF-16 has to be a lie.
aCharset.AssignLiteral("UTF-8");
}
mCharset.Assign(aCharset);
LOG((" Setting from @charset rule to: %s",
PromiseFlatCString(aCharset).get()));
return NS_OK;
return WrapNotNull(encoding);
}
}
// Now try the charset on the <link> or processing instruction
// that loaded us
if (mOwningElement) {
nsAutoString specified16;
mOwningElement->GetCharset(specified16);
encoding = Encoding::ForLabel(specified16);
nsAutoString label16;
mOwningElement->GetCharset(label16);
encoding = Encoding::ForLabel(label16);
if (encoding) {
encoding->Name(aCharset);
mCharset.Assign(aCharset);
LOG((" Setting from charset attribute to: %s",
PromiseFlatCString(aCharset).get()));
return NS_OK;
return WrapNotNull(encoding);
}
}
// In the preload case, the value of the charset attribute on <link> comes
// in via mCharsetHint instead.
encoding = Encoding::ForLabel(mCharsetHint);
if (encoding) {
encoding->Name(aCharset);
mCharset.Assign(aCharset);
LOG((" Setting from charset attribute (preload case) to: %s",
PromiseFlatCString(aCharset).get()));
return NS_OK;
// in via mPreloadEncoding instead.
if (mPreloadEncoding) {
return WrapNotNull(mPreloadEncoding);
}
// Try charset from the parent stylesheet.
if (mParentData) {
aCharset = mParentData->mCharset;
if (!aCharset.IsEmpty()) {
mCharset.Assign(aCharset);
LOG((" Setting from parent sheet to: %s",
PromiseFlatCString(aCharset).get()));
return NS_OK;
encoding = mParentData->mEncoding;
if (encoding) {
return WrapNotNull(encoding);
}
}
if (mLoader->mDocument) {
// no useful data on charset. Try the document charset.
auto encoding = mLoader->mDocument->GetDocumentCharacterSet();
encoding->Name(aCharset);
mCharset.Assign(aCharset);
LOG((" Setting from document to: %s", PromiseFlatCString(aCharset).get()));
return NS_OK;
// Use the document charset.
return mLoader->mDocument->GetDocumentCharacterSet();
}
aCharset.AssignLiteral("UTF-8");
mCharset = aCharset;
LOG((" Setting from default to: %s", PromiseFlatCString(aCharset).get()));
return UTF_8_ENCODING;
}
/*
* Encoding decision for the old style system
*/
NS_IMETHODIMP
SheetLoadData::OnDetermineCharset(nsIUnicharStreamLoader* aLoader,
nsISupports* aContext,
nsACString const& aSegment,
nsACString& aCharset)
{
const Encoding* encoding;
size_t bomLength;
Tie(encoding, bomLength) = Encoding::ForBOM(aSegment);
Unused << bomLength;
if (!encoding) {
nsCOMPtr<nsIChannel> channel;
aLoader->GetChannel(getter_AddRefs(channel));
encoding = DetermineNonBOMEncoding(aSegment, channel);
}
encoding->Name(aCharset);
mEncoding = encoding;
return NS_OK;
}
@ -777,15 +597,41 @@ SheetLoadData::GetReferrerURI()
}
/*
* Here we need to check that the load did not give us an http error
* page and check the mimetype on the channel to make sure we're not
* loading non-text/css data in standards mode.
* Load completion for the old style system.
*/
NS_IMETHODIMP
SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
nsISupports* aContext,
nsresult aStatus,
const nsAString& aBuffer)
{
nsCOMPtr<nsIChannel> channel;
aLoader->GetChannel(getter_AddRefs(channel));
nsCString bytes;
aLoader->GetRawBuffer(bytes);
nsresult rv = VerifySheetReadyToParse(aStatus, bytes, channel);
if (rv != NS_OK_PARSE_SHEET) {
return rv;
}
bool completed;
rv = mLoader->ParseSheet(aBuffer, Span<const uint8_t>(), this, completed);
NS_ASSERTION(completed || !mSyncLoad, "sync load did not complete");
return rv;
}
/*
* Stream completion code shared by Stylo and the old style system.
*
* Here we need to check that the load did not give us an http error
* page and check the mimetype on the channel to make sure we're not
* loading non-text/css data in standards mode.
*/
nsresult
SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
const nsACString& aBytes,
nsIChannel* aChannel)
{
LOG(("SheetLoadData::OnStreamComplete"));
NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
@ -826,16 +672,13 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
return NS_OK;
}
nsCOMPtr<nsIChannel> channel;
nsresult result = aLoader->GetChannel(getter_AddRefs(channel));
if (NS_FAILED(result)) {
LOG_WARN((" No channel from loader"));
mLoader->SheetComplete(this, result);
if (!aChannel) {
mLoader->SheetComplete(this, NS_OK);
return NS_OK;
}
nsCOMPtr<nsIURI> originalURI;
channel->GetOriginalURI(getter_AddRefs(originalURI));
aChannel->GetOriginalURI(getter_AddRefs(originalURI));
// If the channel's original URI is "chrome:", we want that, since
// the observer code in nsXULPrototypeCache depends on chrome stylesheets
@ -843,7 +686,7 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
// this codepath seems nondeterministic.)
// Otherwise we want the potentially-HTTP-redirected URI.
nsCOMPtr<nsIURI> channelURI;
NS_GetFinalChannelURI(channel, getter_AddRefs(channelURI));
NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
if (!channelURI || !originalURI) {
NS_ERROR("Someone just violated the nsIRequest contract");
@ -854,12 +697,13 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
nsCOMPtr<nsIPrincipal> principal;
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
result = NS_ERROR_NOT_AVAILABLE;
nsresult result = NS_ERROR_NOT_AVAILABLE;
if (secMan) { // Could be null if we already shut down
if (mUseSystemPrincipal) {
result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
} else {
result = secMan->GetChannelResultPrincipal(channel, getter_AddRefs(principal));
result =
secMan->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
}
}
@ -873,7 +717,7 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
// If it's an HTTP channel, we want to make sure this is not an
// error document we got.
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
if (httpChannel) {
bool requestSucceeded;
result = httpChannel->GetRequestSucceeded(&requestSucceeded);
@ -890,9 +734,7 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
}
nsAutoCString contentType;
if (channel) {
channel->GetContentType(contentType);
}
aChannel->GetContentType(contentType);
// In standards mode, a style sheet must have one of these MIME
// types to be processed at all. In quirks mode, we accept any
@ -949,7 +791,7 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
SRIMetadata sriMetadata;
mSheet->GetIntegrity(sriMetadata);
if (sriMetadata.IsEmpty()) {
nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
if (loadInfo && loadInfo->GetEnforceSRI()) {
LOG((" Load was blocked by SRI"));
MOZ_LOG(gSriPRLog, mozilla::LogLevel::Debug,
@ -972,11 +814,11 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
if (mLoader->mDocument && mLoader->mDocument->GetDocumentURI()) {
mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
}
nsresult rv = SRICheck::VerifyIntegrity(sriMetadata, aLoader, aBuffer,
sourceUri, mLoader->mReporter);
nsresult rv = SRICheck::VerifyIntegrity(
sriMetadata, aChannel, aBytes, sourceUri, mLoader->mReporter);
nsCOMPtr<nsILoadGroup> loadGroup;
channel->GetLoadGroup(getter_AddRefs(loadGroup));
aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
if (loadGroup) {
mLoader->mReporter->FlushConsoleReports(loadGroup);
} else {
@ -995,11 +837,7 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
// Enough to set the URIs on mSheet, since any sibling datas we have share
// the same mInner as mSheet and will thus get the same URI.
mSheet->SetURIs(channelURI, originalURI, channelURI);
bool completed;
result = mLoader->ParseSheet(aBuffer, this, completed);
NS_ASSERTION(completed || !mSyncLoad, "sync load did not complete");
return result;
return NS_OK_PARSE_SHEET;
}
bool
@ -1469,12 +1307,19 @@ Loader::LoadSheet(SheetLoadData* aLoadData,
// Create a nsIUnicharStreamLoader instance to which we will feed
// the data from the sync load. Do this before creating the
// channel to make error recovery simpler.
nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
if (NS_FAILED(rv)) {
LOG_ERROR((" Failed to create stream loader for sync load"));
SheetComplete(aLoadData, rv);
return rv;
nsCOMPtr<nsIStreamListener> streamLoader;
if (aLoadData->mSheet->IsGecko()) {
nsCOMPtr<nsIUnicharStreamLoader> unicharStreamLoader;
rv = NS_NewUnicharStreamLoader(getter_AddRefs(unicharStreamLoader),
aLoadData);
streamLoader = unicharStreamLoader;
if (NS_FAILED(rv)) {
LOG_ERROR((" Failed to create stream loader for sync load"));
SheetComplete(aLoadData, rv);
return rv;
}
} else {
streamLoader = new StreamLoader(aLoadData);
}
if (mDocument) {
@ -1707,15 +1552,22 @@ Loader::LoadSheet(SheetLoadData* aLoadData,
// We don't have to hold on to the stream loader. The ownership
// model is: Necko owns the stream loader, which owns the load data,
// which owns us
nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
if (NS_FAILED(rv)) {
nsCOMPtr<nsIStreamListener> streamLoader;
if (aLoadData->mSheet->IsGecko()) {
nsCOMPtr<nsIUnicharStreamLoader> unicharStreamLoader;
rv =
NS_NewUnicharStreamLoader(getter_AddRefs(unicharStreamLoader), aLoadData);
streamLoader = unicharStreamLoader;
if (NS_FAILED(rv)) {
#ifdef DEBUG
mSyncCallback = false;
mSyncCallback = false;
#endif
LOG_ERROR((" Failed to create stream loader"));
SheetComplete(aLoadData, rv);
return rv;
LOG_ERROR((" Failed to create stream loader"));
SheetComplete(aLoadData, rv);
return rv;
}
} else {
streamLoader = new StreamLoader(aLoadData);
}
if (mDocument) {
@ -1749,7 +1601,8 @@ Loader::LoadSheet(SheetLoadData* aLoadData,
* correctly.
*/
nsresult
Loader::ParseSheet(const nsAString& aInput,
Loader::ParseSheet(const nsAString& aUTF16,
Span<const uint8_t> aUTF8,
SheetLoadData* aLoadData,
bool& aCompleted)
{
@ -1768,16 +1621,20 @@ Loader::ParseSheet(const nsAString& aInput,
if (aLoadData->mSheet->IsGecko()) {
nsCSSParser parser(this, aLoadData->mSheet->AsGecko());
rv = parser.ParseSheet(aInput, sheetURI, baseURI,
rv = parser.ParseSheet(aUTF16,
sheetURI,
baseURI,
aLoadData->mSheet->Principal(),
aLoadData->mLineNumber);
} else {
rv =
aLoadData->mSheet->AsServo()->ParseSheet(this,
aInput, sheetURI, baseURI,
aLoadData->mSheet->Principal(),
aLoadData->mLineNumber,
GetCompatibilityMode());
rv = aLoadData->mSheet->AsServo()->ParseSheet(
this,
aUTF8.IsEmpty() ? NS_ConvertUTF16toUTF8(aUTF16) : aUTF8,
sheetURI,
baseURI,
aLoadData->mSheet->Principal(),
aLoadData->mLineNumber,
GetCompatibilityMode());
}
mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1);
@ -2036,7 +1893,7 @@ Loader::LoadInlineStyle(nsIContent* aElement,
NS_ADDREF(data);
data->mLineNumber = aLineNumber;
// Parse completion releases the load data
rv = ParseSheet(aBuffer, data, *aCompleted);
rv = ParseSheet(aBuffer, Span<const uint8_t>(), data, *aCompleted);
NS_ENSURE_SUCCESS(rv, rv);
// If aCompleted is true, |data| may well be deleted by now.
@ -2312,9 +2169,13 @@ Loader::LoadSheetSync(nsIURI* aURL,
{
LOG(("css::Loader::LoadSheetSync"));
return InternalLoadNonDocumentSheet(aURL,
false, aParsingMode, aUseSystemPrincipal,
nullptr, EmptyCString(),
aSheet, nullptr);
false,
aParsingMode,
aUseSystemPrincipal,
nullptr,
nullptr,
aSheet,
nullptr);
}
nsresult
@ -2326,31 +2187,38 @@ Loader::LoadSheet(nsIURI* aURL,
{
LOG(("css::Loader::LoadSheet(aURL, aParsingMode, aUseSystemPrincipal, aObserver, aSheet)"));
return InternalLoadNonDocumentSheet(aURL,
false, aParsingMode, aUseSystemPrincipal,
nullptr, EmptyCString(),
aSheet, aObserver);
false,
aParsingMode,
aUseSystemPrincipal,
nullptr,
nullptr,
aSheet,
aObserver);
}
nsresult
Loader::LoadSheet(nsIURI* aURL,
nsIPrincipal* aOriginPrincipal,
const nsCString& aCharset,
nsICSSLoaderObserver* aObserver,
RefPtr<StyleSheet>* aSheet)
{
LOG(("css::Loader::LoadSheet(aURL, aObserver, aSheet) api call"));
NS_PRECONDITION(aSheet, "aSheet is null");
return InternalLoadNonDocumentSheet(aURL,
false, eAuthorSheetFeatures, false,
aOriginPrincipal, aCharset,
aSheet, aObserver);
false,
eAuthorSheetFeatures,
false,
aOriginPrincipal,
nullptr,
aSheet,
aObserver);
}
nsresult
Loader::LoadSheet(nsIURI* aURL,
bool aIsPreload,
nsIPrincipal* aOriginPrincipal,
const nsCString& aCharset,
const Encoding* aPreloadEncoding,
nsICSSLoaderObserver* aObserver,
CORSMode aCORSMode,
ReferrerPolicy aReferrerPolicy,
@ -2358,10 +2226,16 @@ Loader::LoadSheet(nsIURI* aURL,
{
LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
return InternalLoadNonDocumentSheet(aURL,
aIsPreload, eAuthorSheetFeatures, false,
aOriginPrincipal, aCharset,
nullptr, aObserver,
aCORSMode, aReferrerPolicy, aIntegrity);
aIsPreload,
eAuthorSheetFeatures,
false,
aOriginPrincipal,
aPreloadEncoding,
nullptr,
aObserver,
aCORSMode,
aReferrerPolicy,
aIntegrity);
}
nsresult
@ -2370,7 +2244,7 @@ Loader::InternalLoadNonDocumentSheet(nsIURI* aURL,
SheetParsingMode aParsingMode,
bool aUseSystemPrincipal,
nsIPrincipal* aOriginPrincipal,
const nsCString& aCharset,
const Encoding* aPreloadEncoding,
RefPtr<StyleSheet>* aSheet,
nsICSSLoaderObserver* aObserver,
CORSMode aCORSMode,
@ -2421,10 +2295,15 @@ Loader::InternalLoadNonDocumentSheet(nsIURI* aURL,
return rv;
}
SheetLoadData* data =
new SheetLoadData(this, aURL, sheet, syncLoad,
aUseSystemPrincipal, aCharset, aObserver,
aOriginPrincipal, mDocument);
SheetLoadData* data = new SheetLoadData(this,
aURL,
sheet,
syncLoad,
aUseSystemPrincipal,
aPreloadEncoding,
aObserver,
aOriginPrincipal,
mDocument);
NS_ADDREF(data);
rv = LoadSheet(data, state, aIsPreload);

View File

@ -375,11 +375,6 @@ public:
* @param aOriginPrincipal the principal to use for security checks. This
* can be null to indicate that these checks should
* be skipped.
* @param aCharset the encoding to use for converting the sheet data
* from bytes to Unicode. May be empty to indicate that the
* charset of the CSSLoader's document should be used. This
* is only used if neither the network transport nor the
* sheet itself indicate an encoding.
* @param aObserver the observer to notify when the load completes.
* Must not be null.
* @param [out] aSheet the sheet to load. Note that the sheet may well
@ -387,7 +382,6 @@ public:
*/
nsresult LoadSheet(nsIURI* aURL,
nsIPrincipal* aOriginPrincipal,
const nsCString& aCharset,
nsICSSLoaderObserver* aObserver,
RefPtr<StyleSheet>* aSheet);
@ -398,7 +392,7 @@ public:
nsresult LoadSheet(nsIURI* aURL,
bool aIsPreload,
nsIPrincipal* aOriginPrincipal,
const nsCString& aCharset,
const Encoding* aPreloadEncoding,
nsICSSLoaderObserver* aObserver,
CORSMode aCORSMode = CORS_NONE,
ReferrerPolicy aReferrerPolicy = mozilla::net::RP_Unset,
@ -477,6 +471,7 @@ public:
private:
friend class SheetLoadData;
friend class StreamLoader;
nsresult CheckContentPolicy(nsIPrincipal* aSourcePrincipal,
nsIURI* aTargetURI,
@ -520,17 +515,18 @@ private:
StyleSheet* aParentSheet,
ImportRule* aGeckoParentRule);
nsresult InternalLoadNonDocumentSheet(nsIURI* aURL,
bool aIsPreload,
SheetParsingMode aParsingMode,
bool aUseSystemPrincipal,
nsIPrincipal* aOriginPrincipal,
const nsCString& aCharset,
RefPtr<StyleSheet>* aSheet,
nsICSSLoaderObserver* aObserver,
CORSMode aCORSMode = CORS_NONE,
ReferrerPolicy aReferrerPolicy = mozilla::net::RP_Unset,
const nsAString& aIntegrity = EmptyString());
nsresult InternalLoadNonDocumentSheet(
nsIURI* aURL,
bool aIsPreload,
SheetParsingMode aParsingMode,
bool aUseSystemPrincipal,
nsIPrincipal* aOriginPrincipal,
const Encoding* aPreloadEncoding,
RefPtr<StyleSheet>* aSheet,
nsICSSLoaderObserver* aObserver,
CORSMode aCORSMode = CORS_NONE,
ReferrerPolicy aReferrerPolicy = mozilla::net::RP_Unset,
const nsAString& aIntegrity = EmptyString());
// Post a load event for aObserver to be notified about aSheet. The
// notification will be sent with status NS_OK unless the load event is
@ -557,11 +553,13 @@ private:
StyleSheetState aSheetState,
bool aIsPreLoad);
// Parse the stylesheet in aLoadData. The sheet data comes from aInput.
// Set aCompleted to true if the parse finished, false otherwise (e.g. if the
// Parse the stylesheet in aLoadData. The sheet data comes from aUTF16 if
// UTF-16 and from aUTF8 if UTF-8.
// Sets aCompleted to true if the parse finished, false otherwise (e.g. if the
// sheet had an @import). If aCompleted is true when this returns, then
// ParseSheet also called SheetComplete on aLoadData.
nsresult ParseSheet(const nsAString& aInput,
nsresult ParseSheet(const nsAString& aUTF16,
Span<const uint8_t> aUTF8,
SheetLoadData* aLoadData,
bool& aCompleted);

View File

@ -38,10 +38,12 @@ SERVO_BINDING_FUNC(Servo_Element_IsDisplayNone,
RawGeckoElementBorrowed element)
// Styleset and Stylesheet management
SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes, RawServoStyleSheetContentsStrong,
SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes,
RawServoStyleSheetContentsStrong,
mozilla::css::Loader* loader,
mozilla::ServoStyleSheet* gecko_stylesheet,
const nsACString* data,
const uint8_t* data,
size_t data_len,
mozilla::css::SheetParsingMode parsing_mode,
RawGeckoURLExtraData* extra_data,
uint32_t line_number_offset,

View File

@ -195,7 +195,7 @@ ServoStyleSheet::HasRules() const
nsresult
ServoStyleSheet::ParseSheet(css::Loader* aLoader,
const nsAString& aInput,
Span<const uint8_t> aInput,
nsIURI* aSheetURI,
nsIURI* aBaseURI,
nsIPrincipal* aSheetPrincipal,
@ -207,12 +207,16 @@ ServoStyleSheet::ParseSheet(css::Loader* aLoader,
RefPtr<URLExtraData> extraData =
new URLExtraData(aBaseURI, aSheetURI, aSheetPrincipal);
NS_ConvertUTF16toUTF8 input(aInput);
Inner()->mContents =
Servo_StyleSheet_FromUTF8Bytes(
aLoader, this, &input, mParsingMode, extraData,
aLineNumber, aCompatMode, aReusableSheets
).Consume();
Inner()->mContents = Servo_StyleSheet_FromUTF8Bytes(aLoader,
this,
aInput.Elements(),
aInput.Length(),
mParsingMode,
extraData,
aLineNumber,
aCompatMode,
aReusableSheets)
.Consume();
Inner()->mURLData = extraData.forget();
return NS_OK;
@ -291,9 +295,14 @@ ServoStyleSheet::ReparseSheet(const nsAString& aInput)
DropRuleList();
nsresult rv = ParseSheet(loader, aInput, mInner->mSheetURI, mInner->mBaseURI,
mInner->mPrincipal, lineNumber,
eCompatibility_FullStandards, &reusableSheets);
nsresult rv = ParseSheet(loader,
NS_ConvertUTF16toUTF8(aInput),
mInner->mSheetURI,
mInner->mBaseURI,
mInner->mPrincipal,
lineNumber,
eCompatibility_FullStandards,
&reusableSheets);
DidDirty();
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -84,14 +84,15 @@ public:
bool HasRules() const;
MOZ_MUST_USE nsresult ParseSheet(css::Loader* aLoader,
const nsAString& aInput,
nsIURI* aSheetURI,
nsIURI* aBaseURI,
nsIPrincipal* aSheetPrincipal,
uint32_t aLineNumber,
nsCompatibility aCompatMode,
css::LoaderReusableStyleSheets* aReusableSheets = nullptr);
MOZ_MUST_USE nsresult
ParseSheet(css::Loader* aLoader,
Span<const uint8_t> aInput,
nsIURI* aSheetURI,
nsIURI* aBaseURI,
nsIPrincipal* aSheetPrincipal,
uint32_t aLineNumber,
nsCompatibility aCompatMode,
css::LoaderReusableStyleSheets* aReusableSheets = nullptr);
nsresult ReparseSheet(const nsAString& aInput);

View File

@ -0,0 +1,183 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: ft=cpp tw=78 sw=2 et ts=2
*
* 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/. */
#ifndef mozilla_css_SheetLoadData_h
#define mozilla_css_SheetLoadData_h
#include "nsIUnicharStreamLoader.h"
#include "nsIThreadInternal.h"
namespace mozilla {
namespace css {
/*********************************************
* Data needed to properly load a stylesheet *
*********************************************/
static_assert(eAuthorSheetFeatures == 0 && eUserSheetFeatures == 1 &&
eAgentSheetFeatures == 2,
"sheet parsing mode constants won't fit "
"in SheetLoadData::mParsingMode");
class SheetLoadData final
: public nsIRunnable
, public nsIUnicharStreamLoaderObserver
, public nsIThreadObserver
{
protected:
virtual ~SheetLoadData(void);
public:
// Data for loading a sheet linked from a document
SheetLoadData(Loader* aLoader,
const nsAString& aTitle,
nsIURI* aURI,
StyleSheet* aSheet,
nsIStyleSheetLinkingElement* aOwningElement,
bool aIsAlternate,
nsICSSLoaderObserver* aObserver,
nsIPrincipal* aLoaderPrincipal,
nsINode* aRequestingNode);
// Data for loading a sheet linked from an @import rule
SheetLoadData(Loader* aLoader,
nsIURI* aURI,
StyleSheet* aSheet,
SheetLoadData* aParentData,
nsICSSLoaderObserver* aObserver,
nsIPrincipal* aLoaderPrincipal,
nsINode* aRequestingNode);
// Data for loading a non-document sheet
SheetLoadData(Loader* aLoader,
nsIURI* aURI,
StyleSheet* aSheet,
bool aSyncLoad,
bool aUseSystemPrincipal,
const Encoding* aPreloadEncoding,
nsICSSLoaderObserver* aObserver,
nsIPrincipal* aLoaderPrincipal,
nsINode* aRequestingNode);
already_AddRefed<nsIURI> GetReferrerURI();
void ScheduleLoadEventIfNeeded(nsresult aStatus);
NotNull<const Encoding*> DetermineNonBOMEncoding(nsACString const& aSegment,
nsIChannel* aChannel);
nsresult VerifySheetReadyToParse(nsresult aStatus,
const nsACString& aBytes,
nsIChannel* aChannel);
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSITHREADOBSERVER
NS_DECL_NSIUNICHARSTREAMLOADEROBSERVER
// Hold a ref to the CSSLoader so we can call back to it to let it
// know the load finished
RefPtr<Loader> mLoader;
// Title needed to pull datas out of the pending datas table when
// the preferred title is changed
nsString mTitle;
// The encoding we decided to use for the sheet
const Encoding* mEncoding;
// URI we're loading. Null for inline sheets
nsCOMPtr<nsIURI> mURI;
// Should be 1 for non-inline sheets.
uint32_t mLineNumber;
// The sheet we're loading data for
RefPtr<StyleSheet> mSheet;
// Linked list of datas for the same URI as us
SheetLoadData* mNext; // strong ref
// Load data for the sheet that @import-ed us if we were @import-ed
// during the parse
RefPtr<SheetLoadData> mParentData;
// Number of sheets we @import-ed that are still loading
uint32_t mPendingChildren;
// mSyncLoad is true when the load needs to be synchronous -- right
// now only for LoadSheetSync and children of sync loads.
bool mSyncLoad : 1;
// mIsNonDocumentSheet is true if the load was triggered by LoadSheetSync or
// LoadSheet or an @import from such a sheet. Non-document sheet loads can
// proceed even if we have no document.
bool mIsNonDocumentSheet : 1;
// mIsLoading is true from the moment we are placed in the loader's
// "loading datas" table (right after the async channel is opened)
// to the moment we are removed from said table (due to the load
// completing or being cancelled).
bool mIsLoading : 1;
// mIsCancelled is set to true when a sheet load is stopped by
// Stop() or StopLoadingSheet() (which was removed in Bug 556446).
// SheetLoadData::OnStreamComplete() checks this to avoid parsing
// sheets that have been cancelled and such.
bool mIsCancelled : 1;
// mMustNotify is true if the load data is being loaded async and
// the original function call that started the load has returned.
// This applies only to observer notifications; load/error events
// are fired for any SheetLoadData that has a non-null
// mOwningElement.
bool mMustNotify : 1;
// mWasAlternate is true if the sheet was an alternate when the load data was
// created.
bool mWasAlternate : 1;
// mUseSystemPrincipal is true if the system principal should be used for
// this sheet, no matter what the channel principal is. Only true for sync
// loads.
bool mUseSystemPrincipal : 1;
// If true, this SheetLoadData is being used as a way to handle
// async observer notification for an already-complete sheet.
bool mSheetAlreadyComplete : 1;
// This is the element that imported the sheet. Needed to get the
// charset set on it and to fire load/error events.
nsCOMPtr<nsIStyleSheetLinkingElement> mOwningElement;
// The observer that wishes to be notified of load completion
nsCOMPtr<nsICSSLoaderObserver> mObserver;
// The principal that identifies who started loading us.
nsCOMPtr<nsIPrincipal> mLoaderPrincipal;
// The node that identifies who started loading us.
nsCOMPtr<nsINode> mRequestingNode;
// The encoding to use for preloading Must be empty if mOwningElement
// is non-null.
const Encoding* mPreloadEncoding;
// The status our load ended up with; this determines whether we
// should fire error events or load events. This gets initialized
// by ScheduleLoadEventIfNeeded, and is only used after that has
// been called.
MOZ_INIT_OUTSIDE_CTOR nsresult mStatus;
private:
void FireLoadEvent(nsIThreadInternal* aThread);
};
} // namespace css
} // namespace mozilla
#endif // mozilla_css_SheetLoadData_h

View File

@ -0,0 +1,172 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "mozilla/css/StreamLoader.h"
#include "mozilla/IntegerTypeTraits.h"
#include "mozilla/Encoding.h"
#include "nsIChannel.h"
#include "nsIInputStream.h"
using namespace mozilla;
namespace mozilla {
namespace css {
StreamLoader::StreamLoader(mozilla::css::SheetLoadData* aSheetLoadData)
: mSheetLoadData(aSheetLoadData)
, mStatus(NS_OK)
{
MOZ_ASSERT(!aSheetLoadData->mSheet->IsGecko());
}
StreamLoader::~StreamLoader()
{
}
NS_IMPL_ISUPPORTS(StreamLoader, nsIStreamListener)
/* nsIRequestObserver implementation */
NS_IMETHODIMP
StreamLoader::OnStartRequest(nsIRequest* aRequest, nsISupports*)
{
// It's kinda bad to let Web content send a number that results
// in a potentially large allocation directly, but efficiency of
// compression bombs is so great that it doesn't make much sense
// to require a site to send one before going ahead and allocating.
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
if (channel) {
int64_t length;
nsresult rv = channel->GetContentLength(&length);
if (NS_SUCCEEDED(rv) && length > 0) {
if (length > MaxValue<nsACString::size_type>::value) {
return (mStatus = NS_ERROR_OUT_OF_MEMORY);
}
if (!mBytes.SetCapacity(length, mozilla::fallible_t())) {
return (mStatus = NS_ERROR_OUT_OF_MEMORY);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
StreamLoader::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult aStatus)
{
// Decoded data
nsCString utf8String;
// How many bytes of decoded data to skip (3 when skipping UTF-8 BOM needed,
// 0 otherwise)
size_t skip = 0;
const Encoding* encoding;
nsresult rv = NS_OK;
{
// Hold the nsStringBuffer for the bytes from the stack to ensure release
// no matter which return branch is taken.
nsCString bytes(mBytes);
mBytes.Truncate();
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
if (NS_FAILED(mStatus)) {
mSheetLoadData->VerifySheetReadyToParse(mStatus, EmptyCString(), channel);
return mStatus;
}
nsresult rv =
mSheetLoadData->VerifySheetReadyToParse(aStatus, bytes, channel);
if (rv != NS_OK_PARSE_SHEET) {
return rv;
}
rv = NS_OK;
size_t bomLength;
Tie(encoding, bomLength) = Encoding::ForBOM(bytes);
if (!encoding) {
// No BOM
encoding = mSheetLoadData->DetermineNonBOMEncoding(bytes, channel);
rv = encoding->DecodeWithoutBOMHandling(bytes, utf8String);
} else if (encoding == UTF_8_ENCODING) {
// UTF-8 BOM; handling this manually because mozilla::Encoding
// can't handle this without copying with C++ types and uses
// infallible allocation with Rust types (which could avoid
// the copy).
// First, chop off the BOM.
auto tail = Span<const uint8_t>(bytes).From(bomLength);
size_t upTo = Encoding::UTF8ValidUpTo(tail);
if (upTo == tail.Length()) {
// No need to copy
skip = bomLength;
utf8String.Assign(bytes);
} else {
rv = encoding->DecodeWithoutBOMHandling(tail, utf8String, upTo);
}
} else {
// UTF-16LE or UTF-16BE
rv = encoding->DecodeWithBOMRemoval(bytes, utf8String);
}
} // run destructor for `bytes`
if (NS_FAILED(rv)) {
return rv;
}
// For reasons I don't understand, factoring the below lines into
// a method on SheetLoadData resulted in a linker error. Hence,
// accessing fields of mSheetLoadData from here.
mSheetLoadData->mEncoding = encoding;
bool dummy;
return mSheetLoadData->mLoader->ParseSheet(
EmptyString(),
Span<const uint8_t>(utf8String).From(skip),
mSheetLoadData,
dummy);
}
/* nsIStreamListener implementation */
NS_IMETHODIMP
StreamLoader::OnDataAvailable(nsIRequest*,
nsISupports*,
nsIInputStream* aInputStream,
uint64_t,
uint32_t aCount)
{
if (NS_FAILED(mStatus)) {
return mStatus;
}
uint32_t dummy;
return aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy);
}
nsresult
StreamLoader::WriteSegmentFun(nsIInputStream*,
void* aClosure,
const char* aSegment,
uint32_t,
uint32_t aCount,
uint32_t* aWriteCount)
{
StreamLoader* self = static_cast<StreamLoader*>(aClosure);
if (NS_FAILED(self->mStatus)) {
return self->mStatus;
}
if (!self->mBytes.Append(aSegment, aCount, mozilla::fallible_t())) {
self->mBytes.Truncate();
return (self->mStatus = NS_ERROR_OUT_OF_MEMORY);
}
*aWriteCount = aCount;
return NS_OK;
}
} // namespace css
} // namespace mozilla

View File

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef mozilla_css_StreamLoader_h
#define mozilla_css_StreamLoader_h
#include "nsString.h"
#include "mozilla/css/SheetLoadData.h"
class nsIInputStream;
namespace mozilla {
namespace css {
class StreamLoader : public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
explicit StreamLoader(mozilla::css::SheetLoadData* aSheetLoadData);
private:
virtual ~StreamLoader();
/**
* callback method used for ReadSegments
*/
static nsresult WriteSegmentFun(nsIInputStream*,
void*,
const char*,
uint32_t,
uint32_t,
uint32_t*);
RefPtr<mozilla::css::SheetLoadData> mSheetLoadData;
nsCString mBytes;
nsresult mStatus;
};
} // namespace css
} // namespace mozilla
#endif // mozilla_css_StreamLoader_h

View File

@ -170,7 +170,9 @@ EXPORTS.mozilla.css += [
'Loader.h',
'NameSpaceRule.h',
'Rule.h',
'SheetLoadData.h',
'SheetParsingMode.h',
'StreamLoader.h',
'StyleRule.h',
'URLMatchingFunction.h',
]
@ -266,6 +268,7 @@ UNIFIED_SOURCES += [
'ServoStyleSet.cpp',
'ServoStyleSheet.cpp',
'ServoSupportsRule.cpp',
'StreamLoader.cpp',
'StyleAnimationValue.cpp',
'StylePrefs.cpp',
'StyleRule.cpp',

View File

@ -907,7 +907,7 @@ nsLayoutStylesheetCache::BuildPreferenceSheet(RefPtr<StyleSheet>* aSheet,
static const uint32_t kPreallocSize = 1024;
nsString sheetText;
nsCString sheetText;
sheetText.SetCapacity(kPreallocSize);
#define NS_GET_R_G_B(color_) \
@ -990,13 +990,12 @@ nsLayoutStylesheetCache::BuildPreferenceSheet(RefPtr<StyleSheet>* aSheet,
"sheet without reallocation");
if (sheet->IsGecko()) {
sheet->AsGecko()->ReparseSheet(sheetText);
sheet->AsGecko()->ReparseSheet(NS_ConvertUTF8toUTF16(sheetText));
} else {
ServoStyleSheet* servoSheet = sheet->AsServo();
// NB: The pref sheet never has @import rules.
nsresult rv =
servoSheet->ParseSheet(nullptr, sheetText, uri, uri, nullptr, 0,
eCompatibility_FullStandards);
nsresult rv = servoSheet->ParseSheet(
nullptr, sheetText, uri, uri, nullptr, 0, eCompatibility_FullStandards);
// Parsing the about:PreferenceStyleSheet URI can only fail on OOM. If we
// are OOM before we parsed any documents we might as well abort.
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));

View File

@ -10,6 +10,7 @@
#include "ServoBindings.h"
#include "NullPrincipalURI.h"
#include "nsCSSParser.h"
#include "mozilla/Encoding.h"
using namespace mozilla;
using namespace mozilla::css;
@ -23,18 +24,23 @@ using namespace mozilla::net;
#ifdef MOZ_STYLO
static void ServoParsingBench() {
NS_NAMED_LITERAL_CSTRING(css_, EXAMPLE_STYLESHEET);
const nsACString& css = css_;
ASSERT_TRUE(IsUTF8(css));
auto css = AsBytes(MakeStringSpan(EXAMPLE_STYLESHEET));
ASSERT_EQ(Encoding::UTF8ValidUpTo(css), css.Length());
RefPtr<URLExtraData> data = new URLExtraData(
NullPrincipalURI::Create(), nullptr, NullPrincipal::Create());
for (int i = 0; i < PARSING_REPETITIONS; i++) {
RefPtr<RawServoStyleSheetContents> stylesheet =
Servo_StyleSheet_FromUTF8Bytes(
nullptr, nullptr, &css, eAuthorSheetFeatures,
data, 0, eCompatibility_FullStandards, nullptr
).Consume();
Servo_StyleSheet_FromUTF8Bytes(nullptr,
nullptr,
css.Elements(),
css.Length(),
eAuthorSheetFeatures,
data,
0,
eCompatibility_FullStandards,
nullptr)
.Consume();
}
}

View File

@ -984,7 +984,10 @@ nsHtml5TreeOpExecutor::PreloadStyle(const nsAString& aURL,
referrerPolicy = styleReferrerPolicy;
}
mDocument->PreloadStyle(uri, aCharset, aCrossOrigin, referrerPolicy,
mDocument->PreloadStyle(uri,
Encoding::ForLabel(aCharset),
aCrossOrigin,
referrerPolicy,
aIntegrity);
}

View File

@ -253,7 +253,7 @@ with modules["NETWORK"]:
# The async request completed successfully.
errors["NS_BINDING_SUCCEEDED"] = errors["NS_OK"]
# The async request failed for some unknown reason.
# The async request failed for some unknown reason.
errors["NS_BINDING_FAILED"] = FAILURE(1)
# The async request failed because it was aborted by some user action.
errors["NS_BINDING_ABORTED"] = FAILURE(2)
@ -314,7 +314,7 @@ with modules["NETWORK"]:
# The connection attempt failed, for example, because no server was
# listening at specified host:port.
errors["NS_ERROR_CONNECTION_REFUSED"] = FAILURE(13)
# The connection was lost due to a timeout error.
# The connection was lost due to a timeout error.
errors["NS_ERROR_NET_TIMEOUT"] = FAILURE(14)
# The requested action could not be completed while the networking library
# is in the offline state.
@ -339,7 +339,7 @@ with modules["NETWORK"]:
# This request is not resumable, but it was tried to resume it, or to
# request resume-specific data.
errors["NS_ERROR_NOT_RESUMABLE"] = FAILURE(25)
# The request failed as a result of a detected redirection loop.
# The request failed as a result of a detected redirection loop.
errors["NS_ERROR_REDIRECT_LOOP"] = FAILURE(31)
# It was attempted to resume the request, but the entity has changed in the
# meantime.
@ -474,6 +474,8 @@ with modules["PLUGINS"]:
with modules["LAYOUT"]:
# Return code for nsITableLayout
errors["NS_TABLELAYOUT_CELL_NOT_FOUND"] = SUCCESS(0)
# Return code for SheetLoadData::VerifySheetReadyToParse
errors["NS_OK_PARSE_SHEET"] = SUCCESS(1)
# Return code for nsFrame::GetNextPrevLineFromeBlockFrame
errors["NS_POSITION_BEFORE_TABLE"] = SUCCESS(3)
# Return codes for nsPresState::GetProperty()