diff --git a/dom/base/BodyUtil.cpp b/dom/base/BodyUtil.cpp new file mode 100644 index 0000000000..572d17100e --- /dev/null +++ b/dom/base/BodyUtil.cpp @@ -0,0 +1,574 @@ +/* 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 "BodyUtil.h" + +#include "nsError.h" +#include "nsString.h" +#include "nsIGlobalObject.h" +#include "nsIUnicodeDecoder.h" + +#include "nsCharSeparatedTokenizer.h" +#include "nsDOMString.h" +#include "nsNetUtil.h" +#include "nsReadableUtils.h" +#include "nsStreamUtils.h" +#include "nsStringStream.h" + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/EncodingUtils.h" +#include "mozilla/dom/Exceptions.h" +#include "mozilla/dom/FetchUtil.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/FormData.h" +#include "mozilla/dom/Headers.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/URLSearchParams.h" + +namespace mozilla { +namespace dom { + +namespace { + +class StreamDecoder final +{ + nsCOMPtr mDecoder; + nsString mDecoded; + +public: + StreamDecoder() + : mDecoder(EncodingUtils::DecoderForEncoding("UTF-8")) + { + MOZ_ASSERT(mDecoder); + } + + nsresult + AppendText(const char* aSrcBuffer, uint32_t aSrcBufferLen) + { + int32_t destBufferLen; + nsresult rv = + mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen, &destBufferLen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!mDecoded.SetCapacity(mDecoded.Length() + destBufferLen, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + char16_t* destBuffer = mDecoded.BeginWriting() + mDecoded.Length(); + int32_t totalChars = mDecoded.Length(); + + int32_t srcLen = (int32_t) aSrcBufferLen; + int32_t outLen = destBufferLen; + rv = mDecoder->Convert(aSrcBuffer, &srcLen, destBuffer, &outLen); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + totalChars += outLen; + mDecoded.SetLength(totalChars); + + return NS_OK; + } + + nsString& + GetText() + { + return mDecoded; + } +}; + +// Reads over a CRLF and positions start after it. +static bool +PushOverLine(nsACString::const_iterator& aStart) +{ + if (*aStart == nsCRT::CR && (aStart.size_forward() > 1) && *(++aStart) == nsCRT::LF) { + ++aStart; // advance to after CRLF + return true; + } + + return false; +} + +class MOZ_STACK_CLASS FillFormIterator final + : public URLSearchParams::ForEachIterator +{ +public: + explicit FillFormIterator(FormData* aFormData) + : mFormData(aFormData) + { + MOZ_ASSERT(aFormData); + } + + bool URLParamsIterator(const nsString& aName, + const nsString& aValue) override + { + ErrorResult rv; + mFormData->Append(aName, aValue, rv); + MOZ_ASSERT(!rv.Failed()); + return true; + } + +private: + FormData* mFormData; +}; + +/** + * A simple multipart/form-data parser as defined in RFC 2388 and RFC 2046. + * This does not respect any encoding specified per entry, using UTF-8 + * throughout. This is as the Fetch spec states in the consume body algorithm. + * Borrows some things from Necko's nsMultiMixedConv, but is simpler since + * unlike Necko we do not have to deal with receiving incomplete chunks of data. + * + * This parser will fail the entire parse on any invalid entry, so it will + * never return a partially filled FormData. + * The content-disposition header is used to figure out the name and filename + * entries. The inclusion of the filename parameter decides if the entry is + * inserted into the FormData as a string or a File. + * + * File blobs are copies of the underlying data string since we cannot adopt + * char* chunks embedded within the larger body without significant effort. + * FIXME(nsm): Bug 1127552 - We should add telemetry to calls to formData() and + * friends to figure out if Fetch ends up copying big blobs to see if this is + * worth optimizing. + */ +class MOZ_STACK_CLASS FormDataParser +{ +private: + RefPtr mFormData; + nsCString mMimeType; + nsCString mData; + + // Entry state, reset in START_PART. + nsCString mName; + nsCString mFilename; + nsCString mContentType; + + enum + { + START_PART, + PARSE_HEADER, + PARSE_BODY, + } mState; + + nsIGlobalObject* mParentObject; + + // Reads over a boundary and sets start to the position after the end of the + // boundary. Returns false if no boundary is found immediately. + bool + PushOverBoundary(const nsACString& aBoundaryString, + nsACString::const_iterator& aStart, + nsACString::const_iterator& aEnd) + { + // We copy the end iterator to keep the original pointing to the real end + // of the string. + nsACString::const_iterator end(aEnd); + const char* beginning = aStart.get(); + if (FindInReadable(aBoundaryString, aStart, end)) { + // We either should find the body immediately, or after 2 chars with the + // 2 chars being '-', everything else is failure. + if ((aStart.get() - beginning) == 0) { + aStart.advance(aBoundaryString.Length()); + return true; + } + + if ((aStart.get() - beginning) == 2) { + if (*(--aStart) == '-' && *(--aStart) == '-') { + aStart.advance(aBoundaryString.Length() + 2); + return true; + } + } + } + + return false; + } + + bool + ParseHeader(nsACString::const_iterator& aStart, + nsACString::const_iterator& aEnd, + bool* aWasEmptyHeader) + { + nsAutoCString headerName, headerValue; + if (!FetchUtil::ExtractHeader(aStart, aEnd, + headerName, headerValue, + aWasEmptyHeader)) { + return false; + } + if (*aWasEmptyHeader) { + return true; + } + + if (headerName.LowerCaseEqualsLiteral("content-disposition")) { + nsCCharSeparatedTokenizer tokenizer(headerValue, ';'); + bool seenFormData = false; + while (tokenizer.hasMoreTokens()) { + const nsDependentCSubstring& token = tokenizer.nextToken(); + if (token.IsEmpty()) { + continue; + } + + if (token.EqualsLiteral("form-data")) { + seenFormData = true; + continue; + } + + if (seenFormData && + StringBeginsWith(token, NS_LITERAL_CSTRING("name="))) { + mName = StringTail(token, token.Length() - 5); + mName.Trim(" \""); + continue; + } + + if (seenFormData && + StringBeginsWith(token, NS_LITERAL_CSTRING("filename="))) { + mFilename = StringTail(token, token.Length() - 9); + mFilename.Trim(" \""); + continue; + } + } + + if (mName.IsVoid()) { + // Could not parse a valid entry name. + return false; + } + } else if (headerName.LowerCaseEqualsLiteral("content-type")) { + mContentType = headerValue; + } + + return true; + } + + // The end of a body is marked by a CRLF followed by the boundary. So the + // CRLF is part of the boundary and not the body, but any prior CRLFs are + // part of the body. This will position the iterator at the beginning of the + // boundary (after the CRLF). + bool + ParseBody(const nsACString& aBoundaryString, + nsACString::const_iterator& aStart, + nsACString::const_iterator& aEnd) + { + const char* beginning = aStart.get(); + + // Find the boundary marking the end of the body. + nsACString::const_iterator end(aEnd); + if (!FindInReadable(aBoundaryString, aStart, end)) { + return false; + } + + // We found a boundary, strip the just prior CRLF, and consider + // everything else the body section. + if (aStart.get() - beginning < 2) { + // Only the first entry can have a boundary right at the beginning. Even + // an empty body will have a CRLF before the boundary. So this is + // a failure. + return false; + } + + // Check that there is a CRLF right before the boundary. + aStart.advance(-2); + + // Skip optional hyphens. + if (*aStart == '-' && *(aStart.get()+1) == '-') { + if (aStart.get() - beginning < 2) { + return false; + } + + aStart.advance(-2); + } + + if (*aStart != nsCRT::CR || *(aStart.get()+1) != nsCRT::LF) { + return false; + } + + nsAutoCString body(beginning, aStart.get() - beginning); + + // Restore iterator to after the \r\n as we promised. + // We do not need to handle the extra hyphens case since our boundary + // parser in PushOverBoundary() + aStart.advance(2); + + if (!mFormData) { + mFormData = new FormData(); + } + + NS_ConvertUTF8toUTF16 name(mName); + + if (mFilename.IsVoid()) { + ErrorResult rv; + mFormData->Append(name, NS_ConvertUTF8toUTF16(body), rv); + MOZ_ASSERT(!rv.Failed()); + } else { + // Unfortunately we've to copy the data first since all our strings are + // going to free it. We also need fallible alloc, so we can't just use + // ToNewCString(). + char* copy = static_cast(moz_xmalloc(body.Length())); + if (!copy) { + NS_WARNING("Failed to copy File entry body."); + return false; + } + nsCString::const_iterator bodyIter, bodyEnd; + body.BeginReading(bodyIter); + body.EndReading(bodyEnd); + char *p = copy; + while (bodyIter != bodyEnd) { + *p++ = *bodyIter++; + } + p = nullptr; + + RefPtr file = + File::CreateMemoryFile(mParentObject, + reinterpret_cast(copy), body.Length(), + NS_ConvertUTF8toUTF16(mFilename), + NS_ConvertUTF8toUTF16(mContentType), /* aLastModifiedDate */ 0); + Optional dummy; + ErrorResult rv; + mFormData->Append(name, *file, dummy, rv); + if (NS_WARN_IF(rv.Failed())) { + return false; + } + } + + return true; + } + +public: + FormDataParser(const nsACString& aMimeType, const nsACString& aData, nsIGlobalObject* aParent) + : mMimeType(aMimeType), mData(aData), mState(START_PART), mParentObject(aParent) + { + } + + bool + Parse() + { + // Determine boundary from mimetype. + const char* boundaryId = nullptr; + boundaryId = strstr(mMimeType.BeginWriting(), "boundary"); + if (!boundaryId) { + return false; + } + + boundaryId = strchr(boundaryId, '='); + if (!boundaryId) { + return false; + } + + // Skip over '='. + boundaryId++; + + char *attrib = (char *) strchr(boundaryId, ';'); + if (attrib) *attrib = '\0'; + + nsAutoCString boundaryString(boundaryId); + if (attrib) *attrib = ';'; + + boundaryString.Trim(" \""); + + if (boundaryString.Length() == 0) { + return false; + } + + nsACString::const_iterator start, end; + mData.BeginReading(start); + // This should ALWAYS point to the end of data. + // Helpers make copies. + mData.EndReading(end); + + while (start != end) { + switch(mState) { + case START_PART: + mName.SetIsVoid(true); + mFilename.SetIsVoid(true); + mContentType = NS_LITERAL_CSTRING("text/plain"); + + // MUST start with boundary. + if (!PushOverBoundary(boundaryString, start, end)) { + return false; + } + + if (start != end && *start == '-') { + // End of data. + if (!mFormData) { + mFormData = new FormData(); + } + return true; + } + + if (!PushOverLine(start)) { + return false; + } + mState = PARSE_HEADER; + break; + + case PARSE_HEADER: + bool emptyHeader; + if (!ParseHeader(start, end, &emptyHeader)) { + return false; + } + + if (emptyHeader && !PushOverLine(start)) { + return false; + } + + mState = emptyHeader ? PARSE_BODY : PARSE_HEADER; + break; + + case PARSE_BODY: + if (mName.IsVoid()) { + NS_WARNING("No content-disposition header with a valid name was " + "found. Failing at body parse."); + return false; + } + + if (!ParseBody(boundaryString, start, end)) { + return false; + } + + mState = START_PART; + break; + + default: + MOZ_CRASH("Invalid case"); + } + } + + NS_NOTREACHED("Should never reach here."); + return false; + } + + already_AddRefed GetFormData() + { + return mFormData.forget(); + } +}; +} + +// static +void +BodyUtil::ConsumeArrayBuffer(JSContext* aCx, + JS::MutableHandle aValue, + uint32_t aInputLength, uint8_t* aInput, + ErrorResult& aRv) +{ + JS::Rooted arrayBuffer(aCx); + arrayBuffer = JS_NewArrayBufferWithContents(aCx, aInputLength, + reinterpret_cast(aInput)); + if (!arrayBuffer) { + JS_ClearPendingException(aCx); + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + aValue.set(arrayBuffer); +} + +// static +already_AddRefed +BodyUtil::ConsumeBlob(nsISupports* aParent, const nsString& aMimeType, + uint32_t aInputLength, uint8_t* aInput, + ErrorResult& aRv) +{ + RefPtr blob = + Blob::CreateMemoryBlob(aParent, + reinterpret_cast(aInput), aInputLength, + aMimeType); + + if (!blob) { + aRv.Throw(NS_ERROR_DOM_UNKNOWN_ERR); + return nullptr; + } + return blob.forget(); +} + +// static +already_AddRefed +BodyUtil::ConsumeFormData(nsIGlobalObject* aParent, const nsCString& aMimeType, + const nsCString& aStr, ErrorResult& aRv) +{ + NS_NAMED_LITERAL_CSTRING(formDataMimeType, "multipart/form-data"); + + // Allow semicolon separated boundary/encoding suffix like multipart/form-data; boundary= + // but disallow multipart/form-datafoobar. + bool isValidFormDataMimeType = StringBeginsWith(aMimeType, formDataMimeType); + + if (isValidFormDataMimeType && aMimeType.Length() > formDataMimeType.Length()) { + isValidFormDataMimeType = aMimeType[formDataMimeType.Length()] == ';'; + } + + if (isValidFormDataMimeType) { + FormDataParser parser(aMimeType, aStr, aParent); + if (!parser.Parse()) { + aRv.ThrowTypeError(); + return nullptr; + } + + RefPtr fd = parser.GetFormData(); + MOZ_ASSERT(fd); + return fd.forget(); + } + + NS_NAMED_LITERAL_CSTRING(urlDataMimeType, "application/x-www-form-urlencoded"); + bool isValidUrlEncodedMimeType = StringBeginsWith(aMimeType, urlDataMimeType); + + if (isValidUrlEncodedMimeType && aMimeType.Length() > urlDataMimeType.Length()) { + isValidUrlEncodedMimeType = aMimeType[urlDataMimeType.Length()] == ';'; + } + + if (isValidUrlEncodedMimeType) { + URLParams params; + params.ParseInput(aStr); + + RefPtr fd = new FormData(aParent); + FillFormIterator iterator(fd); + DebugOnly status = params.ForEach(iterator); + MOZ_ASSERT(status); + + return fd.forget(); + } + + aRv.ThrowTypeError(); + return nullptr; +} + +// static +nsresult +BodyUtil::ConsumeText(uint32_t aInputLength, uint8_t* aInput, + nsString& aText) +{ + StreamDecoder decoder; + nsresult rv = decoder.AppendText(reinterpret_cast(aInput), + aInputLength); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + aText = decoder.GetText(); + return NS_OK; +} + +// static +void +BodyUtil::ConsumeJson(JSContext* aCx, JS::MutableHandle aValue, + const nsString& aStr, ErrorResult& aRv) +{ + aRv.MightThrowJSException(); + + AutoForceSetExceptionOnContext forceExn(aCx); + JS::Rooted json(aCx); + if (!JS_ParseJSON(aCx, aStr.get(), aStr.Length(), &json)) { + if (!JS_IsExceptionPending(aCx)) { + aRv.Throw(NS_ERROR_DOM_UNKNOWN_ERR); + return; + } + + JS::Rooted exn(aCx); + DebugOnly gotException = JS_GetPendingException(aCx, &exn); + MOZ_ASSERT(gotException); + + JS_ClearPendingException(aCx); + aRv.ThrowJSException(aCx, exn); + return; + } + + aValue.set(json); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/base/BodyUtil.h b/dom/base/BodyUtil.h new file mode 100644 index 0000000000..964720b04a --- /dev/null +++ b/dom/base/BodyUtil.h @@ -0,0 +1,68 @@ +/* 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_dom_BodyUtil_h +#define mozilla_dom_BodyUtil_h + +#include "nsString.h" +#include "nsError.h" + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/FormData.h" + +namespace mozilla { +namespace dom { + +class BodyUtil final +{ +private: + BodyUtil() = delete; + +public: + /** + * Creates an array buffer from an array, assigning the result to |aValue|. + * The array buffer takes ownership of |aInput|, which must be allocated + * by |malloc|. + */ + static void + ConsumeArrayBuffer(JSContext* aCx, JS::MutableHandle aValue, + uint32_t aInputLength, uint8_t* aInput, ErrorResult& aRv); + + /** + * Creates an in-memory blob from an array. The blob takes ownership of + * |aInput|, which must be allocated by |malloc|. + */ + static already_AddRefed + ConsumeBlob(nsISupports* aParent, const nsString& aMimeType, + uint32_t aInputLength, uint8_t* aInput, ErrorResult& aRv); + + /** + * Creates a form data object from a UTF-8 encoded |aStr|. Returns |nullptr| + * and sets |aRv| to MSG_BAD_FORMDATA if |aStr| contains invalid data. + */ + static already_AddRefed + ConsumeFormData(nsIGlobalObject* aParent, const nsCString& aMimeType, + const nsCString& aStr, ErrorResult& aRv); + + /** + * UTF-8 decodes |aInput| into |aText|. The caller may free |aInput| + * once this method returns. + */ + static nsresult + ConsumeText(uint32_t aInputLength, uint8_t* aInput, nsString& aText); + + /** + * Parses a UTF-8 encoded |aStr| as JSON, assigning the result to |aValue|. + * Sets |aRv| to a syntax error if |aStr| contains invalid data. + */ + static void + ConsumeJson(JSContext* aCx, JS::MutableHandle aValue, + const nsString& aStr, ErrorResult& aRv); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_BodyUtil_h diff --git a/dom/base/DOMMatrix.h b/dom/base/DOMMatrix.h index c8e3750aab..db5d03f8af 100644 --- a/dom/base/DOMMatrix.h +++ b/dom/base/DOMMatrix.h @@ -22,6 +22,8 @@ namespace dom { class GlobalObject; class DOMMatrix; +class DOMPoint; +struct DOMPointInit; class DOMMatrixReadOnly : public nsWrapperCache { diff --git a/dom/base/EventSource.cpp b/dom/base/EventSource.cpp index 6b2139ca18..183065d721 100644 --- a/dom/base/EventSource.cpp +++ b/dom/base/EventSource.cpp @@ -190,6 +190,7 @@ EventSource::Init(nsISupports* aOwner, nsCOMPtr sgo = do_QueryInterface(aOwner); NS_ENSURE_STATE(sgo); + // XXXbz why are we checking this? This doesn't match anything in the spec. nsCOMPtr scriptContext = sgo->GetContext(); NS_ENSURE_STATE(scriptContext); @@ -212,19 +213,13 @@ EventSource::Init(nsISupports* aOwner, // Get the load group for the page. When requesting we'll add ourselves to it. // This way any pending requests will be automatically aborted if the user // leaves the page. - nsresult rv; - nsIScriptContext* sc = GetContextForEventHandlers(&rv); - if (sc) { - nsCOMPtr doc = - nsContentUtils::GetDocumentFromScriptContext(sc); - if (doc) { - mLoadGroup = doc->GetDocumentLoadGroup(); - } + nsCOMPtr doc = GetDocumentIfCurrent(); + if (doc) { + mLoadGroup = doc->GetDocumentLoadGroup(); } - // get the src nsCOMPtr baseURI; - rv = GetBaseURI(getter_AddRefs(baseURI)); + nsresult rv = GetBaseURI(getter_AddRefs(baseURI)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr srcURI; @@ -525,8 +520,9 @@ EventSource::AsyncOnChannelRedirect(nsIChannel *aOldChannel, mHttpChannel = do_QueryInterface(aNewChannel); NS_ENSURE_STATE(mHttpChannel); - rv = SetupHttpChannel(); - NS_ENSURE_SUCCESS(rv, rv); + SetupHttpChannel(); + // The HTTP impl already copies over the referrer and referrer policy on + // redirects, so we don't need to SetupReferrerPolicy(). if ((aFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) { rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc)); @@ -592,17 +588,14 @@ EventSource::GetBaseURI(nsIURI **aBaseURI) nsCOMPtr baseURI; // first we try from document->GetBaseURI() - nsresult rv; - nsIScriptContext* sc = GetContextForEventHandlers(&rv); - nsCOMPtr doc = - nsContentUtils::GetDocumentFromScriptContext(sc); + nsCOMPtr doc = GetDocumentIfCurrent(); if (doc) { baseURI = doc->GetBaseURI(); } // otherwise we get from the doc's principal if (!baseURI) { - rv = mPrincipal->GetURI(getter_AddRefs(baseURI)); + nsresult rv = mPrincipal->GetURI(getter_AddRefs(baseURI)); NS_ENSURE_SUCCESS(rv, rv); } @@ -612,18 +605,7 @@ EventSource::GetBaseURI(nsIURI **aBaseURI) return NS_OK; } -net::ReferrerPolicy -EventSource::GetReferrerPolicy() -{ - nsresult rv; - nsIScriptContext* sc = GetContextForEventHandlers(&rv); - NS_ENSURE_SUCCESS(rv, mozilla::net::RP_Default); - - nsCOMPtr doc = nsContentUtils::GetDocumentFromScriptContext(sc); - return doc ? doc->GetReferrerPolicy() : mozilla::net::RP_Default; -} - -nsresult +void EventSource::SetupHttpChannel() { mHttpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET")); @@ -639,11 +621,15 @@ EventSource::SetupHttpChannel() mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Last-Event-ID"), NS_ConvertUTF16toUTF8(mLastEventID), false); } +} - nsCOMPtr codebase; - nsresult rv = GetBaseURI(getter_AddRefs(codebase)); - if (NS_SUCCEEDED(rv)) { - rv = mHttpChannel->SetReferrerWithPolicy(codebase, this->GetReferrerPolicy()); +nsresult +EventSource::SetupReferrerPolicy() +{ + nsCOMPtr doc = GetDocumentIfCurrent(); + if (doc) { + nsresult rv = mHttpChannel->SetReferrerWithPolicy(doc->GetDocumentURI(), + doc->GetReferrerPolicy()); NS_ENSURE_SUCCESS(rv, rv); } @@ -670,9 +656,7 @@ EventSource::InitChannelAndRequestEventSource() nsLoadFlags loadFlags; loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE; - nsIScriptContext* sc = GetContextForEventHandlers(&rv); - nsCOMPtr doc = - nsContentUtils::GetDocumentFromScriptContext(sc); + nsCOMPtr doc = GetDocumentIfCurrent(); nsSecurityFlags securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; @@ -709,7 +693,8 @@ EventSource::InitChannelAndRequestEventSource() mHttpChannel = do_QueryInterface(channel); NS_ENSURE_TRUE(mHttpChannel, NS_ERROR_NO_INTERFACE); - rv = SetupHttpChannel(); + SetupHttpChannel(); + rv = SetupReferrerPolicy(); NS_ENSURE_SUCCESS(rv, rv); #ifdef DEBUG diff --git a/dom/base/EventSource.h b/dom/base/EventSource.h index ea1fe10aa8..ef93b1e2d6 100644 --- a/dom/base/EventSource.h +++ b/dom/base/EventSource.h @@ -107,9 +107,8 @@ protected: nsresult GetBaseURI(nsIURI **aBaseURI); - net::ReferrerPolicy GetReferrerPolicy(); - - nsresult SetupHttpChannel(); + void SetupHttpChannel(); + nsresult SetupReferrerPolicy(); nsresult InitChannelAndRequestEventSource(); nsresult ResetConnection(); nsresult DispatchFailConnection(); diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 87a62a91d7..7eb5b9acfb 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -1182,10 +1182,6 @@ FragmentOrElement::DestroyContent() nsBindingManager::eRunDtor); document->ClearBoxObjectFor(this); - // XXX We really should let cycle collection do this, but that currently still - // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684). - ReleaseWrapper(this); - uint32_t i, count = mAttrsAndChildren.ChildCount(); for (i = 0; i < count; ++i) { // The child can remove itself from the parent in BindToTree. diff --git a/dom/base/PostMessageEvent.cpp b/dom/base/PostMessageEvent.cpp index 042dce7ec2..7e6145661c 100644 --- a/dom/base/PostMessageEvent.cpp +++ b/dom/base/PostMessageEvent.cpp @@ -15,11 +15,15 @@ #include "mozilla/dom/MessagePortBinding.h" #include "mozilla/dom/PMessagePort.h" #include "mozilla/dom/StructuredCloneTags.h" +#include "mozilla/dom/UnionConversions.h" #include "mozilla/EventDispatcher.h" +#include "nsContentUtils.h" #include "nsGlobalWindow.h" #include "nsIPresShell.h" #include "nsIPrincipal.h" +#include "nsIScriptError.h" #include "nsPresContext.h" +#include "nsQueryObject.h" namespace mozilla { namespace dom { diff --git a/dom/base/PostMessageEvent.h b/dom/base/PostMessageEvent.h index 54e1a29d6e..96b4f3768e 100644 --- a/dom/base/PostMessageEvent.h +++ b/dom/base/PostMessageEvent.h @@ -14,6 +14,7 @@ #include "nsThreadUtils.h" class nsGlobalWindow; +class nsIDocument; class nsIPrincipal; class nsPIDOMWindow; diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index 7e4070bd56..e12cc9b8b0 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -396,10 +396,6 @@ StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx, } if (aTag == SCTAG_DOM_WEBCRYPTO_KEY) { - if (!NS_IsMainThread()) { - return nullptr; - } - nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)); if (!global) { return nullptr; @@ -512,7 +508,6 @@ StructuredCloneHolder::WriteFullySerializableObjects(JSContext* aCx, { CryptoKey* key = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(CryptoKey, aObj, key))) { - MOZ_ASSERT(NS_IsMainThread()); return JS_WriteUint32Pair(aWriter, SCTAG_DOM_WEBCRYPTO_KEY, 0) && key->WriteStructuredClone(aWriter); } diff --git a/dom/base/SubtleCrypto.cpp b/dom/base/SubtleCrypto.cpp index debce4ce65..1b97529a1b 100644 --- a/dom/base/SubtleCrypto.cpp +++ b/dom/base/SubtleCrypto.cpp @@ -104,8 +104,8 @@ SubtleCrypto::ImportKey(JSContext* cx, const Sequence& keyUsages, ErrorResult& aRv) { - SUBTLECRYPTO_METHOD_BODY(ImportKey, aRv, cx, format, keyData, algorithm, - extractable, keyUsages) + SUBTLECRYPTO_METHOD_BODY(ImportKey, aRv, mParent, cx, format, keyData, + algorithm, extractable, keyUsages) } already_AddRefed @@ -121,7 +121,8 @@ SubtleCrypto::GenerateKey(JSContext* cx, const ObjectOrString& algorithm, bool extractable, const Sequence& keyUsages, ErrorResult& aRv) { - SUBTLECRYPTO_METHOD_BODY(GenerateKey, aRv, cx, algorithm, extractable, keyUsages) + SUBTLECRYPTO_METHOD_BODY(GenerateKey, aRv, mParent, cx, algorithm, + extractable, keyUsages) } already_AddRefed @@ -132,7 +133,7 @@ SubtleCrypto::DeriveKey(JSContext* cx, bool extractable, const Sequence& keyUsages, ErrorResult& aRv) { - SUBTLECRYPTO_METHOD_BODY(DeriveKey, aRv, cx, algorithm, baseKey, + SUBTLECRYPTO_METHOD_BODY(DeriveKey, aRv, mParent, cx, algorithm, baseKey, derivedKeyType, extractable, keyUsages) } @@ -168,9 +169,9 @@ SubtleCrypto::UnwrapKey(JSContext* cx, const Sequence& keyUsages, ErrorResult& aRv) { - SUBTLECRYPTO_METHOD_BODY(UnwrapKey, aRv, cx, format, wrappedKey, unwrappingKey, - unwrapAlgorithm, unwrappedKeyAlgorithm, - extractable, keyUsages) + SUBTLECRYPTO_METHOD_BODY(UnwrapKey, aRv, mParent, cx, format, wrappedKey, + unwrappingKey, unwrapAlgorithm, + unwrappedKeyAlgorithm, extractable, keyUsages) } } // namespace dom diff --git a/dom/base/WebKitCSSMatrix.cpp b/dom/base/WebKitCSSMatrix.cpp index b5baf25a0a..2cb6d6bf09 100644 --- a/dom/base/WebKitCSSMatrix.cpp +++ b/dom/base/WebKitCSSMatrix.cpp @@ -8,8 +8,10 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/WebKitCSSMatrixBinding.h" +#include "mozilla/Preferences.h" #include "nsCSSParser.h" #include "nsStyleTransformMatrix.h" +#include "RuleNodeCacheConditions.h" namespace mozilla { namespace dom { diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index 2885280832..14fc2e1fbc 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -894,19 +894,17 @@ WebSocketImpl::GetInterface(const nsIID& aIID, void** aResult) if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) || aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) { - nsresult rv; - nsIScriptContext* sc = mWebSocket->GetContextForEventHandlers(&rv); - nsCOMPtr doc = - nsContentUtils::GetDocumentFromScriptContext(sc); - if (!doc) { + nsCOMPtr win = mWebSocket->GetWindowIfCurrent(); + if (!win) { return NS_ERROR_NOT_AVAILABLE; } + nsresult rv; nsCOMPtr wwatch = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr outerWindow = doc->GetWindow(); + nsCOMPtr outerWindow = win->GetOuterWindow(); return wwatch->GetPrompt(outerWindow, aIID, aResult); } @@ -1499,16 +1497,6 @@ WebSocketImpl::Init(JSContext* aCx, return; } - nsIScriptContext* sc = nullptr; - { - nsresult rv; - sc = mWebSocket->GetContextForEventHandlers(&rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - aRv.Throw(rv); - return; - } - } - nsCOMPtr uri; { nsresult rv = NS_NewURI(getter_AddRefs(uri), mURI); @@ -1522,7 +1510,15 @@ WebSocketImpl::Init(JSContext* aCx, // Check content policy. int16_t shouldLoad = nsIContentPolicy::ACCEPT; - nsCOMPtr originDoc = nsContentUtils::GetDocumentFromScriptContext(sc); + nsCOMPtr originDoc = mWebSocket->GetDocumentIfCurrent(); + if (!originDoc) { + nsresult rv = mWebSocket->CheckInnerWindowCorrectness(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + } + mOriginDocument = do_GetWeakReference(originDoc); aRv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET, uri, @@ -2670,11 +2666,7 @@ WebSocketImpl::GetLoadGroup(nsILoadGroup** aLoadGroup) *aLoadGroup = nullptr; if (mIsMainThread) { - nsresult rv; - nsIScriptContext* sc = mWebSocket->GetContextForEventHandlers(&rv); - nsCOMPtr doc = - nsContentUtils::GetDocumentFromScriptContext(sc); - + nsCOMPtr doc = mWebSocket->GetDocumentIfCurrent(); if (doc) { *aLoadGroup = doc->GetDocumentLoadGroup().take(); } diff --git a/dom/base/crashtests/1251361.html b/dom/base/crashtests/1251361.html new file mode 100644 index 0000000000..57c76121f5 --- /dev/null +++ b/dom/base/crashtests/1251361.html @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index 09ca168a74..6799f3f6c0 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -204,3 +204,4 @@ HTTP(..) load xhr_abortinprogress.html load xhr_empty_datauri.html load xhr_html_nullresponse.html load 1230422.html +load 1251361.html diff --git a/dom/base/moz.build b/dom/base/moz.build index 039f513920..b355c9f028 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -153,6 +153,7 @@ EXPORTS.mozilla.dom += [ 'Attr.h', 'BarProps.h', 'BlobSet.h', + 'BodyUtil.h', 'ChildIterator.h', 'ChromeNodeList.h', 'ChromeUtils.h', @@ -220,6 +221,7 @@ UNIFIED_SOURCES += [ 'AnonymousContent.cpp', 'Attr.cpp', 'BarProps.cpp', + 'BodyUtil.cpp', 'ChildIterator.cpp', 'ChromeNodeList.cpp', 'ChromeUtils.cpp', diff --git a/dom/base/nsContentIterator.cpp b/dom/base/nsContentIterator.cpp index 3791b339b9..092d9a46ad 100644 --- a/dom/base/nsContentIterator.cpp +++ b/dom/base/nsContentIterator.cpp @@ -405,8 +405,8 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange) // 'degenerate', i.e., not collapsed but still contain no content. if (mFirst && - NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre, startNode, startIndx, - endNode, endIndx))) { + !NodeIsInTraversalRange(mFirst, mPre, startNode, startIndx, + endNode, endIndx)) { mFirst = nullptr; } } @@ -448,9 +448,9 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange) mLast = GetPrevSibling(endNode); NS_WARN_IF(!mLast); - if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre, - startNode, startIndx, - endNode, endIndx))) { + if (!NodeIsInTraversalRange(mLast, mPre, + startNode, startIndx, + endNode, endIndx)) { mLast = nullptr; } } else { @@ -485,7 +485,7 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange) // If either first or last is null, they both have to be null! - if (NS_WARN_IF(!mFirst) || NS_WARN_IF(!mLast)) { + if (!mFirst || !mLast) { mFirst = nullptr; mLast = nullptr; } @@ -774,7 +774,11 @@ nsContentIterator::NextNode(nsINode* aNode, nsTArray* aIndexes) // post-order nsINode* parent = node->GetParentNode(); - NS_WARN_IF(!parent); + if (NS_WARN_IF(!parent)) { + MOZ_ASSERT(parent, "The node is the root node but not the last node"); + mIsDone = true; + return node; + } nsIContent* sibling = nullptr; int32_t indx = 0; @@ -839,7 +843,11 @@ nsContentIterator::PrevNode(nsINode* aNode, nsTArray* aIndexes) // if we are a Pre-order iterator, use pre-order if (mPre) { nsINode* parent = node->GetParentNode(); - NS_WARN_IF(!parent); + if (NS_WARN_IF(!parent)) { + MOZ_ASSERT(parent, "The node is the root node but not the first node"); + mIsDone = true; + return aNode; + } nsIContent* sibling = nullptr; int32_t indx = 0; diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index e905cec394..88061ffa3b 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -5854,23 +5854,6 @@ nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsAString& aOrigin) return NS_OK; } -/* static */ -nsIDocument* -nsContentUtils::GetDocumentFromScriptContext(nsIScriptContext *aScriptContext) -{ - if (!aScriptContext) { - return nullptr; - } - - nsCOMPtr window = - do_QueryInterface(aScriptContext->GetGlobalObject()); - if (!window) { - return nullptr; - } - - return window->GetDoc(); -} - /* static */ bool nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal, nsIChannel* aChannel, bool aAllowIfInheritsPrincipal) diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index f5203376c0..279cf34a22 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -1738,16 +1738,6 @@ public: bool aShift = false, bool aMeta = false); - /** - * Gets the nsIDocument given the script context. Will return nullptr on failure. - * - * @param aScriptContext the script context to get the document for; can be null - * - * @return the document associated with the script context - */ - static nsIDocument* - GetDocumentFromScriptContext(nsIScriptContext* aScriptContext); - static bool CheckMayLoad(nsIPrincipal* aPrincipal, nsIChannel* aChannel, bool aAllowIfInheritsPrincipal); /** diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index f58979ca13..85f843ea19 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -8942,10 +8942,6 @@ nsDocument::Destroy() mExternalResourceMap.Shutdown(); mRegistry = nullptr; - - // XXX We really should let cycle collection do this, but that currently still - // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684). - ReleaseWrapper(static_cast(this)); } void diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index 2c04aa1a95..ea6a27ecdf 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -99,11 +99,13 @@ struct nsDelayedBlurOrFocusEvent nsDelayedBlurOrFocusEvent(EventMessage aEventMessage, nsIPresShell* aPresShell, nsIDocument* aDocument, - EventTarget* aTarget) + EventTarget* aTarget, + EventTarget* aRelatedTarget) : mPresShell(aPresShell) , mDocument(aDocument) , mTarget(aTarget) , mEventMessage(aEventMessage) + , mRelatedTarget(aRelatedTarget) { } @@ -119,6 +121,7 @@ struct nsDelayedBlurOrFocusEvent nsCOMPtr mDocument; nsCOMPtr mTarget; EventMessage mEventMessage; + nsCOMPtr mRelatedTarget; }; inline void ImplCycleCollectionUnlink(nsDelayedBlurOrFocusEvent& aField) @@ -126,6 +129,7 @@ inline void ImplCycleCollectionUnlink(nsDelayedBlurOrFocusEvent& aField) aField.mPresShell = nullptr; aField.mDocument = nullptr; aField.mTarget = nullptr; + aField.mRelatedTarget = nullptr; } inline void @@ -137,6 +141,7 @@ ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, CycleCollectionNoteChild(aCallback, aField.mPresShell.get(), aName, aFlags); CycleCollectionNoteChild(aCallback, aField.mDocument.get(), aName, aFlags); CycleCollectionNoteChild(aCallback, aField.mTarget.get(), aName, aFlags); + CycleCollectionNoteChild(aCallback, aField.mRelatedTarget.get(), aName, aFlags); } NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager) @@ -1039,8 +1044,10 @@ nsFocusManager::FireDelayedEvents(nsIDocument* aDocument) EventMessage message = mDelayedBlurFocusEvents[i].mEventMessage; nsCOMPtr target = mDelayedBlurFocusEvents[i].mTarget; nsCOMPtr presShell = mDelayedBlurFocusEvents[i].mPresShell; + nsCOMPtr relatedTarget = mDelayedBlurFocusEvents[i].mRelatedTarget; mDelayedBlurFocusEvents.RemoveElementAt(i); - SendFocusOrBlurEvent(message, presShell, aDocument, target, 0, false); + SendFocusOrBlurEvent(message, presShell, aDocument, target, 0, + false, false, relatedTarget); --i; } } @@ -1292,6 +1299,7 @@ nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags, isElementInActiveWindow, isElementInFocusedWindow, sendFocusEvent)); if (sendFocusEvent) { + nsCOMPtr oldFocusedContent = mFocusedContent; // return if blurring fails or the focus changes during the blur if (mFocusedWindow) { // if the focus is being moved to another element in the same document, @@ -1317,12 +1325,13 @@ nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags, commonAncestor = GetCommonAncestor(newWindow, mFocusedWindow); if (!Blur(currentIsSameOrAncestor ? mFocusedWindow.get() : nullptr, - commonAncestor, !isElementInFocusedWindow, aAdjustWidget)) + commonAncestor, !isElementInFocusedWindow, aAdjustWidget, + contentToFocus)) return; } Focus(newWindow, contentToFocus, aFlags, !isElementInFocusedWindow, - aFocusChanged, false, aAdjustWidget); + aFocusChanged, false, aAdjustWidget, oldFocusedContent); } else { // otherwise, for inactive windows and when the caller cannot steal the @@ -1570,7 +1579,8 @@ bool nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear, nsPIDOMWindow* aAncestorWindowToFocus, bool aIsLeavingDocument, - bool aAdjustWidgets) + bool aAdjustWidgets, + nsIContent* aContentToFocus) { LOGFOCUS(("<>")); @@ -1680,7 +1690,8 @@ nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear, window->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0); SendFocusOrBlurEvent(eBlur, presShell, - content->GetComposedDoc(), content, 1, false); + content->GetComposedDoc(), content, 1, + false, false, aContentToFocus); } // if we are leaving the document or the window was lowered, make the caret @@ -1750,7 +1761,8 @@ nsFocusManager::Focus(nsPIDOMWindow* aWindow, bool aIsNewDocument, bool aFocusChanged, bool aWindowRaised, - bool aAdjustWidgets) + bool aAdjustWidgets, + nsIContent* aContentLostFocus) { LOGFOCUS(("<>")); @@ -1903,7 +1915,7 @@ nsFocusManager::Focus(nsPIDOMWindow* aWindow, SendFocusOrBlurEvent(eFocus, presShell, aContent->GetComposedDoc(), aContent, aFlags & FOCUSMETHOD_MASK, - aWindowRaised, isRefocus); + aWindowRaised, isRefocus, aContentLostFocus); } else { IMEStateManager::OnChangeFocus(presContext, nullptr, GetFocusMoveActionCause(aFlags)); @@ -1959,12 +1971,13 @@ class FocusBlurEvent : public nsRunnable public: FocusBlurEvent(nsISupports* aTarget, EventMessage aEventMessage, nsPresContext* aContext, bool aWindowRaised, - bool aIsRefocus) + bool aIsRefocus, EventTarget* aRelatedTarget) : mTarget(aTarget) , mContext(aContext) , mEventMessage(aEventMessage) , mWindowRaised(aWindowRaised) , mIsRefocus(aIsRefocus) + , mRelatedTarget(aRelatedTarget) { } @@ -1975,6 +1988,7 @@ public: event.mFlags.mCancelable = false; event.mFromRaise = mWindowRaised; event.mIsRefocus = mIsRefocus; + event.mRelatedTarget = mRelatedTarget; return EventDispatcher::Dispatch(mTarget, mContext, &event); } @@ -1983,8 +1997,21 @@ public: EventMessage mEventMessage; bool mWindowRaised; bool mIsRefocus; + nsCOMPtr mRelatedTarget; }; +static nsIDocument* +GetDocumentHelper(EventTarget* aTarget) +{ + nsCOMPtr node = do_QueryInterface(aTarget); + if (!node) { + nsCOMPtr win = do_QueryInterface(aTarget); + return win ? win->GetExtantDoc() : nullptr; + } + + return node->OwnerDoc(); +} + void nsFocusManager::SendFocusOrBlurEvent(EventMessage aEventMessage, nsIPresShell* aPresShell, @@ -1992,19 +2019,22 @@ nsFocusManager::SendFocusOrBlurEvent(EventMessage aEventMessage, nsISupports* aTarget, uint32_t aFocusMethod, bool aWindowRaised, - bool aIsRefocus) + bool aIsRefocus, + EventTarget* aRelatedTarget) { NS_ASSERTION(aEventMessage == eFocus || aEventMessage == eBlur, "Wrong event type for SendFocusOrBlurEvent"); nsCOMPtr eventTarget = do_QueryInterface(aTarget); + nsCOMPtr eventTargetDoc = GetDocumentHelper(eventTarget); + nsCOMPtr relatedTargetDoc = GetDocumentHelper(aRelatedTarget); - nsCOMPtr n = do_QueryInterface(aTarget); - if (!n) { - nsCOMPtr win = do_QueryInterface(aTarget); - n = win ? win->GetExtantDoc() : nullptr; + // set aRelatedTarget to null if it's not in the same document as eventTarget + if (eventTargetDoc != relatedTargetDoc) { + aRelatedTarget = nullptr; } - bool dontDispatchEvent = n && nsContentUtils::IsUserFocusIgnored(n); + bool dontDispatchEvent = + eventTargetDoc && nsContentUtils::IsUserFocusIgnored(eventTargetDoc); // for focus events, if this event was from a mouse or key and event // handling on the document is suppressed, queue the event and fire it @@ -2020,14 +2050,15 @@ nsFocusManager::SendFocusOrBlurEvent(EventMessage aEventMessage, if (mDelayedBlurFocusEvents[i - 1].mEventMessage == aEventMessage && mDelayedBlurFocusEvents[i - 1].mPresShell == aPresShell && mDelayedBlurFocusEvents[i - 1].mDocument == aDocument && - mDelayedBlurFocusEvents[i - 1].mTarget == eventTarget) { + mDelayedBlurFocusEvents[i - 1].mTarget == eventTarget && + mDelayedBlurFocusEvents[i - 1].mRelatedTarget == aRelatedTarget) { mDelayedBlurFocusEvents.RemoveElementAt(i - 1); } } mDelayedBlurFocusEvents.AppendElement( nsDelayedBlurOrFocusEvent(aEventMessage, aPresShell, - aDocument, eventTarget)); + aDocument, eventTarget, aRelatedTarget)); return; } @@ -2045,7 +2076,7 @@ nsFocusManager::SendFocusOrBlurEvent(EventMessage aEventMessage, if (!dontDispatchEvent) { nsContentUtils::AddScriptRunner( new FocusBlurEvent(aTarget, aEventMessage, aPresShell->GetPresContext(), - aWindowRaised, aIsRefocus)); + aWindowRaised, aIsRefocus, aRelatedTarget)); } } diff --git a/dom/base/nsFocusManager.h b/dom/base/nsFocusManager.h index a2aad11dce..a298acdd9b 100644 --- a/dom/base/nsFocusManager.h +++ b/dom/base/nsFocusManager.h @@ -239,9 +239,10 @@ protected: * If aAdjustWidget is false, don't change the widget focus state. */ bool Blur(nsPIDOMWindow* aWindowToClear, - nsPIDOMWindow* aAncestorWindowToFocus, - bool aIsLeavingDocument, - bool aAdjustWidget); + nsPIDOMWindow* aAncestorWindowToFocus, + bool aIsLeavingDocument, + bool aAdjustWidget, + nsIContent* aContentToFocus = nullptr); /** * Focus an element in the active window and child frame. @@ -275,7 +276,8 @@ protected: bool aIsNewDocument, bool aFocusChanged, bool aWindowRaised, - bool aAdjustWidget); + bool aAdjustWidget, + nsIContent* aContentLostFocus = nullptr); /** * Fires a focus or blur event at aTarget. @@ -291,7 +293,8 @@ protected: nsISupports* aTarget, uint32_t aFocusMethod, bool aWindowRaised, - bool aIsRefocus = false); + bool aIsRefocus = false, + mozilla::dom::EventTarget* aRelatedTarget = nullptr); /** * Scrolls aContent into view unless the FLAG_NOSCROLL flag is set. diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index 8a31d9e2fe..0474edb093 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -69,7 +69,7 @@ using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::dom::ipc; -static const int kMinTelemetryMessageSize = 8192; +static const size_t kMinTelemetryMessageSize = 8192; nsFrameMessageManager::nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback, nsFrameMessageManager* aParentManager, @@ -1799,25 +1799,21 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript( if (!JS::Compile(cx, options, srcBuf, &script)) { return; } - } else { - // We're going to run these against some non-global scope. - if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) { - return; - } + // We're going to run these against some non-global scope. + } else if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) { + return; } + MOZ_ASSERT(script); aScriptp.set(script); nsAutoCString scheme; uri->GetScheme(scheme); // We don't cache data: scripts! if (aShouldCache && !scheme.EqualsLiteral("data")) { - nsMessageManagerScriptHolder* holder; - // Root the object also for caching. - if (script) { - holder = new nsMessageManagerScriptHolder(cx, script, aRunInGlobalScope); - } + nsMessageManagerScriptHolder* holder = + new nsMessageManagerScriptHolder(cx, script, aRunInGlobalScope); sCachedScripts->Put(aURL, holder); } } diff --git a/dom/base/nsGenericDOMDataNode.cpp b/dom/base/nsGenericDOMDataNode.cpp index c80370bdea..505599dc03 100644 --- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -793,14 +793,6 @@ nsGenericDOMDataNode::SaveSubtreeState() { } -void -nsGenericDOMDataNode::DestroyContent() -{ - // XXX We really should let cycle collection do this, but that currently still - // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684). - ReleaseWrapper(this); -} - #ifdef DEBUG void nsGenericDOMDataNode::List(FILE* out, int32_t aIndent) const diff --git a/dom/base/nsGenericDOMDataNode.h b/dom/base/nsGenericDOMDataNode.h index 52b44c1bd3..38153152ec 100644 --- a/dom/base/nsGenericDOMDataNode.h +++ b/dom/base/nsGenericDOMDataNode.h @@ -149,7 +149,6 @@ public: MOZ_WARN_UNUSED_RESULT virtual bool AppendTextTo(nsAString& aResult, const mozilla::fallible_t&) override; - virtual void DestroyContent() override; virtual void SaveSubtreeState() override; #ifdef DEBUG diff --git a/dom/base/nsHostObjectProtocolHandler.cpp b/dom/base/nsHostObjectProtocolHandler.cpp index c98a35f1e4..f17a053e9a 100644 --- a/dom/base/nsHostObjectProtocolHandler.cpp +++ b/dom/base/nsHostObjectProtocolHandler.cpp @@ -11,6 +11,7 @@ #include "mozilla/dom/File.h" #include "mozilla/dom/MediaSource.h" #include "mozilla/LoadInfo.h" +#include "mozilla/ModuleUtils.h" #include "mozilla/Preferences.h" #include "nsClassHashtable.h" #include "nsError.h" @@ -737,3 +738,53 @@ NS_GetSourceForMediaSourceURI(nsIURI* aURI, mozilla::dom::MediaSource** aSource) source.forget(aSource); return NS_OK; } + +#define NS_BLOBPROTOCOLHANDLER_CID \ +{ 0xb43964aa, 0xa078, 0x44b2, \ + { 0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 } } + +#define NS_MEDIASTREAMPROTOCOLHANDLER_CID \ +{ 0x27d1fa24, 0x2b73, 0x4db3, \ + { 0xab, 0x48, 0xb9, 0x83, 0x83, 0x40, 0xe0, 0x81 } } + +#define NS_MEDIASOURCEPROTOCOLHANDLER_CID \ +{ 0x12ef31fc, 0xa8fb, 0x4661, \ + { 0x9a, 0x63, 0xfb, 0x61, 0x04,0x5d, 0xb8, 0x61 } } + +#define NS_FONTTABLEPROTOCOLHANDLER_CID \ +{ 0x3fc8f04e, 0xd719, 0x43ca, \ + { 0x9a, 0xd0, 0x18, 0xee, 0x32, 0x02, 0x11, 0xf2 } } + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsBlobProtocolHandler) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaStreamProtocolHandler) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaSourceProtocolHandler) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsFontTableProtocolHandler) + +NS_DEFINE_NAMED_CID(NS_BLOBPROTOCOLHANDLER_CID); +NS_DEFINE_NAMED_CID(NS_MEDIASTREAMPROTOCOLHANDLER_CID); +NS_DEFINE_NAMED_CID(NS_MEDIASOURCEPROTOCOLHANDLER_CID); +NS_DEFINE_NAMED_CID(NS_FONTTABLEPROTOCOLHANDLER_CID); + +static const mozilla::Module::CIDEntry kHostObjectProtocolHandlerCIDs[] = { + { &kNS_BLOBPROTOCOLHANDLER_CID, false, nullptr, nsBlobProtocolHandlerConstructor }, + { &kNS_MEDIASTREAMPROTOCOLHANDLER_CID, false, nullptr, nsMediaStreamProtocolHandlerConstructor }, + { &kNS_MEDIASOURCEPROTOCOLHANDLER_CID, false, nullptr, nsMediaSourceProtocolHandlerConstructor }, + { &kNS_FONTTABLEPROTOCOLHANDLER_CID, false, nullptr, nsFontTableProtocolHandlerConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kHostObjectProtocolHandlerContracts[] = { + { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX BLOBURI_SCHEME, &kNS_BLOBPROTOCOLHANDLER_CID }, + { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MEDIASTREAMURI_SCHEME, &kNS_MEDIASTREAMPROTOCOLHANDLER_CID }, + { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MEDIASOURCEURI_SCHEME, &kNS_MEDIASOURCEPROTOCOLHANDLER_CID }, + { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX FONTTABLEURI_SCHEME, &kNS_FONTTABLEPROTOCOLHANDLER_CID }, + { nullptr } +}; + +static const mozilla::Module kHostObjectProtocolHandlerModule = { + mozilla::Module::kVersion, + kHostObjectProtocolHandlerCIDs, + kHostObjectProtocolHandlerContracts +}; + +NSMODULE_DEFN(HostObjectProtocolHandler) = &kHostObjectProtocolHandlerModule; diff --git a/dom/base/nsHostObjectProtocolHandler.h b/dom/base/nsHostObjectProtocolHandler.h index 909631f37e..d38a93a34d 100644 --- a/dom/base/nsHostObjectProtocolHandler.h +++ b/dom/base/nsHostObjectProtocolHandler.h @@ -136,20 +136,4 @@ NS_GetStreamForMediaStreamURI(nsIURI* aURI, mozilla::DOMMediaStream** aStream); extern nsresult NS_GetSourceForMediaSourceURI(nsIURI* aURI, mozilla::dom::MediaSource** aSource); -#define NS_BLOBPROTOCOLHANDLER_CID \ -{ 0xb43964aa, 0xa078, 0x44b2, \ - { 0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 } } - -#define NS_MEDIASTREAMPROTOCOLHANDLER_CID \ -{ 0x27d1fa24, 0x2b73, 0x4db3, \ - { 0xab, 0x48, 0xb9, 0x83, 0x83, 0x40, 0xe0, 0x81 } } - -#define NS_MEDIASOURCEPROTOCOLHANDLER_CID \ -{ 0x12ef31fc, 0xa8fb, 0x4661, \ - { 0x9a, 0x63, 0xfb, 0x61, 0x04,0x5d, 0xb8, 0x61 } } - -#define NS_FONTTABLEPROTOCOLHANDLER_CID \ -{ 0x3fc8f04e, 0xd719, 0x43ca, \ - { 0x9a, 0xd0, 0x18, 0xee, 0x32, 0x02, 0x11, 0xf2 } } - #endif /* nsHostObjectProtocolHandler_h */ diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index e3e3bcbf8c..f7cab4f7ec 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -877,7 +877,9 @@ public: * Destroy this node and its children. Ideally this shouldn't be needed * but for now we need to do it to break cycles. */ - virtual void DestroyContent() = 0; + virtual void DestroyContent() + { + } /** * Saves the form state of this node and its children. diff --git a/dom/base/nsInProcessTabChildGlobal.cpp b/dom/base/nsInProcessTabChildGlobal.cpp index 68f12ed3da..95ce868eb6 100644 --- a/dom/base/nsInProcessTabChildGlobal.cpp +++ b/dom/base/nsInProcessTabChildGlobal.cpp @@ -251,6 +251,7 @@ nsInProcessTabChildGlobal::GetOwnerContent() nsresult nsInProcessTabChildGlobal::PreHandleEvent(EventChainPreVisitor& aVisitor) { + aVisitor.mForceContentDispatch = true; aVisitor.mCanHandle = true; #ifdef DEBUG diff --git a/dom/base/nsMimeTypeArray.cpp b/dom/base/nsMimeTypeArray.cpp index 3f5067b21f..210648d9f3 100644 --- a/dom/base/nsMimeTypeArray.cpp +++ b/dom/base/nsMimeTypeArray.cpp @@ -119,50 +119,7 @@ nsMimeTypeArray::NamedGetter(const nsAString& aName, bool &aFound) return mimeType; } - // Now let's check with the MIME service. - nsCOMPtr mimeSrv = do_GetService("@mozilla.org/mime;1"); - if (!mimeSrv) { - return nullptr; - } - - nsCOMPtr mimeInfo; - mimeSrv->GetFromTypeAndExtension(NS_ConvertUTF16toUTF8(lowerName), - EmptyCString(), getter_AddRefs(mimeInfo)); - if (!mimeInfo) { - return nullptr; - } - - // Now we check whether we can really claim to support this type - nsHandlerInfoAction action = nsIHandlerInfo::saveToDisk; - mimeInfo->GetPreferredAction(&action); - if (action != nsIMIMEInfo::handleInternally) { - bool hasHelper = false; - mimeInfo->GetHasDefaultHandler(&hasHelper); - - if (!hasHelper) { - nsCOMPtr helper; - mimeInfo->GetPreferredApplicationHandler(getter_AddRefs(helper)); - - if (!helper) { - // mime info from the OS may not have a PreferredApplicationHandler - // so just check for an empty default description - nsAutoString defaultDescription; - mimeInfo->GetDefaultDescription(defaultDescription); - - if (defaultDescription.IsEmpty()) { - // no support; just leave - return nullptr; - } - } - } - } - - // If we got here, we support this type! Say so. - aFound = true; - - nsMimeType *mt = new nsMimeType(mWindow, lowerName); - mMimeTypes.AppendElement(mt); - return mt; + return nullptr; } bool @@ -229,13 +186,7 @@ nsMimeType::nsMimeType(nsPIDOMWindow* aWindow, mDescription(aDescription), mExtension(aExtension) { -} - -nsMimeType::nsMimeType(nsPIDOMWindow* aWindow, const nsAString& aType) - : mWindow(aWindow), - mPluginElement(nullptr), - mType(aType) -{ + MOZ_ASSERT(aPluginElement); } nsMimeType::~nsMimeType() @@ -264,6 +215,8 @@ nsMimeType::GetDescription(nsString& aRetval) const nsPluginElement* nsMimeType::GetEnabledPlugin() const { + // mPluginElement might be null if we got unlinked but are still somehow being + // called into. if (!mPluginElement || !mPluginElement->PluginTag()->IsEnabled()) { return nullptr; } diff --git a/dom/base/nsMimeTypeArray.h b/dom/base/nsMimeTypeArray.h index 1707b97f33..180c17bb1f 100644 --- a/dom/base/nsMimeTypeArray.h +++ b/dom/base/nsMimeTypeArray.h @@ -63,7 +63,6 @@ public: const nsAString& aType, const nsAString& aDescription, const nsAString& aExtension); - nsMimeType(nsPIDOMWindow* aWindow, const nsAString& aMimeType); nsPIDOMWindow* GetParentObject() const; virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; @@ -83,10 +82,9 @@ protected: nsCOMPtr mWindow; - // Strong reference to the active plugin, if any. Note that this - // creates an explicit reference cycle through the plugin element's - // mimetype array. We rely on the cycle collector to break this - // cycle. + // Strong reference to the active plugin. Note that this creates an explicit + // reference cycle through the plugin element's mimetype array. We rely on the + // cycle collector to break this cycle. RefPtr mPluginElement; nsString mType; nsString mDescription; diff --git a/dom/base/nsPerformance.cpp b/dom/base/nsPerformance.cpp index a564758c93..ce44bc7e70 100644 --- a/dom/base/nsPerformance.cpp +++ b/dom/base/nsPerformance.cpp @@ -68,9 +68,11 @@ nsPerformanceTiming::nsPerformanceTiming(nsPerformance* aPerformance, mZeroTime = 0; } - // The aHttpChannel argument is null if this nsPerformanceTiming object - // is being used for the navigation timing (document) and has a non-null - // value for the resource timing (any resources within the page). + // The aHttpChannel argument is null if this nsPerformanceTiming object is + // being used for navigation timing (which is only relevant for documents). + // It has a non-null value if this nsPerformanceTiming object is being used + // for resource timing, which can include document loads, both toplevel and + // in subframes, and resources linked from a document. if (aHttpChannel) { mTimingAllowed = CheckAllowedOrigin(aHttpChannel, aChannel); bool redirectsPassCheck = false; @@ -144,6 +146,14 @@ nsPerformanceTiming::CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, if (!loadInfo) { return false; } + + // TYPE_DOCUMENT loads have no loadingPrincipal. And that's OK, because we + // never actually need to have a performance timing entry for TYPE_DOCUMENT + // loads. + if (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) { + return false; + } + nsCOMPtr principal = loadInfo->LoadingPrincipal(); // Check if the resource is either same origin as the page that started diff --git a/dom/base/nsPlainTextSerializer.cpp b/dom/base/nsPlainTextSerializer.cpp index 1accfa2bd7..2d0226c898 100644 --- a/dom/base/nsPlainTextSerializer.cpp +++ b/dom/base/nsPlainTextSerializer.cpp @@ -74,7 +74,6 @@ nsPlainTextSerializer::nsPlainTextSerializer() mCiteQuoteLevel = 0; mStructs = true; // will be read from prefs later mHeaderStrategy = 1 /*indent increasingly*/; // ditto - mDontWrapAnyQuotes = false; // ditto mHasWrittenCiteBlockquote = false; mSpanLevel = 0; for (int32_t i = 0; i <= 6; i++) { @@ -178,15 +177,6 @@ nsPlainTextSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn, mHeaderStrategy = Preferences::GetInt(PREF_HEADER_STRATEGY, mHeaderStrategy); - - // DontWrapAnyQuotes is set according to whether plaintext mail - // is wrapping to window width -- see bug 134439. - // We'll only want this if we're wrapping and formatted. - if (mFlags & nsIDocumentEncoder::OutputWrap || mWrapColumn > 0) { - mDontWrapAnyQuotes = - Preferences::GetBool("mail.compose.wrap_to_window_width", - mDontWrapAnyQuotes); - } } // The pref is default inited to false in libpref, but we use true @@ -1588,8 +1578,7 @@ nsPlainTextSerializer::Write(const nsAString& aStr) // that does normal formatted text. The one for preformatted text calls // Output directly while the other code path goes through AddToLine. if ((mPreFormattedMail && !mWrapColumn) || (IsInPre() && !mPreFormattedMail) - || ((mSpanLevel > 0 || mDontWrapAnyQuotes) - && mEmptyLines >= 0 && str.First() == char16_t('>'))) { + || (mSpanLevel > 0 && mEmptyLines >= 0 && str.First() == char16_t('>'))) { // No intelligent wrapping. // This mustn't be mixed with intelligent wrapping without clearing diff --git a/dom/base/nsPlainTextSerializer.h b/dom/base/nsPlainTextSerializer.h index acdbff5bae..3b45408feb 100644 --- a/dom/base/nsPlainTextSerializer.h +++ b/dom/base/nsPlainTextSerializer.h @@ -128,15 +128,6 @@ private: uint32_t mHeadLevel; bool mAtFirstColumn; - // Handling of quoted text (for mail): - // Quotes need to be wrapped differently from non-quoted text, - // because quoted text has a few extra characters (e.g. ">> ") - // which makes the line length longer. - // Mail can represent quotes in different ways: - // Not wrapped in any special tag (if mail.compose.wrap_to_window_width) - // or in a . - bool mDontWrapAnyQuotes; // no special quote markers - bool mStructs; // Output structs (pref) // If we've just written out a cite blockquote, we need to remember it diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 01251db434..60788f237f 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -67,9 +67,6 @@ GetSriLog() return gSriPRLog; } -// The nsScriptLoadRequest is passed as the context to necko, and thus -// it needs to be threadsafe. Necko won't do anything with this -// context, but it will AddRef and Release it on other threads. NS_IMPL_ISUPPORTS0(nsScriptLoadRequest) nsScriptLoadRequestList::~nsScriptLoadRequestList() diff --git a/dom/base/nsScriptLoader.h b/dom/base/nsScriptLoader.h index 70d730e417..338f8a62b8 100644 --- a/dom/base/nsScriptLoader.h +++ b/dom/base/nsScriptLoader.h @@ -82,7 +82,7 @@ public: { } - NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_ISUPPORTS void FireScriptAvailable(nsresult aResult) { diff --git a/dom/base/nsXMLHttpRequest.cpp b/dom/base/nsXMLHttpRequest.cpp index fbdf84d998..ccaac1373b 100644 --- a/dom/base/nsXMLHttpRequest.cpp +++ b/dom/base/nsXMLHttpRequest.cpp @@ -1391,11 +1391,7 @@ nsXMLHttpRequest::GetLoadGroup() const return ref.forget(); } - nsresult rv = NS_ERROR_FAILURE; - nsIScriptContext* sc = - const_cast(this)->GetContextForEventHandlers(&rv); - nsCOMPtr doc = - nsContentUtils::GetDocumentFromScriptContext(sc); + nsIDocument* doc = GetDocumentIfCurrent(); if (doc) { return doc->GetDocumentLoadGroup(); } @@ -1558,10 +1554,13 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url, mState &= ~XML_HTTP_REQUEST_ASYNC; } - nsIScriptContext* sc = GetContextForEventHandlers(&rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr doc = - nsContentUtils::GetDocumentFromScriptContext(sc); + nsCOMPtr doc = GetDocumentIfCurrent(); + if (!doc) { + // This could be because we're no longer current or because we're in some + // non-window context... + nsresult rv = CheckInnerWindowCorrectness(); + NS_ENSURE_SUCCESS(rv, rv); + } nsCOMPtr baseURI; if (mBaseURI) { @@ -2015,14 +2014,16 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt) NS_ENSURE_SUCCESS(rv, rv); baseURI = docURI; - nsIScriptContext* sc = GetContextForEventHandlers(&rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr doc = - nsContentUtils::GetDocumentFromScriptContext(sc); + nsCOMPtr doc = GetDocumentIfCurrent(); nsCOMPtr chromeXHRDocURI, chromeXHRDocBaseURI; if (doc) { chromeXHRDocURI = doc->GetDocumentURI(); chromeXHRDocBaseURI = doc->GetBaseURI(); + } else { + // If we're no longer current, just kill the load, though it really should + // have been killed already. + nsresult rv = CheckInnerWindowCorrectness(); + NS_ENSURE_SUCCESS(rv, rv); } // Create an empty document from it. diff --git a/dom/bindings/PrimitiveConversions.h b/dom/bindings/PrimitiveConversions.h index 99ed392c8e..3da58a8775 100644 --- a/dom/bindings/PrimitiveConversions.h +++ b/dom/bindings/PrimitiveConversions.h @@ -190,10 +190,10 @@ struct PrimitiveConversionTraits_Limits { template<> struct PrimitiveConversionTraits_Limits { static inline int64_t min() { - return -(1LL << 53); + return -(1LL << 53) + 1; } static inline int64_t max() { - return (1LL << 53); + return (1LL << 53) - 1; } }; @@ -203,7 +203,7 @@ struct PrimitiveConversionTraits_Limits { return 0; } static inline uint64_t max() { - return (1LL << 53); + return (1LL << 53) - 1; } }; diff --git a/dom/crypto/CryptoBuffer.cpp b/dom/crypto/CryptoBuffer.cpp index 2b50074252..3beaa36b22 100644 --- a/dom/crypto/CryptoBuffer.cpp +++ b/dom/crypto/CryptoBuffer.cpp @@ -115,29 +115,12 @@ CryptoBuffer::FromJwkBase64(const nsString& aBase64) NS_ConvertUTF16toUTF8 temp(aBase64); temp.StripWhitespace(); - // Re-add padding - if (temp.Length() % 4 == 3) { - temp.AppendLiteral("="); - } else if (temp.Length() % 4 == 2) { - temp.AppendLiteral("=="); - } if (temp.Length() % 4 == 1) { - return NS_ERROR_FAILURE; // bad Base64 - } - - // Translate from URL-safe character set to normal - temp.ReplaceChar('-', '+'); - temp.ReplaceChar('_', '/'); - - // Perform the actual base64 decode - nsCString binaryData; - nsresult rv = Base64Decode(temp, binaryData); + Base64URLDecodeOptions options; + // JWK prohibits padding per RFC 7515, section 2. + options.mPadding = Base64URLDecodePadding::Reject; + nsresult rv = Base64URLDecode(temp, options, *this); NS_ENSURE_SUCCESS(rv, rv); - if (!Assign((const uint8_t*) binaryData.BeginReading(), - binaryData.Length())) { - return NS_ERROR_FAILURE; - } - return NS_OK; } @@ -150,23 +133,12 @@ CryptoBuffer::ToJwkBase64(nsString& aBase64) return NS_OK; } - // Perform the actual base64 encode - nsCString base64; - nsDependentCSubstring binaryData((const char*) Elements(), - (const char*) (Elements() + Length())); - nsresult rv = Base64Encode(binaryData, base64); + nsAutoCString base64; + Base64URLEncodeOptions options; + options.mPad = false; + nsresult rv = Base64URLEncode(Length(), Elements(), options, base64); NS_ENSURE_SUCCESS(rv, rv); - // Strip padding - base64.Trim("="); - - // Translate to the URL-safe charset - base64.ReplaceChar('+', '-'); - base64.ReplaceChar('/', '_'); - if (base64.FindCharInSet("+/", 0) != kNotFound) { - return NS_ERROR_FAILURE; - } - CopyASCIItoUTF16(base64, aBase64); return NS_OK; } diff --git a/dom/crypto/CryptoKey.cpp b/dom/crypto/CryptoKey.cpp index 497aae7b12..1ed8638851 100644 --- a/dom/crypto/CryptoKey.cpp +++ b/dom/crypto/CryptoKey.cpp @@ -9,7 +9,6 @@ #include "nsNSSComponent.h" #include "ScopedNSSTypes.h" #include "mozilla/dom/CryptoKey.h" -#include "mozilla/dom/WebCryptoCommon.h" #include "mozilla/dom/SubtleCryptoBinding.h" #include "mozilla/dom/ToJSValue.h" @@ -746,7 +745,7 @@ CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk, { CKA_ID, objID->data, objID->len }, { CKA_EC_PARAMS, params->data, params->len }, { CKA_EC_POINT, ecPoint->data, ecPoint->len }, - { CKA_VALUE, (void*) d.Elements(), d.Length() }, + { CKA_VALUE, (void*) d.Elements(), (CK_ULONG) d.Length() }, }; return PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate, @@ -793,14 +792,14 @@ CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk, { CKA_SENSITIVE, &falseValue, sizeof(falseValue) }, { CKA_PRIVATE, &falseValue, sizeof(falseValue) }, { CKA_ID, objID->data, objID->len }, - { CKA_MODULUS, (void*) n.Elements(), n.Length() }, - { CKA_PUBLIC_EXPONENT, (void*) e.Elements(), e.Length() }, - { CKA_PRIVATE_EXPONENT, (void*) d.Elements(), d.Length() }, - { CKA_PRIME_1, (void*) p.Elements(), p.Length() }, - { CKA_PRIME_2, (void*) q.Elements(), q.Length() }, - { CKA_EXPONENT_1, (void*) dp.Elements(), dp.Length() }, - { CKA_EXPONENT_2, (void*) dq.Elements(), dq.Length() }, - { CKA_COEFFICIENT, (void*) qi.Elements(), qi.Length() }, + { CKA_MODULUS, (void*) n.Elements(), (CK_ULONG) n.Length() }, + { CKA_PUBLIC_EXPONENT, (void*) e.Elements(), (CK_ULONG) e.Length() }, + { CKA_PRIVATE_EXPONENT, (void*) d.Elements(), (CK_ULONG) d.Length() }, + { CKA_PRIME_1, (void*) p.Elements(), (CK_ULONG) p.Length() }, + { CKA_PRIME_2, (void*) q.Elements(), (CK_ULONG) q.Length() }, + { CKA_EXPONENT_1, (void*) dp.Elements(), (CK_ULONG) dp.Length() }, + { CKA_EXPONENT_2, (void*) dq.Elements(), (CK_ULONG) dq.Length() }, + { CKA_COEFFICIENT, (void*) qi.Elements(), (CK_ULONG) qi.Length() }, }; return PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate, diff --git a/dom/crypto/WebCryptoTask.cpp b/dom/crypto/WebCryptoTask.cpp index 9a9a9a9191..5c33065c1e 100644 --- a/dom/crypto/WebCryptoTask.cpp +++ b/dom/crypto/WebCryptoTask.cpp @@ -1337,24 +1337,16 @@ private: class ImportKeyTask : public WebCryptoTask { public: - void Init(JSContext* aCx, - const nsAString& aFormat, - const ObjectOrString& aAlgorithm, bool aExtractable, - const Sequence& aKeyUsages) + void Init(nsIGlobalObject* aGlobal, JSContext* aCx, + const nsAString& aFormat, const ObjectOrString& aAlgorithm, + bool aExtractable, const Sequence& aKeyUsages) { mFormat = aFormat; mDataIsSet = false; mDataIsJwk = false; - // Get the current global object from the context - nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)); - if (!global) { - mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR; - return; - } - // This stuff pretty much always happens, so we'll do it here - mKey = new CryptoKey(global); + mKey = new CryptoKey(aGlobal); mKey->SetExtractable(aExtractable); mKey->ClearUsages(); for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) { @@ -1477,20 +1469,20 @@ private: class ImportSymmetricKeyTask : public ImportKeyTask { public: - ImportSymmetricKeyTask(JSContext* aCx, + ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) { - Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); } - ImportSymmetricKeyTask(JSContext* aCx, + ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat, const JS::Handle aKeyData, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) { - Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); if (NS_FAILED(mEarlyRv)) { return; } @@ -1503,12 +1495,11 @@ public: } } - void Init(JSContext* aCx, - const nsAString& aFormat, + void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) { - ImportKeyTask::Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); if (NS_FAILED(mEarlyRv)) { return; } @@ -1635,20 +1626,20 @@ private: class ImportRsaKeyTask : public ImportKeyTask { public: - ImportRsaKeyTask(JSContext* aCx, + ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) { - Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); } - ImportRsaKeyTask(JSContext* aCx, + ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat, JS::Handle aKeyData, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) { - Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); if (NS_FAILED(mEarlyRv)) { return; } @@ -1661,12 +1652,12 @@ public: } } - void Init(JSContext* aCx, + void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) { - ImportKeyTask::Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); if (NS_FAILED(mEarlyRv)) { return; } @@ -1803,19 +1794,19 @@ private: class ImportEcKeyTask : public ImportKeyTask { public: - ImportEcKeyTask(JSContext* aCx, const nsAString& aFormat, - const ObjectOrString& aAlgorithm, bool aExtractable, - const Sequence& aKeyUsages) + ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, + const nsAString& aFormat, const ObjectOrString& aAlgorithm, + bool aExtractable, const Sequence& aKeyUsages) { - Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); } - ImportEcKeyTask(JSContext* aCx, const nsAString& aFormat, - JS::Handle aKeyData, + ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, + const nsAString& aFormat, JS::Handle aKeyData, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) { - Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); if (NS_FAILED(mEarlyRv)) { return; } @@ -1824,11 +1815,11 @@ public: NS_ENSURE_SUCCESS_VOID(mEarlyRv); } - void Init(JSContext* aCx, const nsAString& aFormat, + void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) { - ImportKeyTask::Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); if (NS_FAILED(mEarlyRv)) { return; } @@ -1956,30 +1947,30 @@ private: class ImportDhKeyTask : public ImportKeyTask { public: - ImportDhKeyTask(JSContext* aCx, const nsAString& aFormat, - const ObjectOrString& aAlgorithm, bool aExtractable, - const Sequence& aKeyUsages) + ImportDhKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, + const nsAString& aFormat, const ObjectOrString& aAlgorithm, + bool aExtractable, const Sequence& aKeyUsages) { - Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); } - ImportDhKeyTask(JSContext* aCx, const nsAString& aFormat, - JS::Handle aKeyData, + ImportDhKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, + const nsAString& aFormat, JS::Handle aKeyData, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) { - Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); if (NS_SUCCEEDED(mEarlyRv)) { SetKeyData(aCx, aKeyData); NS_ENSURE_SUCCESS_VOID(mEarlyRv); } } - void Init(JSContext* aCx, const nsAString& aFormat, + void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) { - ImportKeyTask::Init(aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); + ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages); if (NS_FAILED(mEarlyRv)) { return; } @@ -2202,18 +2193,12 @@ private: class GenerateSymmetricKeyTask : public WebCryptoTask { public: - GenerateSymmetricKeyTask(JSContext* aCx, + GenerateSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) { - nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)); - if (!global) { - mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR; - return; - } - // Create an empty key and set easy attributes - mKey = new CryptoKey(global); + mKey = new CryptoKey(aGlobal); mKey->SetExtractable(aExtractable); mKey->SetType(CryptoKey::SECRET); @@ -2858,7 +2843,7 @@ template class DeriveKeyTask : public DeriveBitsTask { public: - DeriveKeyTask(JSContext* aCx, + DeriveKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey, const ObjectOrString& aDerivedKeyType, bool aExtractable, const Sequence& aKeyUsages) @@ -2870,7 +2855,7 @@ public: } NS_NAMED_LITERAL_STRING(format, WEBCRYPTO_KEY_FORMAT_RAW); - mTask = new ImportSymmetricKeyTask(aCx, format, aDerivedKeyType, + mTask = new ImportSymmetricKeyTask(aGlobal, aCx, format, aDerivedKeyType, aExtractable, aKeyUsages); } @@ -3300,7 +3285,8 @@ WebCryptoTask::CreateDigestTask(JSContext* aCx, } WebCryptoTask* -WebCryptoTask::CreateImportKeyTask(JSContext* aCx, +WebCryptoTask::CreateImportKeyTask(nsIGlobalObject* aGlobal, + JSContext* aCx, const nsAString& aFormat, JS::Handle aKeyData, const ObjectOrString& aAlgorithm, @@ -3338,19 +3324,19 @@ WebCryptoTask::CreateImportKeyTask(JSContext* aCx, algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) || algName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) || algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { - return new ImportSymmetricKeyTask(aCx, aFormat, aKeyData, aAlgorithm, - aExtractable, aKeyUsages); + return new ImportSymmetricKeyTask(aGlobal, aCx, aFormat, aKeyData, + aAlgorithm, aExtractable, aKeyUsages); } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) || algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) || algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) { - return new ImportRsaKeyTask(aCx, aFormat, aKeyData, aAlgorithm, + return new ImportRsaKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm, aExtractable, aKeyUsages); } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) || algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) { - return new ImportEcKeyTask(aCx, aFormat, aKeyData, aAlgorithm, + return new ImportEcKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm, aExtractable, aKeyUsages); } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_DH)) { - return new ImportDhKeyTask(aCx, aFormat, aKeyData, aAlgorithm, + return new ImportDhKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm, aExtractable, aKeyUsages); } else { return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR); @@ -3399,7 +3385,8 @@ WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat, } WebCryptoTask* -WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx, +WebCryptoTask::CreateGenerateKeyTask(nsIGlobalObject* aGlobal, + JSContext* aCx, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages) @@ -3425,7 +3412,8 @@ WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx, algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) || algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) || algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) { - return new GenerateSymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages); + return new GenerateSymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable, + aKeyUsages); } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) || algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) || algName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS) || @@ -3439,7 +3427,8 @@ WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx, } WebCryptoTask* -WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx, +WebCryptoTask::CreateDeriveKeyTask(nsIGlobalObject* aGlobal, + JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey, const ObjectOrString& aDerivedKeyType, @@ -3465,21 +3454,21 @@ WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx, } if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) { - return new DeriveKeyTask(aCx, aAlgorithm, aBaseKey, - aDerivedKeyType, aExtractable, - aKeyUsages); + return new DeriveKeyTask(aGlobal, aCx, aAlgorithm, + aBaseKey, aDerivedKeyType, + aExtractable, aKeyUsages); } if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) { - return new DeriveKeyTask(aCx, aAlgorithm, aBaseKey, - aDerivedKeyType, aExtractable, - aKeyUsages); + return new DeriveKeyTask(aGlobal, aCx, aAlgorithm, + aBaseKey, aDerivedKeyType, + aExtractable, aKeyUsages); } if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) { - return new DeriveKeyTask(aCx, aAlgorithm, aBaseKey, - aDerivedKeyType, aExtractable, - aKeyUsages); + return new DeriveKeyTask(aGlobal, aCx, aAlgorithm, + aBaseKey, aDerivedKeyType, + aExtractable, aKeyUsages); } return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR); @@ -3573,7 +3562,8 @@ WebCryptoTask::CreateWrapKeyTask(JSContext* aCx, } WebCryptoTask* -WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx, +WebCryptoTask::CreateUnwrapKeyTask(nsIGlobalObject* aGlobal, + JSContext* aCx, const nsAString& aFormat, const ArrayBufferViewOrArrayBuffer& aWrappedKey, CryptoKey& aUnwrappingKey, @@ -3607,13 +3597,13 @@ WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx, keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) || keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HKDF) || keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) { - importTask = new ImportSymmetricKeyTask(aCx, aFormat, + importTask = new ImportSymmetricKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm, aExtractable, aKeyUsages); } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) || keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) || keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS)) { - importTask = new ImportRsaKeyTask(aCx, aFormat, + importTask = new ImportRsaKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm, aExtractable, aKeyUsages); } else { diff --git a/dom/crypto/WebCryptoTask.h b/dom/crypto/WebCryptoTask.h index a59f56ca9a..33cb6273ff 100644 --- a/dom/crypto/WebCryptoTask.h +++ b/dom/crypto/WebCryptoTask.h @@ -122,7 +122,8 @@ public: const ObjectOrString& aAlgorithm, const CryptoOperationData& aData); - static WebCryptoTask* CreateImportKeyTask(JSContext* aCx, + static WebCryptoTask* CreateImportKeyTask(nsIGlobalObject* aGlobal, + JSContext* aCx, const nsAString& aFormat, JS::Handle aKeyData, const ObjectOrString& aAlgorithm, @@ -130,12 +131,14 @@ public: const Sequence& aKeyUsages); static WebCryptoTask* CreateExportKeyTask(const nsAString& aFormat, CryptoKey& aKey); - static WebCryptoTask* CreateGenerateKeyTask(JSContext* aCx, + static WebCryptoTask* CreateGenerateKeyTask(nsIGlobalObject* aGlobal, + JSContext* aCx, const ObjectOrString& aAlgorithm, bool aExtractable, const Sequence& aKeyUsages); - static WebCryptoTask* CreateDeriveKeyTask(JSContext* aCx, + static WebCryptoTask* CreateDeriveKeyTask(nsIGlobalObject* aGlobal, + JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey, const ObjectOrString& aDerivedKeyType, @@ -151,7 +154,8 @@ public: CryptoKey& aKey, CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm); - static WebCryptoTask* CreateUnwrapKeyTask(JSContext* aCx, + static WebCryptoTask* CreateUnwrapKeyTask(nsIGlobalObject* aGlobal, + JSContext* aCx, const nsAString& aFormat, const ArrayBufferViewOrArrayBuffer& aWrappedKey, CryptoKey& aUnwrappingKey, diff --git a/dom/crypto/test/file_indexedDB.html b/dom/crypto/test/file_indexedDB.html new file mode 100644 index 0000000000..09e95b18fc --- /dev/null +++ b/dom/crypto/test/file_indexedDB.html @@ -0,0 +1,82 @@ + + + + + Bug 1188750 - WebCrypto must ensure NSS is initialized before deserializing + + + + + diff --git a/dom/crypto/test/mochitest.ini b/dom/crypto/test/mochitest.ini index 8bbffee853..4091334b7b 100644 --- a/dom/crypto/test/mochitest.ini +++ b/dom/crypto/test/mochitest.ini @@ -2,11 +2,15 @@ # Bug 1010743 - Re-enable WebCrypto tests on b2g skip-if = (buildapp == 'b2g') support-files = + file_indexedDB.html test-array.js test-vectors.js + test-worker.js test_WebCrypto.css util.js +[test_indexedDB.html] +skip-if = toolkit == 'android' # bug 1200570 [test_WebCrypto.html] [test_WebCrypto_DH.html] [test_WebCrypto_ECDH.html] @@ -18,4 +22,6 @@ support-files = [test_WebCrypto_Reject_Generating_Keys_Without_Usages.html] [test_WebCrypto_RSA_OAEP.html] [test_WebCrypto_RSA_PSS.html] +[test_WebCrypto_Structured_Cloning.html] +[test_WebCrypto_Workers.html] [test_WebCrypto_Wrap_Unwrap.html] diff --git a/dom/crypto/test/test-array.js b/dom/crypto/test/test-array.js index b24b338aa0..1297f8eb89 100644 --- a/dom/crypto/test/test-array.js +++ b/dom/crypto/test/test-array.js @@ -88,6 +88,34 @@ function Test(name, test) { }; } +function WorkerTest(worker, name, test) { + this.name = `${name} (Worker)`; + this.startTime = null; + this.endTime = null; + this.result = null; + this.row = null; + + this.run = function() { + // Note the start time + this.startTime = new Date(); + + // Send the test code to the worker. + worker.postMessage(test.toSource()); + + // We expect only boolean responses from the worker script. + worker.onmessage = e => this.complete(e.data); + worker.onerror = e => this.complete(false); + }; + + var base = new Test(name, test); + + // Inherit what we need from the |Test| class. We can't simply use its + // prototype as Test is just a function that can be used like a constructor. + for (var method of ["draw", "setRow", "next", "complete"]) { + this[method] = base[method].bind(this); + } +} + var TestArray = { tests: [], table: null, @@ -98,13 +126,18 @@ var TestArray = { fail: 0, pending: 0, currTest: 0, + worker: new Worker("test-worker.js"), addTest: function(name, testFn) { // Give it a reference to the array var test = new Test(name, testFn); test.ta = this; + // Add test to tests this.tests.push(test); + + // Run the test in a worker too. + this.tests.push(new WorkerTest(this.worker, name, testFn)); }, updateSummary: function() { @@ -174,6 +207,7 @@ function start() { MOCHITEST = ("SimpleTest" in window); if (MOCHITEST) { SimpleTest.waitForExplicitFinish(); + SimpleTest.requestLongerTimeout(2); window.addEventListener("load", function() { SimpleTest.waitForFocus(start); }); diff --git a/dom/crypto/test/test-vectors.js b/dom/crypto/test/test-vectors.js index 1a68e196af..abd1b52bc3 100644 --- a/dom/crypto/test/test-vectors.js +++ b/dom/crypto/test/test-vectors.js @@ -627,7 +627,7 @@ tv = { kty: "EC", crv: "P-384", d: "RT8f0pRw4CL1Tgk4rwuNnNbFoQBNTTBkr7WVLLm4fDA3boYZpNB_t-rbMVLx0CRp", - x: "_XwhXRnOzEfCsWIRCz3QLClaDkigQFvXmqYNdh/7vJdADykPbfGi1VgAu3XJdXoD", + x: "_XwhXRnOzEfCsWIRCz3QLClaDkigQFvXmqYNdh_7vJdADykPbfGi1VgAu3XJdXoD", y: "S1P_FBCXYGE-5VPvTCRnFT7bPIPmUPV9qKTM24TQFYEUgIDfzCLsyGCWK-rhP6jU" }, diff --git a/dom/crypto/test/test-worker.js b/dom/crypto/test/test-worker.js new file mode 100644 index 0000000000..02f248fae7 --- /dev/null +++ b/dom/crypto/test/test-worker.js @@ -0,0 +1,44 @@ +/* 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/. */ + +importScripts("util.js"); +importScripts("test-vectors.js"); + +var window = this; + +function finish(result) { + postMessage(result); +} + +function complete(test, valid) { + return function(x) { + if (valid) { + finish(valid(x)); + } else { + finish(true); + } + }; +} + +function memcmp_complete(test, value) { + return function (x) { + finish(util.memcmp(x, value)); + }; +} + +function error(test) { + return function (x) { + throw x; + }; +} + +onmessage = function (msg) { + var test = eval("(" + msg.data + ")"); + + try { + test.call({complete: finish}); + } catch (err) { + error(`Failed to run worker test: ${err}\n`); + } +}; diff --git a/dom/crypto/test/test_WebCrypto.html b/dom/crypto/test/test_WebCrypto.html index bbbd5d8292..4831fcfaa8 100644 --- a/dom/crypto/test/test_WebCrypto.html +++ b/dom/crypto/test/test_WebCrypto.html @@ -286,6 +286,7 @@ TestArray.addTest( .get(dbkey); req.onerror = error(that); req.onsuccess = complete(that, function(e) { + db.close(); return hasKeyFields(e.target.result.val); }); } @@ -972,6 +973,11 @@ TestArray.addTest( TestArray.addTest( "Test that we check keys before using them for encryption/signatures", function() { + // This test isn't supported in workers. + if (window.importScripts) { + return this.complete(true); + } + var that = this; function doCheckRSASSA() { diff --git a/dom/crypto/test/test_WebCrypto_JWK.html b/dom/crypto/test/test_WebCrypto_JWK.html index a079dcbc41..f9916f5f2d 100644 --- a/dom/crypto/test/test_WebCrypto_JWK.html +++ b/dom/crypto/test/test_WebCrypto_JWK.html @@ -177,8 +177,11 @@ TestArray.addTest( ); // ----------------------------------------------------------------------------- -function importExportRSAPrivateKey(jwk) { - return function () { +TestArray.addTest( + "JWK import/export of an RSA private key", + function () { + var jwk = tv.rsassa.jwk_priv; + var that = this; var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }; @@ -208,16 +211,43 @@ function importExportRSAPrivateKey(jwk) { error(that) ); } -} - -TestArray.addTest( - "JWK import/export of an RSA private key", - importExportRSAPrivateKey(tv.rsassa.jwk_priv) ); +// ----------------------------------------------------------------------------- TestArray.addTest( "JWK import/export of an RSA private key where p < q", - importExportRSAPrivateKey(tv.rsassa.jwk_priv_pLTq) + function () { + var jwk = tv.rsassa.jwk_priv_pLTq; + + var that = this; + var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }; + + function doExport(k) { + return crypto.subtle.exportKey("jwk", k); + } + + crypto.subtle.importKey("jwk", jwk, alg, true, ['sign']) + .then(doExport) + .then( + complete(that, function(x) { + return hasBaseJwkFields(x) && + hasFields(x, ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']) && + x.kty == 'RSA' && + x.alg == 'RS256' && + x.ext && + shallowArrayEquals(x.key_ops, ['sign']) && + x.n == jwk.n && + x.e == jwk.e && + x.d == jwk.d && + x.p == jwk.p && + x.q == jwk.q && + x.dp == jwk.dp && + x.dq == jwk.dq && + x.qi == jwk.qi; + }), + error(that) + ); + } ); // ----------------------------------------------------------------------------- diff --git a/dom/crypto/test/test_WebCrypto_Structured_Cloning.html b/dom/crypto/test/test_WebCrypto_Structured_Cloning.html new file mode 100644 index 0000000000..3275c54669 --- /dev/null +++ b/dom/crypto/test/test_WebCrypto_Structured_Cloning.html @@ -0,0 +1,305 @@ + + + + +WebCrypto Test Suite + + + + + + + + + + + + + + + + + + + + + +
+ + +
RUN ALL
+ +
+ Summary: + 0 passed, + 0 failed, + 0 pending. +
+
+ + + + + + + +
TestResultTime
+ +
+ + +
+ + + diff --git a/dom/crypto/test/test_WebCrypto_Workers.html b/dom/crypto/test/test_WebCrypto_Workers.html new file mode 100644 index 0000000000..0e8202d2fa --- /dev/null +++ b/dom/crypto/test/test_WebCrypto_Workers.html @@ -0,0 +1,116 @@ + + + + +WebCrypto Test Suite + + + + + + + + + + + + + + + + + + + + + +
+ + +
RUN ALL
+ +
+ Summary: + 0 passed, + 0 failed, + 0 pending. +
+
+ + + + + + + +
TestResultTime
+ +
+ + +
+ + + diff --git a/dom/crypto/test/test_indexedDB.html b/dom/crypto/test/test_indexedDB.html new file mode 100644 index 0000000000..0e09f1f98d --- /dev/null +++ b/dom/crypto/test/test_indexedDB.html @@ -0,0 +1,60 @@ + + + + + Bug 1188750 - WebCrypto must ensure NSS is initialized before deserializing + + + + + + + diff --git a/dom/crypto/test/util.js b/dom/crypto/test/util.js index 7a119533ac..f7b4c260f3 100644 --- a/dom/crypto/test/util.js +++ b/dom/crypto/test/util.js @@ -42,6 +42,44 @@ var util = { } return abv; }, + + clone: function (obj) { + return new Promise(resolve => { + let {port1, port2} = new MessageChannel(); + + // Wait for the cloned object to arrive. + port1.onmessage = msg => resolve(msg.data); + + // Clone the object. + port2.postMessage(obj); + }); + }, + + cloneExportCompareKeys: function (key) { + return util.clone(key).then(clone => { + var exports = []; + + if (key instanceof CryptoKey) { + exports.push(crypto.subtle.exportKey("raw", key)); + exports.push(crypto.subtle.exportKey("raw", clone)); + } else { + exports.push(crypto.subtle.exportKey("spki", key.publicKey)); + exports.push(crypto.subtle.exportKey("spki", clone.publicKey)); + exports.push(crypto.subtle.exportKey("pkcs8", key.privateKey)); + exports.push(crypto.subtle.exportKey("pkcs8", clone.privateKey)); + } + + return Promise.all(exports).then(pairs => { + for (var i = 0; i < pairs.length; i += 2) { + if (!util.memcmp(pairs[i], pairs[i + 1])) { + throw new Error("keys don't match"); + } + } + + return clone; + }); + }); + } }; function exists(x) { diff --git a/dom/events/DOMEventTargetHelper.cpp b/dom/events/DOMEventTargetHelper.cpp index b179407a63..e2bb760590 100644 --- a/dom/events/DOMEventTargetHelper.cpp +++ b/dom/events/DOMEventTargetHelper.cpp @@ -163,6 +163,27 @@ DOMEventTargetHelper::DisconnectFromOwner() } } +nsPIDOMWindow* +DOMEventTargetHelper::GetWindowIfCurrent() const +{ + if (NS_FAILED(CheckInnerWindowCorrectness())) { + return nullptr; + } + + return GetOwner(); +} + +nsIDocument* +DOMEventTargetHelper::GetDocumentIfCurrent() const +{ + nsPIDOMWindow* win = GetWindowIfCurrent(); + if (!win) { + return nullptr; + } + + return win->GetDoc(); +} + NS_IMETHODIMP DOMEventTargetHelper::RemoveEventListener(const nsAString& aType, nsIDOMEventListener* aListener, @@ -359,11 +380,10 @@ DOMEventTargetHelper::GetContextForEventHandlers(nsresult* aRv) nsresult DOMEventTargetHelper::WantsUntrusted(bool* aRetVal) { - nsresult rv; - nsIScriptContext* context = GetContextForEventHandlers(&rv); + nsresult rv = CheckInnerWindowCorrectness(); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr doc = - nsContentUtils::GetDocumentFromScriptContext(context); + + nsCOMPtr doc = GetDocumentIfCurrent(); // We can let listeners on workers to always handle all the events. *aRetVal = (doc && !nsContentUtils::IsChromeDoc(doc)) || !NS_IsMainThread(); return rv; diff --git a/dom/events/DOMEventTargetHelper.h b/dom/events/DOMEventTargetHelper.h index ca0fdc3c1c..cba8fd9da3 100644 --- a/dom/events/DOMEventTargetHelper.h +++ b/dom/events/DOMEventTargetHelper.h @@ -20,6 +20,7 @@ #include "mozilla/dom/EventTarget.h" struct JSCompartment; +class nsIDocument; namespace mozilla { @@ -126,7 +127,7 @@ public: return nsPIDOMWindow::GetOuterFromCurrentInner(GetOwner()); } - nsresult CheckInnerWindowCorrectness() + nsresult CheckInnerWindowCorrectness() const { NS_ENSURE_STATE(!mHasOrHasHadOwnerWindow || mOwnerWindow); if (mOwnerWindow && !mOwnerWindow->IsCurrentInnerWindow()) { @@ -136,6 +137,12 @@ public: } nsPIDOMWindow* GetOwner() const { return mOwnerWindow; } + // Like GetOwner, but only returns non-null if the window being returned is + // current (in the "current document" sense of the HTML spec). + nsPIDOMWindow* GetWindowIfCurrent() const; + // Returns the document associated with this event target, if that document is + // the current document of its browsing context. Will return null otherwise. + nsIDocument* GetDocumentIfCurrent() const; void BindToOwner(nsIGlobalObject* aOwner); void BindToOwner(nsPIDOMWindow* aOwner); void BindToOwner(DOMEventTargetHelper* aOther); diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index c35a2c27df..2ed4420ec7 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -21,6 +21,7 @@ #include "nsStringStream.h" #include "mozilla/ErrorResult.h" +#include "mozilla/dom/BodyUtil.h" #include "mozilla/dom/EncodingUtils.h" #include "mozilla/dom/Exceptions.h" #include "mozilla/dom/FetchDriver.h" @@ -42,7 +43,6 @@ #include "WorkerRunnable.h" #include "WorkerScope.h" #include "Workers.h" -#include "FetchUtil.h" namespace mozilla { namespace dom { @@ -451,7 +451,7 @@ ExtractFromURLSearchParams(const URLSearchParams& aParams, nsAutoString serialized; aParams.Stringify(serialized); aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8"); - return NS_NewStringInputStream(aStream, serialized); + return NS_NewCStringInputStream(aStream, NS_ConvertUTF16toUTF8(serialized)); } } // namespace @@ -1012,7 +1012,7 @@ FetchBody::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength switch (mConsumeType) { case CONSUME_ARRAYBUFFER: { JS::Rooted arrayBuffer(cx); - FetchUtil::ConsumeArrayBuffer(cx, &arrayBuffer, aResultLength, aResult, + BodyUtil::ConsumeArrayBuffer(cx, &arrayBuffer, aResultLength, aResult, error); if (!error.Failed()) { @@ -1026,7 +1026,7 @@ FetchBody::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength break; } case CONSUME_BLOB: { - RefPtr blob = FetchUtil::ConsumeBlob( + RefPtr blob = BodyUtil::ConsumeBlob( DerivedClass()->GetParentObject(), NS_ConvertUTF8toUTF16(mMimeType), aResultLength, aResult, error); if (!error.Failed()) { @@ -1041,7 +1041,7 @@ FetchBody::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength data.Adopt(reinterpret_cast(aResult), aResultLength); autoFree.Reset(); - RefPtr fd = FetchUtil::ConsumeFormData( + RefPtr fd = BodyUtil::ConsumeFormData( DerivedClass()->GetParentObject(), mMimeType, data, error); if (!error.Failed()) { @@ -1053,12 +1053,12 @@ FetchBody::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength // fall through handles early exit. case CONSUME_JSON: { nsString decoded; - if (NS_SUCCEEDED(FetchUtil::ConsumeText(aResultLength, aResult, decoded))) { + if (NS_SUCCEEDED(BodyUtil::ConsumeText(aResultLength, aResult, decoded))) { if (mConsumeType == CONSUME_TEXT) { localPromise->MaybeResolve(decoded); } else { JS::Rooted json(cx); - FetchUtil::ConsumeJson(cx, &json, decoded, error); + BodyUtil::ConsumeJson(cx, &json, decoded, error); if (!error.Failed()) { localPromise->MaybeResolve(cx, json); } diff --git a/dom/fetch/FetchUtil.cpp b/dom/fetch/FetchUtil.cpp index eb2a6f95a2..6d1c27e99e 100644 --- a/dom/fetch/FetchUtil.cpp +++ b/dom/fetch/FetchUtil.cpp @@ -9,55 +9,6 @@ namespace mozilla { namespace dom { -namespace { -class StreamDecoder final -{ - nsCOMPtr mDecoder; - nsString mDecoded; - -public: - StreamDecoder() - : mDecoder(EncodingUtils::DecoderForEncoding("UTF-8")) - { - MOZ_ASSERT(mDecoder); - } - - nsresult - AppendText(const char* aSrcBuffer, uint32_t aSrcBufferLen) - { - int32_t destBufferLen; - nsresult rv = - mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen, &destBufferLen); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!mDecoded.SetCapacity(mDecoded.Length() + destBufferLen, fallible)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - char16_t* destBuffer = mDecoded.BeginWriting() + mDecoded.Length(); - int32_t totalChars = mDecoded.Length(); - - int32_t srcLen = (int32_t) aSrcBufferLen; - int32_t outLen = destBufferLen; - rv = mDecoder->Convert(aSrcBuffer, &srcLen, destBuffer, &outLen); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - totalChars += outLen; - mDecoded.SetLength(totalChars); - - return NS_OK; - } - - nsString& - GetText() - { - return mDecoded; - } -}; -} - // static nsresult FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod) @@ -86,42 +37,6 @@ FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod return NS_OK; } -// static -void -FetchUtil::ConsumeArrayBuffer(JSContext* aCx, - JS::MutableHandle aValue, - uint32_t aInputLength, uint8_t* aInput, - ErrorResult& aRv) -{ - JS::Rooted arrayBuffer(aCx); - arrayBuffer = JS_NewArrayBufferWithContents(aCx, aInputLength, - reinterpret_cast(aInput)); - if (!arrayBuffer) { - JS_ClearPendingException(aCx); - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return; - } - aValue.set(arrayBuffer); -} - -// static -already_AddRefed -FetchUtil::ConsumeBlob(nsISupports* aParent, const nsString& aMimeType, - uint32_t aInputLength, uint8_t* aInput, - ErrorResult& aRv) -{ - RefPtr blob = - Blob::CreateMemoryBlob(aParent, - reinterpret_cast(aInput), aInputLength, - aMimeType); - - if (!blob) { - aRv.Throw(NS_ERROR_DOM_UNKNOWN_ERR); - return nullptr; - } - return blob.forget(); -} - static bool FindCRLF(nsACString::const_iterator& aStart, nsACString::const_iterator& aEnd) @@ -189,450 +104,5 @@ FetchUtil::ExtractHeader(nsACString::const_iterator& aStart, return PushOverLine(aStart); } -namespace { -class MOZ_STACK_CLASS FillFormIterator final - : public URLSearchParams::ForEachIterator -{ -public: - explicit FillFormIterator(FormData* aFormData) - : mFormData(aFormData) - { - MOZ_ASSERT(aFormData); - } - - bool URLParamsIterator(const nsString& aName, - const nsString& aValue) override - { - ErrorResult rv; - mFormData->Append(aName, aValue, rv); - MOZ_ASSERT(!rv.Failed()); - return true; - } - -private: - FormData* mFormData; -}; - -/** - * A simple multipart/form-data parser as defined in RFC 2388 and RFC 2046. - * This does not respect any encoding specified per entry, using UTF-8 - * throughout. This is as the Fetch spec states in the consume body algorithm. - * Borrows some things from Necko's nsMultiMixedConv, but is simpler since - * unlike Necko we do not have to deal with receiving incomplete chunks of data. - * - * This parser will fail the entire parse on any invalid entry, so it will - * never return a partially filled FormData. - * The content-disposition header is used to figure out the name and filename - * entries. The inclusion of the filename parameter decides if the entry is - * inserted into the FormData as a string or a File. - * - * File blobs are copies of the underlying data string since we cannot adopt - * char* chunks embedded within the larger body without significant effort. - * FIXME(nsm): Bug 1127552 - We should add telemetry to calls to formData() and - * friends to figure out if Fetch ends up copying big blobs to see if this is - * worth optimizing. - */ -class MOZ_STACK_CLASS FormDataParser -{ -private: - RefPtr mFormData; - nsCString mMimeType; - nsCString mData; - - // Entry state, reset in START_PART. - nsCString mName; - nsCString mFilename; - nsCString mContentType; - - enum - { - START_PART, - PARSE_HEADER, - PARSE_BODY, - } mState; - - nsIGlobalObject* mParentObject; - - // Reads over a boundary and sets start to the position after the end of the - // boundary. Returns false if no boundary is found immediately. - bool - PushOverBoundary(const nsACString& aBoundaryString, - nsACString::const_iterator& aStart, - nsACString::const_iterator& aEnd) - { - // We copy the end iterator to keep the original pointing to the real end - // of the string. - nsACString::const_iterator end(aEnd); - const char* beginning = aStart.get(); - if (FindInReadable(aBoundaryString, aStart, end)) { - // We either should find the body immediately, or after 2 chars with the - // 2 chars being '-', everything else is failure. - if ((aStart.get() - beginning) == 0) { - aStart.advance(aBoundaryString.Length()); - return true; - } - - if ((aStart.get() - beginning) == 2) { - if (*(--aStart) == '-' && *(--aStart) == '-') { - aStart.advance(aBoundaryString.Length() + 2); - return true; - } - } - } - - return false; - } - - bool - ParseHeader(nsACString::const_iterator& aStart, - nsACString::const_iterator& aEnd, - bool* aWasEmptyHeader) - { - nsAutoCString headerName, headerValue; - if (!FetchUtil::ExtractHeader(aStart, aEnd, - headerName, headerValue, - aWasEmptyHeader)) { - return false; - } - if (*aWasEmptyHeader) { - return true; - } - - if (headerName.LowerCaseEqualsLiteral("content-disposition")) { - nsCCharSeparatedTokenizer tokenizer(headerValue, ';'); - bool seenFormData = false; - while (tokenizer.hasMoreTokens()) { - const nsDependentCSubstring& token = tokenizer.nextToken(); - if (token.IsEmpty()) { - continue; - } - - if (token.EqualsLiteral("form-data")) { - seenFormData = true; - continue; - } - - if (seenFormData && - StringBeginsWith(token, NS_LITERAL_CSTRING("name="))) { - mName = StringTail(token, token.Length() - 5); - mName.Trim(" \""); - continue; - } - - if (seenFormData && - StringBeginsWith(token, NS_LITERAL_CSTRING("filename="))) { - mFilename = StringTail(token, token.Length() - 9); - mFilename.Trim(" \""); - continue; - } - } - - if (mName.IsVoid()) { - // Could not parse a valid entry name. - return false; - } - } else if (headerName.LowerCaseEqualsLiteral("content-type")) { - mContentType = headerValue; - } - - return true; - } - - // The end of a body is marked by a CRLF followed by the boundary. So the - // CRLF is part of the boundary and not the body, but any prior CRLFs are - // part of the body. This will position the iterator at the beginning of the - // boundary (after the CRLF). - bool - ParseBody(const nsACString& aBoundaryString, - nsACString::const_iterator& aStart, - nsACString::const_iterator& aEnd) - { - const char* beginning = aStart.get(); - - // Find the boundary marking the end of the body. - nsACString::const_iterator end(aEnd); - if (!FindInReadable(aBoundaryString, aStart, end)) { - return false; - } - - // We found a boundary, strip the just prior CRLF, and consider - // everything else the body section. - if (aStart.get() - beginning < 2) { - // Only the first entry can have a boundary right at the beginning. Even - // an empty body will have a CRLF before the boundary. So this is - // a failure. - return false; - } - - // Check that there is a CRLF right before the boundary. - aStart.advance(-2); - - // Skip optional hyphens. - if (*aStart == '-' && *(aStart.get()+1) == '-') { - if (aStart.get() - beginning < 2) { - return false; - } - - aStart.advance(-2); - } - - if (*aStart != nsCRT::CR || *(aStart.get()+1) != nsCRT::LF) { - return false; - } - - nsAutoCString body(beginning, aStart.get() - beginning); - - // Restore iterator to after the \r\n as we promised. - // We do not need to handle the extra hyphens case since our boundary - // parser in PushOverBoundary() - aStart.advance(2); - - if (!mFormData) { - mFormData = new FormData(); - } - - NS_ConvertUTF8toUTF16 name(mName); - - if (mFilename.IsVoid()) { - ErrorResult rv; - mFormData->Append(name, NS_ConvertUTF8toUTF16(body), rv); - MOZ_ASSERT(!rv.Failed()); - } else { - // Unfortunately we've to copy the data first since all our strings are - // going to free it. We also need fallible alloc, so we can't just use - // ToNewCString(). - char* copy = static_cast(moz_xmalloc(body.Length())); - if (!copy) { - NS_WARNING("Failed to copy File entry body."); - return false; - } - nsCString::const_iterator bodyIter, bodyEnd; - body.BeginReading(bodyIter); - body.EndReading(bodyEnd); - char *p = copy; - while (bodyIter != bodyEnd) { - *p++ = *bodyIter++; - } - p = nullptr; - - RefPtr file = - File::CreateMemoryFile(mParentObject, - reinterpret_cast(copy), body.Length(), - NS_ConvertUTF8toUTF16(mFilename), - NS_ConvertUTF8toUTF16(mContentType), /* aLastModifiedDate */ 0); - Optional dummy; - ErrorResult rv; - mFormData->Append(name, *file, dummy, rv); - if (NS_WARN_IF(rv.Failed())) { - return false; - } - } - - return true; - } - -public: - FormDataParser(const nsACString& aMimeType, const nsACString& aData, nsIGlobalObject* aParent) - : mMimeType(aMimeType), mData(aData), mState(START_PART), mParentObject(aParent) - { - } - - bool - Parse() - { - // Determine boundary from mimetype. - const char* boundaryId = nullptr; - boundaryId = strstr(mMimeType.BeginWriting(), "boundary"); - if (!boundaryId) { - return false; - } - - boundaryId = strchr(boundaryId, '='); - if (!boundaryId) { - return false; - } - - // Skip over '='. - boundaryId++; - - char *attrib = (char *) strchr(boundaryId, ';'); - if (attrib) *attrib = '\0'; - - nsAutoCString boundaryString(boundaryId); - if (attrib) *attrib = ';'; - - boundaryString.Trim(" \""); - - if (boundaryString.Length() == 0) { - return false; - } - - nsACString::const_iterator start, end; - mData.BeginReading(start); - // This should ALWAYS point to the end of data. - // Helpers make copies. - mData.EndReading(end); - - while (start != end) { - switch(mState) { - case START_PART: - mName.SetIsVoid(true); - mFilename.SetIsVoid(true); - mContentType = NS_LITERAL_CSTRING("text/plain"); - - // MUST start with boundary. - if (!PushOverBoundary(boundaryString, start, end)) { - return false; - } - - if (start != end && *start == '-') { - // End of data. - if (!mFormData) { - mFormData = new FormData(); - } - return true; - } - - if (!PushOverLine(start)) { - return false; - } - mState = PARSE_HEADER; - break; - - case PARSE_HEADER: - bool emptyHeader; - if (!ParseHeader(start, end, &emptyHeader)) { - return false; - } - - if (emptyHeader && !PushOverLine(start)) { - return false; - } - - mState = emptyHeader ? PARSE_BODY : PARSE_HEADER; - break; - - case PARSE_BODY: - if (mName.IsVoid()) { - NS_WARNING("No content-disposition header with a valid name was " - "found. Failing at body parse."); - return false; - } - - if (!ParseBody(boundaryString, start, end)) { - return false; - } - - mState = START_PART; - break; - - default: - MOZ_CRASH("Invalid case"); - } - } - - NS_NOTREACHED("Should never reach here."); - return false; - } - - already_AddRefed GetFormData() - { - return mFormData.forget(); - } -}; -} - -// static -already_AddRefed -FetchUtil::ConsumeFormData(nsIGlobalObject* aParent, const nsCString& aMimeType, - const nsCString& aStr, ErrorResult& aRv) -{ - NS_NAMED_LITERAL_CSTRING(formDataMimeType, "multipart/form-data"); - - // Allow semicolon separated boundary/encoding suffix like multipart/form-data; boundary= - // but disallow multipart/form-datafoobar. - bool isValidFormDataMimeType = StringBeginsWith(aMimeType, formDataMimeType); - - if (isValidFormDataMimeType && aMimeType.Length() > formDataMimeType.Length()) { - isValidFormDataMimeType = aMimeType[formDataMimeType.Length()] == ';'; - } - - if (isValidFormDataMimeType) { - FormDataParser parser(aMimeType, aStr, aParent); - if (!parser.Parse()) { - aRv.ThrowTypeError(); - return nullptr; - } - - RefPtr fd = parser.GetFormData(); - MOZ_ASSERT(fd); - return fd.forget(); - } - - NS_NAMED_LITERAL_CSTRING(urlDataMimeType, "application/x-www-form-urlencoded"); - bool isValidUrlEncodedMimeType = StringBeginsWith(aMimeType, urlDataMimeType); - - if (isValidUrlEncodedMimeType && aMimeType.Length() > urlDataMimeType.Length()) { - isValidUrlEncodedMimeType = aMimeType[urlDataMimeType.Length()] == ';'; - } - - if (isValidUrlEncodedMimeType) { - URLParams params; - params.ParseInput(aStr); - - RefPtr fd = new FormData(aParent); - FillFormIterator iterator(fd); - DebugOnly status = params.ForEach(iterator); - MOZ_ASSERT(status); - - return fd.forget(); - } - - aRv.ThrowTypeError(); - return nullptr; -} - -// static -nsresult -FetchUtil::ConsumeText(uint32_t aInputLength, uint8_t* aInput, - nsString& aText) -{ - StreamDecoder decoder; - nsresult rv = decoder.AppendText(reinterpret_cast(aInput), - aInputLength); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - aText = decoder.GetText(); - return NS_OK; -} - -// static -void -FetchUtil::ConsumeJson(JSContext* aCx, JS::MutableHandle aValue, - const nsString& aStr, ErrorResult& aRv) -{ - aRv.MightThrowJSException(); - - AutoForceSetExceptionOnContext forceExn(aCx); - JS::Rooted json(aCx); - if (!JS_ParseJSON(aCx, aStr.get(), aStr.Length(), &json)) { - if (!JS_IsExceptionPending(aCx)) { - aRv.Throw(NS_ERROR_DOM_UNKNOWN_ERR); - return; - } - - JS::Rooted exn(aCx); - DebugOnly gotException = JS_GetPendingException(aCx, &exn); - MOZ_ASSERT(gotException); - - JS_ClearPendingException(aCx); - aRv.ThrowJSException(aCx, exn); - return; - } - - aValue.set(json); -} - } // namespace dom } // namespace mozilla diff --git a/dom/fetch/FetchUtil.h b/dom/fetch/FetchUtil.h index 53d41383c3..d99aa39b44 100644 --- a/dom/fetch/FetchUtil.h +++ b/dom/fetch/FetchUtil.h @@ -26,46 +26,6 @@ public: static nsresult GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod); - /** - * Creates an array buffer from an array, assigning the result to |aValue|. - * The array buffer takes ownership of |aInput|, which must be allocated - * by |malloc|. - */ - static void - ConsumeArrayBuffer(JSContext* aCx, JS::MutableHandle aValue, - uint32_t aInputLength, uint8_t* aInput, ErrorResult& aRv); - - /** - * Creates an in-memory blob from an array. The blob takes ownership of - * |aInput|, which must be allocated by |malloc|. - */ - static already_AddRefed - ConsumeBlob(nsISupports* aParent, const nsString& aMimeType, - uint32_t aInputLength, uint8_t* aInput, ErrorResult& aRv); - - /** - * Creates a form data object from a UTF-8 encoded |aStr|. Returns |nullptr| - * and sets |aRv| to MSG_BAD_FORMDATA if |aStr| contains invalid data. - */ - static already_AddRefed - ConsumeFormData(nsIGlobalObject* aParent, const nsCString& aMimeType, - const nsCString& aStr, ErrorResult& aRv); - - /** - * UTF-8 decodes |aInput| into |aText|. The caller may free |aInput| - * once this method returns. - */ - static nsresult - ConsumeText(uint32_t aInputLength, uint8_t* aInput, nsString& aText); - - /** - * Parses a UTF-8 encoded |aStr| as JSON, assigning the result to |aValue|. - * Sets |aRv| to a syntax error if |aStr| contains invalid data. - */ - static void - ConsumeJson(JSContext* aCx, JS::MutableHandle aValue, - const nsString& aStr, ErrorResult& aRv); - /** * Extracts an HTTP header from a substring range. */ diff --git a/dom/filehandle/ActorsParent.cpp b/dom/filehandle/ActorsParent.cpp index 2a5a9942b9..3f708bf1ba 100644 --- a/dom/filehandle/ActorsParent.cpp +++ b/dom/filehandle/ActorsParent.cpp @@ -897,7 +897,7 @@ FileHandleThreadPool::Enqueue(FileHandle* aFileHandle, if (aFileHandleOp) { fileHandleQueue->Enqueue(aFileHandleOp); if (aFinish) { - existingFileHandleQueue->Finish(); + fileHandleQueue->Finish(); } } } diff --git a/dom/push/PushNotifier.cpp b/dom/push/PushNotifier.cpp index d41295d077..4767b76152 100644 --- a/dom/push/PushNotifier.cpp +++ b/dom/push/PushNotifier.cpp @@ -15,8 +15,8 @@ #include "mozilla/Services.h" #include "mozilla/unused.h" +#include "mozilla/dom/BodyUtil.h" #include "mozilla/dom/ContentParent.h" -#include "mozilla/dom/FetchUtil.h" namespace mozilla { namespace dom { @@ -341,7 +341,7 @@ PushMessage::EnsureDecodedText() if (mData.IsEmpty() || !mDecodedText.IsEmpty()) { return NS_OK; } - nsresult rv = FetchUtil::ConsumeText( + nsresult rv = BodyUtil::ConsumeText( mData.Length(), reinterpret_cast(mData.Elements()), mDecodedText @@ -373,7 +373,7 @@ PushMessage::Json(JSContext* aCx, return rv; } ErrorResult error; - FetchUtil::ConsumeJson(aCx, aResult, mDecodedText, error); + BodyUtil::ConsumeJson(aCx, aResult, mDecodedText, error); if (error.Failed()) { return error.StealNSResult(); } diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index d1365bf542..6e61ac0356 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -43,8 +43,8 @@ #include "nsIUnicodeDecoder.h" #include "nsIUnicodeEncoder.h" +#include "mozilla/dom/BodyUtil.h" #include "mozilla/dom/EncodingUtils.h" -#include "mozilla/dom/FetchUtil.h" #include "mozilla/dom/TypedArray.h" #endif @@ -1037,7 +1037,7 @@ PushMessageData::Json(JSContext* cx, JS::MutableHandle aRetval, aRv.Throw(NS_ERROR_DOM_UNKNOWN_ERR); return; } - FetchUtil::ConsumeJson(cx, aRetval, mDecodedText, aRv); + BodyUtil::ConsumeJson(cx, aRetval, mDecodedText, aRv); } void @@ -1055,7 +1055,7 @@ PushMessageData::ArrayBuffer(JSContext* cx, { uint8_t* data = GetContentsCopy(); if (data) { - FetchUtil::ConsumeArrayBuffer(cx, aRetval, mBytes.Length(), data, aRv); + BodyUtil::ConsumeArrayBuffer(cx, aRetval, mBytes.Length(), data, aRv); } } @@ -1064,7 +1064,7 @@ PushMessageData::Blob(ErrorResult& aRv) { uint8_t* data = GetContentsCopy(); if (data) { - RefPtr blob = FetchUtil::ConsumeBlob( + RefPtr blob = BodyUtil::ConsumeBlob( mOwner, EmptyString(), mBytes.Length(), data, aRv); if (blob) { return blob.forget(); @@ -1079,7 +1079,7 @@ PushMessageData::EnsureDecodedText() if (mBytes.IsEmpty() || !mDecodedText.IsEmpty()) { return NS_OK; } - nsresult rv = FetchUtil::ConsumeText( + nsresult rv = BodyUtil::ConsumeText( mBytes.Length(), reinterpret_cast(mBytes.Elements()), mDecodedText diff --git a/dom/workers/ServiceWorkerScriptCache.cpp b/dom/workers/ServiceWorkerScriptCache.cpp index 611afb4c47..f73c1797ea 100644 --- a/dom/workers/ServiceWorkerScriptCache.cpp +++ b/dom/workers/ServiceWorkerScriptCache.cpp @@ -537,7 +537,8 @@ private: ErrorResult result; nsCOMPtr body; - result = NS_NewStringInputStream(getter_AddRefs(body), mCN->Buffer()); + result = NS_NewCStringInputStream(getter_AddRefs(body), + NS_ConvertUTF16toUTF8(mCN->Buffer())); if (NS_WARN_IF(result.Failed())) { MOZ_ASSERT(!result.IsErrorWithMessage()); Fail(result.StealNSResult()); diff --git a/editor/libeditor/nsHTMLDataTransfer.cpp b/editor/libeditor/nsHTMLDataTransfer.cpp index 51b9e23635..cea880433f 100644 --- a/editor/libeditor/nsHTMLDataTransfer.cpp +++ b/editor/libeditor/nsHTMLDataTransfer.cpp @@ -1658,9 +1658,6 @@ NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation(int32_t aSelectionType) NS_IMETHODIMP nsHTMLEditor::InsertTextWithQuotations(const nsAString &aStringToInsert) { - if (mWrapToWindow) - return InsertText(aStringToInsert); - // The whole operation should be undoable in one transaction: BeginTransaction(); @@ -1761,9 +1758,6 @@ nsHTMLEditor::InsertAsPlaintextQuotation(const nsAString & aQuotedText, bool aAddCites, nsIDOMNode **aNodeInserted) { - if (mWrapToWindow) - return nsPlaintextEditor::InsertAsQuotation(aQuotedText, aNodeInserted); - // get selection RefPtr selection = GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); diff --git a/editor/libeditor/nsPlaintextEditor.cpp b/editor/libeditor/nsPlaintextEditor.cpp index 65c37f037d..ac74806435 100644 --- a/editor/libeditor/nsPlaintextEditor.cpp +++ b/editor/libeditor/nsPlaintextEditor.cpp @@ -69,7 +69,6 @@ using namespace mozilla::dom; nsPlaintextEditor::nsPlaintextEditor() : nsEditor() , mRules(nullptr) -, mWrapToWindow(false) , mWrapColumn(0) , mMaxTextLength(-1) , mInitTriggerCounter(0) @@ -1045,27 +1044,17 @@ nsPlaintextEditor::SetWrapWidth(int32_t aWrapColumn) if (IsWrapHackEnabled() && aWrapColumn >= 0) styleValue.AppendLiteral("font-family: -moz-fixed; "); - // If "mail.compose.wrap_to_window_width" is set, and we're a mail editor, - // then remember our wrap width (for output purposes) but set the visual - // wrapping to window width. - // We may reset mWrapToWindow here, based on the pref's current value. - if (IsMailEditor()) - { - mWrapToWindow = - Preferences::GetBool("mail.compose.wrap_to_window_width", mWrapToWindow); - } - // and now we're ready to set the new whitespace/wrapping style. - if (aWrapColumn > 0 && !mWrapToWindow) // Wrap to a fixed column - { + if (aWrapColumn > 0) { + // Wrap to a fixed column. styleValue.AppendLiteral("white-space: pre-wrap; width: "); styleValue.AppendInt(aWrapColumn); styleValue.AppendLiteral("ch;"); - } - else if (mWrapToWindow || aWrapColumn == 0) + } else if (aWrapColumn == 0) { styleValue.AppendLiteral("white-space: pre-wrap;"); - else + } else { styleValue.AppendLiteral("white-space: pre;"); + } return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::style, styleValue, true); } diff --git a/editor/libeditor/nsPlaintextEditor.h b/editor/libeditor/nsPlaintextEditor.h index 8df00eaa5e..6975aeedd9 100644 --- a/editor/libeditor/nsPlaintextEditor.h +++ b/editor/libeditor/nsPlaintextEditor.h @@ -218,7 +218,6 @@ protected: protected: nsCOMPtr mRules; - bool mWrapToWindow; int32_t mWrapColumn; int32_t mMaxTextLength; int32_t mInitTriggerCounter; diff --git a/gfx/gl/AndroidSurfaceTexture.cpp b/gfx/gl/AndroidSurfaceTexture.cpp index e7fa1cc21c..0e7611c9e9 100644 --- a/gfx/gl/AndroidSurfaceTexture.cpp +++ b/gfx/gl/AndroidSurfaceTexture.cpp @@ -6,7 +6,6 @@ #ifdef MOZ_WIDGET_ANDROID -#include #include #include #include "AndroidSurfaceTexture.h" @@ -27,9 +26,57 @@ using namespace mozilla::widget::sdk; namespace mozilla { namespace gl { -// UGH -static std::map sInstances; -static int sNextID = 0; +// Maintains a mapping between AndroidSurfaceTexture instances and their +// unique numerical IDs. [thread-safe] +class InstanceMap +{ + typedef AndroidSurfaceTexture* InstancePtr; + typedef std::map MapType; + +public: + InstanceMap() + : mNextId(0) + , mMonitor("AndroidSurfaceTexture::InstanceMap::mMonitor") + {} + + int Add(InstancePtr aInstance) + { + MonitorAutoLock lock(mMonitor); + mInstances.insert({++mNextId, aInstance}); + return mNextId; + } + + void Remove(int aId) + { + MonitorAutoLock lock(mMonitor); + mInstances.erase(aId); + } + + InstancePtr Get(int aId) const + { + MonitorAutoLock lock(mMonitor); + + auto it = mInstances.find(aId); + if (it == mInstances.end()) { + return nullptr; + } + return it->second; + } + +private: + MapType mInstances; + int mNextId; + + mutable Monitor mMonitor; +}; + +static InstanceMap sInstances; + +AndroidSurfaceTexture* +AndroidSurfaceTexture::Find(int aId) +{ + return sInstances.Get(aId); +} static bool IsSTSupported() @@ -59,19 +106,6 @@ AndroidSurfaceTexture::Create(GLContext* aContext, GLuint aTexture) return st.forget(); } -AndroidSurfaceTexture* -AndroidSurfaceTexture::Find(int id) -{ - std::map::iterator it; - - it = sInstances.find(id); - if (it == sInstances.end()) - return nullptr; - - return it->second; -} - - nsresult AndroidSurfaceTexture::Attach(GLContext* aContext, PRIntervalTime aTimeout) { @@ -170,8 +204,7 @@ AndroidSurfaceTexture::Init(GLContext* aContext, GLuint aTexture) mSurface.Get()); MOZ_ASSERT(mNativeWindow, "Failed to create native window from surface"); - mID = ++sNextID; - sInstances.insert(std::pair(mID, this)); + mID = sInstances.Add(this); return true; } @@ -180,15 +213,15 @@ AndroidSurfaceTexture::AndroidSurfaceTexture() : mTexture(0) , mSurfaceTexture() , mSurface() - , mMonitor("AndroidSurfaceTexture::mContextMonitor") , mAttachedContext(nullptr) , mCanDetach(false) + , mMonitor("AndroidSurfaceTexture::mContextMonitor") { } AndroidSurfaceTexture::~AndroidSurfaceTexture() { - sInstances.erase(mID); + sInstances.Remove(mID); mFrameAvailableCallback = nullptr; @@ -205,7 +238,7 @@ AndroidSurfaceTexture::UpdateTexImage() } void -AndroidSurfaceTexture::GetTransformMatrix(gfx::Matrix4x4& aMatrix) +AndroidSurfaceTexture::GetTransformMatrix(gfx::Matrix4x4& aMatrix) const { JNIEnv* const env = jni::GetEnvForThread(); diff --git a/gfx/gl/AndroidSurfaceTexture.h b/gfx/gl/AndroidSurfaceTexture.h index 03639c5fe9..fcff8f2934 100644 --- a/gfx/gl/AndroidSurfaceTexture.h +++ b/gfx/gl/AndroidSurfaceTexture.h @@ -44,7 +44,7 @@ public: // Android Jelly Bean. static already_AddRefed Create(); - static AndroidSurfaceTexture* Find(int id); + static AndroidSurfaceTexture* Find(int aId); // If we are on Jelly Bean, the SurfaceTexture can be detached and reattached // to allow consumption from different GLContexts. It is recommended to only @@ -58,19 +58,19 @@ public: // Ability to detach is based on API version (16+), and we also block PowerVR since it has some type // of fencing problem. Bug 1100126. - bool CanDetach() { return mCanDetach; } + bool CanDetach() const { return mCanDetach; } - GLContext* GetAttachedContext() { return mAttachedContext; } + GLContext* AttachedContext() const { return mAttachedContext; } - AndroidNativeWindow* NativeWindow() { + AndroidNativeWindow* NativeWindow() const { return mNativeWindow; } // This attaches the updated data to the TEXTURE_EXTERNAL target void UpdateTexImage(); - void GetTransformMatrix(mozilla::gfx::Matrix4x4& aMatrix); - int ID() { return mID; } + void GetTransformMatrix(mozilla::gfx::Matrix4x4& aMatrix) const; + int ID() const { return mID; } void SetDefaultSize(mozilla::gfx::IntSize size); @@ -82,8 +82,8 @@ public: // callback from the underlying SurfaceTexture instance void NotifyFrameAvailable(); - GLuint Texture() { return mTexture; } - const widget::sdk::Surface::Ref& JavaSurface() { return mSurface; } + GLuint Texture() const { return mTexture; } + const widget::sdk::Surface::Ref& JavaSurface() const { return mSurface; } private: AndroidSurfaceTexture(); @@ -96,13 +96,14 @@ private: widget::sdk::SurfaceTexture::GlobalRef mSurfaceTexture; widget::sdk::Surface::GlobalRef mSurface; - Monitor mMonitor; GLContext* mAttachedContext; bool mCanDetach; RefPtr mNativeWindow; int mID; nsCOMPtr mFrameAvailableCallback; + + mutable Monitor mMonitor; }; } diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index 94fb03c877..a839e7b074 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -103,6 +103,7 @@ static const char *sExtensionNames[] = { "GL_ARB_texture_float", "GL_ARB_texture_non_power_of_two", "GL_ARB_texture_rectangle", + "GL_ARB_texture_rg", "GL_ARB_texture_storage", "GL_ARB_texture_swizzle", "GL_ARB_timer_query", diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h index 44ac106036..8a0dc6777d 100644 --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -137,6 +137,7 @@ enum class GLFeature { texture_half_float, texture_half_float_linear, texture_non_power_of_two, + texture_rg, texture_storage, texture_swizzle, transform_feedback2, @@ -425,6 +426,7 @@ public: ARB_texture_float, ARB_texture_non_power_of_two, ARB_texture_rectangle, + ARB_texture_rg, ARB_texture_storage, ARB_texture_swizzle, ARB_timer_query, diff --git a/gfx/gl/GLContextEGL.h b/gfx/gl/GLContextEGL.h index 073340b780..84d05ebcc7 100644 --- a/gfx/gl/GLContextEGL.h +++ b/gfx/gl/GLContextEGL.h @@ -103,9 +103,6 @@ public: void UnbindTex2DOffscreen(GLContext *aOffscreen); void BindOffscreenFramebuffer(); - static already_AddRefed - CreateEGLPixmapOffscreenContext(const gfx::IntSize& size); - static already_AddRefed CreateEGLPBufferOffscreenContext(CreateContextFlags flags, const gfx::IntSize& size, diff --git a/gfx/gl/GLContextFeatures.cpp b/gfx/gl/GLContextFeatures.cpp index 39a5714e44..b67249d12d 100644 --- a/gfx/gl/GLContextFeatures.cpp +++ b/gfx/gl/GLContextFeatures.cpp @@ -681,6 +681,15 @@ static const FeatureInfo sFeatureInfoArr[] = { GLContext::Extensions_End } }, + { + "texture_rg", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::ARB_texture_rg, + { + GLContext::Extensions_End + } + }, { "texture_storage", GLVersion::GL4_2, diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp index 97c2607a9e..f197c1bdee 100644 --- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -965,46 +965,6 @@ GLContextEGL::CreateEGLPBufferOffscreenContext(CreateContextFlags flags, return gl.forget(); } -/*static*/ already_AddRefed -GLContextEGL::CreateEGLPixmapOffscreenContext(const mozilla::gfx::IntSize& size) -{ - gfxASurface *thebesSurface = nullptr; - EGLNativePixmapType pixmap = 0; - - if (!pixmap) { - return nullptr; - } - - EGLSurface surface = 0; - EGLConfig config = 0; - - if (!config) { - return nullptr; - } - MOZ_ASSERT(surface); - - SurfaceCaps dummyCaps = SurfaceCaps::Any(); - RefPtr glContext = - GLContextEGL::CreateGLContext(CreateContextFlags::NONE, dummyCaps, - nullptr, true, - config, surface); - if (!glContext) { - NS_WARNING("Failed to create GLContext from XSurface"); - sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); - return nullptr; - } - - if (!glContext->Init()) { - NS_WARNING("Failed to initialize GLContext!"); - // GLContextEGL::dtor will destroy |surface| for us. - return nullptr; - } - - glContext->HoldSurface(thebesSurface); - - return glContext.forget(); -} - /*static*/ already_AddRefed GLContextProviderEGL::CreateHeadless(CreateContextFlags flags) { diff --git a/gfx/gl/SharedSurfaceEGL.cpp b/gfx/gl/SharedSurfaceEGL.cpp index 61174c81e9..e911f3f3ec 100644 --- a/gfx/gl/SharedSurfaceEGL.cpp +++ b/gfx/gl/SharedSurfaceEGL.cpp @@ -142,6 +142,15 @@ SharedSurface_EGLImage::ProducerReleaseImpl() mGL->fFinish(); } +void +SharedSurface_EGLImage::ProducerReadAcquireImpl() +{ + // Wait on the fence, because presumably we're going to want to read this surface + if (mSync) { + mEGL->fClientWaitSync(Display(), mSync, 0, LOCAL_EGL_FOREVER); + } +} + EGLDisplay SharedSurface_EGLImage::Display() const { diff --git a/gfx/gl/SharedSurfaceEGL.h b/gfx/gl/SharedSurfaceEGL.h index 8fbe23ad54..280956f836 100644 --- a/gfx/gl/SharedSurfaceEGL.h +++ b/gfx/gl/SharedSurfaceEGL.h @@ -71,6 +71,9 @@ public: virtual void ProducerAcquireImpl() override {} virtual void ProducerReleaseImpl() override; + virtual void ProducerReadAcquireImpl() override; + virtual void ProducerReadReleaseImpl() override {}; + virtual GLuint ProdTexture() override { return mProdTex; } diff --git a/gfx/gl/SkiaGLGlue.cpp b/gfx/gl/SkiaGLGlue.cpp index 9130db42a8..8b9e787fb1 100755 --- a/gfx/gl/SkiaGLGlue.cpp +++ b/gfx/gl/SkiaGLGlue.cpp @@ -371,6 +371,10 @@ const GLubyte* glGetString_mozilla(GrGLenum name) } else if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_framebuffer_object)) { strcat(extensionsString, "GL_EXT_framebuffer_object "); } + + if (sGLContext.get()->IsSupported(GLFeature::texture_rg)) { + strcat(extensionsString, "GL_ARB_texture_rg "); + } } if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_texture_format_BGRA8888)) { diff --git a/gfx/ipc/SharedDIBWin.cpp b/gfx/ipc/SharedDIBWin.cpp index cd40e835da..197e197d41 100644 --- a/gfx/ipc/SharedDIBWin.cpp +++ b/gfx/ipc/SharedDIBWin.cpp @@ -13,7 +13,7 @@ namespace gfx { static const uint32_t kByteAlign = 1 << gfxAlphaRecovery::GoodAlignmentLog2(); static const uint32_t kHeaderBytes = - (sizeof(BITMAPV4HEADER) + kByteAlign - 1) & ~(kByteAlign - 1); + (uint32_t(sizeof(BITMAPV4HEADER)) + kByteAlign - 1) & ~(kByteAlign - 1); SharedDIBWin::SharedDIBWin() : mSharedHdc(nullptr) diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h index 1a070323c5..7df2231ac8 100644 --- a/gfx/layers/Compositor.h +++ b/gfx/layers/Compositor.h @@ -128,6 +128,10 @@ class DataTextureSource; class CompositingRenderTarget; class CompositorBridgeParent; class LayerManagerComposite; +class CompositorOGL; +class CompositorD3D9; +class CompositorD3D11; +class BasicCompositor; enum SurfaceInitMode { @@ -425,6 +429,11 @@ public: virtual LayersBackend GetBackendType() const = 0; + virtual CompositorOGL* AsCompositorOGL() { return nullptr; } + virtual CompositorD3D9* AsCompositorD3D9() { return nullptr; } + virtual CompositorD3D11* AsCompositorD3D11() { return nullptr; } + virtual BasicCompositor* AsBasicCompositor() { return nullptr; } + /** * Each Compositor has a unique ID. * This ID is used to keep references to each Compositor in a map accessed diff --git a/gfx/layers/GLImages.h b/gfx/layers/GLImages.h index d69dd00018..ae4f7ff9ed 100644 --- a/gfx/layers/GLImages.h +++ b/gfx/layers/GLImages.h @@ -6,6 +6,7 @@ #ifndef GFX_GLIMAGES_H #define GFX_GLIMAGES_H +#include "AndroidSurfaceTexture.h" #include "GLContextTypes.h" #include "GLTypes.h" #include "ImageContainer.h" // for Image @@ -14,9 +15,6 @@ #include "mozilla/gfx/Point.h" // for IntSize namespace mozilla { -namespace gl { -class AndroidSurfaceTexture; -} // namespace gl namespace layers { class GLImage : public Image { @@ -79,7 +77,7 @@ public: } private: - gl::AndroidSurfaceTexture* mSurfaceTexture; + RefPtr mSurfaceTexture; gfx::IntSize mSize; gl::OriginPos mOriginPos; }; diff --git a/gfx/layers/LayerScope.cpp b/gfx/layers/LayerScope.cpp index 5f8ba55704..67158d9e30 100644 --- a/gfx/layers/LayerScope.cpp +++ b/gfx/layers/LayerScope.cpp @@ -1023,7 +1023,7 @@ SenderHelper::SendLayer(LayerComposite* aLayer, Compositor* comp = compHost->GetCompositor(); // Send EffectChain only for CompositorOGL if (LayersBackend::LAYERS_OPENGL == comp->GetBackendType()) { - CompositorOGL* compOGL = static_cast(comp); + CompositorOGL* compOGL = comp->AsCompositorOGL(); EffectChain effect; // Generate primary effect (lock and gen) AutoLockCompositableHost lock(compHost); diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp index f554caa7af..216bf87386 100644 --- a/gfx/layers/LayerTreeInvalidation.cpp +++ b/gfx/layers/LayerTreeInvalidation.cpp @@ -132,7 +132,6 @@ struct LayerPropertiesBase : public LayerProperties : mLayer(aLayer) , mMaskLayer(nullptr) , mVisibleRegion(mLayer->GetLocalVisibleRegion().ToUnknownRegion()) - , mInvalidRegion(aLayer->GetInvalidRegion()) , mPostXScale(aLayer->GetPostXScale()) , mPostYScale(aLayer->GetPostYScale()) , mOpacity(aLayer->GetLocalOpacity()) @@ -202,7 +201,7 @@ struct LayerPropertiesBase : public LayerProperties } AddRegion(result, ComputeChangeInternal(aCallback, aGeometryChanged)); - AddTransformedRegion(result, mLayer->GetInvalidRegion(), mTransform); + AddTransformedRegion(result, mLayer->GetInvalidRegion().GetRegion(), mTransform); if (mMaskLayer && otherMask) { AddTransformedRegion(result, mMaskLayer->ComputeChange(aCallback, aGeometryChanged), @@ -252,7 +251,6 @@ struct LayerPropertiesBase : public LayerProperties UniquePtr mMaskLayer; nsTArray> mAncestorMaskLayers; nsIntRegion mVisibleRegion; - nsIntRegion mInvalidRegion; Matrix4x4 mTransform; float mPostXScale; float mPostYScale; diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index ff5f609776..1feacec627 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -1945,7 +1945,7 @@ Layer::PrintInfo(std::stringstream& aStream, const char* aPrefix) if (1.0 != mOpacity) { aStream << nsPrintfCString(" [opacity=%g]", mOpacity).get(); } - if (GetContentFlags() & CONTENT_OPAQUE) { + if (IsOpaque()) { aStream << " [opaqueContent]"; } if (GetContentFlags() & CONTENT_COMPONENT_ALPHA) { diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index cb51d9e265..616be12326 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -28,6 +28,7 @@ #include "mozilla/gfx/BaseMargin.h" // for BaseMargin #include "mozilla/gfx/BasePoint.h" // for BasePoint #include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/gfx/TiledRegion.h" // for TiledIntRegion #include "mozilla/gfx/Types.h" // for SurfaceFormat #include "mozilla/gfx/UserData.h" // for UserData, etc #include "mozilla/layers/LayersTypes.h" @@ -1530,6 +1531,12 @@ public: return !GetLocalVisibleRegion().IsEmpty() || Extend3DContext(); } + /** + * Return true if current layer content is opaque. + * It does not guarantee that layer content is always opaque. + */ + virtual bool IsOpaque() { return GetContentFlags() & CONTENT_OPAQUE; } + /** * Returns the product of the opacities of this layer and all ancestors up * to and excluding the nearest ancestor that has UseIntermediateSurface() set. @@ -1669,20 +1676,24 @@ public: * Returns the current area of the layer (in layer-space coordinates) * marked as needed to be recomposited. */ - const nsIntRegion& GetInvalidRegion() { return mInvalidRegion; } + const gfx::TiledIntRegion& GetInvalidRegion() { return mInvalidRegion; } void AddInvalidRegion(const nsIntRegion& aRegion) { - mInvalidRegion.Or(mInvalidRegion, aRegion); + mInvalidRegion.Add(aRegion); } /** * Mark the entirety of the layer's visible region as being invalid. */ - void SetInvalidRectToVisibleRegion() { mInvalidRegion = GetVisibleRegion().ToUnknownRegion(); } + void SetInvalidRectToVisibleRegion() + { + mInvalidRegion.SetEmpty(); + mInvalidRegion.Add(GetVisibleRegion().ToUnknownRegion()); + } /** * Adds to the current invalid rect. */ - void AddInvalidRect(const gfx::IntRect& aRect) { mInvalidRegion.Or(mInvalidRegion, aRect); } + void AddInvalidRect(const gfx::IntRect& aRect) { mInvalidRegion.Add(aRect); } /** * Clear the invalid rect, marking the layer as being identical to what is currently @@ -1840,7 +1851,7 @@ protected: bool mForceIsolatedGroup; Maybe mClipRect; gfx::IntRect mTileSourceRect; - nsIntRegion mInvalidRegion; + gfx::TiledIntRegion mInvalidRegion; nsTArray > mApzcs; uint32_t mContentFlags; bool mUseTileSourceRect; diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index 0237473d5e..5a60800a19 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -175,7 +175,7 @@ BasicCompositor::CreateRenderTargetForWindow(const LayoutDeviceIntRect& aRect, S IntRect rect = aRect.ToUnknownRect(); if (aBufferMode != BufferMode::BUFFER_NONE) { - RefPtr target = mWidget->CreateBackBufferDrawTarget(mDrawTarget, aRect); + RefPtr target = mWidget->CreateBackBufferDrawTarget(mDrawTarget, aRect, aInit == INIT_MODE_CLEAR); if (!target) { return nullptr; } @@ -615,6 +615,14 @@ BasicCompositor::EndFrameForExternalComposition(const gfx::Matrix& aTransform) mDidExternalComposition = true; } +BasicCompositor* +AssertBasicCompositor(Compositor* aCompositor) +{ + BasicCompositor* compositor = aCompositor ? aCompositor->AsBasicCompositor() + : nullptr; + MOZ_DIAGNOSTIC_ASSERT(!!compositor); + return compositor; +} } // namespace layers } // namespace mozilla diff --git a/gfx/layers/basic/BasicCompositor.h b/gfx/layers/basic/BasicCompositor.h index c791f94f54..022a39df51 100644 --- a/gfx/layers/basic/BasicCompositor.h +++ b/gfx/layers/basic/BasicCompositor.h @@ -48,6 +48,9 @@ protected: virtual ~BasicCompositor(); public: + + virtual BasicCompositor* AsBasicCompositor() override { return this; } + virtual bool Initialize() override; virtual void Destroy() override {} @@ -144,6 +147,8 @@ private: uint32_t mMaxTextureSize; }; +BasicCompositor* AssertBasicCompositor(Compositor* aCompositor); + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/basic/BasicPaintedLayer.h b/gfx/layers/basic/BasicPaintedLayer.h index 4518b64fce..59f71b20ce 100644 --- a/gfx/layers/basic/BasicPaintedLayer.h +++ b/gfx/layers/basic/BasicPaintedLayer.h @@ -54,9 +54,8 @@ public: { NS_ASSERTION(BasicManager()->InConstruction(), "Can only set properties in construction phase"); - mInvalidRegion.Or(mInvalidRegion, aRegion); - mInvalidRegion.SimplifyOutward(20); - mValidRegion.Sub(mValidRegion, mInvalidRegion); + mInvalidRegion.Add(aRegion); + mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion()); } virtual void PaintThebes(gfxContext* aContext, diff --git a/gfx/layers/basic/GrallocTextureHostBasic.cpp b/gfx/layers/basic/GrallocTextureHostBasic.cpp index 5591cd17e5..fa950896ec 100644 --- a/gfx/layers/basic/GrallocTextureHostBasic.cpp +++ b/gfx/layers/basic/GrallocTextureHostBasic.cpp @@ -180,7 +180,11 @@ GrallocTextureHostBasic::ClearTextureSource() void GrallocTextureHostBasic::SetCompositor(Compositor* aCompositor) { - BasicCompositor* compositor = static_cast(aCompositor); + BasicCompositor* compositor = AssertBasicCompositor(aCompositor); + if (!compositor) { + return; + } + mCompositor = compositor; if (mTextureSource) { mTextureSource->SetCompositor(compositor); diff --git a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp index d3341a200b..8834773e4c 100644 --- a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp +++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp @@ -62,7 +62,7 @@ MacIOSurfaceTextureSourceBasic::GetSurface(gfx::DrawTarget* aTarget) void MacIOSurfaceTextureSourceBasic::SetCompositor(Compositor* aCompositor) { - mCompositor = static_cast(aCompositor); + mCompositor = AssertBasicCompositor(aCompositor); } bool @@ -81,7 +81,11 @@ MacIOSurfaceTextureHostBasic::Lock() void MacIOSurfaceTextureHostBasic::SetCompositor(Compositor* aCompositor) { - BasicCompositor* compositor = static_cast(aCompositor); + BasicCompositor* compositor = AssertBasicCompositor(aCompositor); + if (!compositor) { + mTextureSource = nullptr; + return; + } mCompositor = compositor; if (mTextureSource) { mTextureSource->SetCompositor(compositor); diff --git a/gfx/layers/basic/X11TextureSourceBasic.cpp b/gfx/layers/basic/X11TextureSourceBasic.cpp index 28169067c3..24f21e5fc6 100644 --- a/gfx/layers/basic/X11TextureSourceBasic.cpp +++ b/gfx/layers/basic/X11TextureSourceBasic.cpp @@ -45,9 +45,7 @@ X11TextureSourceBasic::GetSurface(DrawTarget* aTarget) void X11TextureSourceBasic::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor->GetBackendType() == LayersBackend::LAYERS_BASIC); - BasicCompositor* compositor = static_cast(aCompositor); - mCompositor = compositor; + mCompositor = AssertBasicCompositor(aCompositor); } SurfaceFormat diff --git a/gfx/layers/client/ClientPaintedLayer.h b/gfx/layers/client/ClientPaintedLayer.h index cfc17ba10e..820745aafe 100644 --- a/gfx/layers/client/ClientPaintedLayer.h +++ b/gfx/layers/client/ClientPaintedLayer.h @@ -60,9 +60,8 @@ public: { NS_ASSERTION(ClientManager()->InConstruction(), "Can only set properties in construction phase"); - mInvalidRegion.Or(mInvalidRegion, aRegion); - mInvalidRegion.SimplifyOutward(20); - mValidRegion.Sub(mValidRegion, mInvalidRegion); + mInvalidRegion.Add(aRegion); + mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion()); } virtual void RenderLayer() override { RenderLayerWithReadback(nullptr); } diff --git a/gfx/layers/client/ClientTiledPaintedLayer.h b/gfx/layers/client/ClientTiledPaintedLayer.h index f4c3f2a8a9..fe4b4018db 100644 --- a/gfx/layers/client/ClientTiledPaintedLayer.h +++ b/gfx/layers/client/ClientTiledPaintedLayer.h @@ -54,10 +54,10 @@ public: // PaintedLayer virtual Layer* AsLayer() override { return this; } virtual void InvalidateRegion(const nsIntRegion& aRegion) override { - mInvalidRegion.Or(mInvalidRegion, aRegion); - mInvalidRegion.SimplifyOutward(20); - mValidRegion.Sub(mValidRegion, mInvalidRegion); - mLowPrecisionValidRegion.Sub(mLowPrecisionValidRegion, mInvalidRegion); + mInvalidRegion.Add(aRegion); + nsIntRegion invalidRegion = mInvalidRegion.GetRegion(); + mValidRegion.Sub(mValidRegion, invalidRegion); + mLowPrecisionValidRegion.Sub(mLowPrecisionValidRegion, invalidRegion); } // Shadow methods diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp index 81b92cf394..e72e0663b7 100644 --- a/gfx/layers/composite/ImageHost.cpp +++ b/gfx/layers/composite/ImageHost.cpp @@ -330,9 +330,9 @@ ImageHost::Composite(LayerComposite* aLayer, } TimedImage* img = &mImages[imageIndex]; + img->mTextureHost->SetCompositor(GetCompositor()); SetCurrentTextureHost(img->mTextureHost); // Make sure the front buffer has a compositor - mCurrentTextureHost->SetCompositor(GetCompositor()); if (mCurrentTextureSource) { mCurrentTextureSource->SetCompositor(GetCompositor()); } @@ -581,6 +581,27 @@ ImageHost::GetImageSize() const return IntSize(); } +bool +ImageHost::IsOpaque() +{ + const TimedImage* img = ChooseImage(); + if (!img) { + return false; + } + + if (img->mPictureRect.width == 0 || + img->mPictureRect.height == 0 || + !img->mTextureHost) { + return false; + } + + gfx::SurfaceFormat format = img->mTextureHost->GetFormat(); + if (gfx::IsOpaque(format)) { + return true; + } + return false; +} + already_AddRefed ImageHost::GenEffect(const gfx::Filter& aFilter) { diff --git a/gfx/layers/composite/ImageHost.h b/gfx/layers/composite/ImageHost.h index 3860c33ff2..a0566d1a0b 100644 --- a/gfx/layers/composite/ImageHost.h +++ b/gfx/layers/composite/ImageHost.h @@ -115,6 +115,8 @@ public: BIAS_POSITIVE, }; + bool IsOpaque(); + protected: struct TimedImage { RefPtr mTextureHost; diff --git a/gfx/layers/composite/ImageLayerComposite.cpp b/gfx/layers/composite/ImageLayerComposite.cpp index 337b053ade..162c24a431 100644 --- a/gfx/layers/composite/ImageLayerComposite.cpp +++ b/gfx/layers/composite/ImageLayerComposite.cpp @@ -15,6 +15,7 @@ #include "mozilla/gfx/Rect.h" // for Rect #include "mozilla/layers/Compositor.h" // for Compositor #include "mozilla/layers/Effects.h" // for EffectChain +#include "mozilla/layers/ImageHost.h" // for ImageHost #include "mozilla/layers/TextureHost.h" // for TextureHost, etc #include "mozilla/mozalloc.h" // for operator delete #include "nsAString.h" @@ -50,7 +51,7 @@ ImageLayerComposite::SetCompositableHost(CompositableHost* aHost) { switch (aHost->GetType()) { case CompositableType::IMAGE: - mImageHost = aHost; + mImageHost = static_cast(aHost); return true; default: return false; @@ -78,6 +79,16 @@ ImageLayerComposite::GetLayer() return this; } +void +ImageLayerComposite::SetLayerManager(LayerManagerComposite* aManager) +{ + LayerComposite::SetLayerManager(aManager); + mManager = aManager; + if (mImageHost) { + mImageHost->SetCompositor(mCompositor); + } +} + void ImageLayerComposite::RenderLayer(const IntRect& aClipRect) { @@ -143,6 +154,20 @@ ImageLayerComposite::ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransform ComputeEffectiveTransformForMaskLayers(aTransformToSurface); } +bool +ImageLayerComposite::IsOpaque() +{ + if (!mImageHost || + !mImageHost->IsAttached()) { + return false; + } + + if (mScaleMode == ScaleMode::STRETCH) { + return mImageHost->IsOpaque(); + } + return false; +} + CompositableHost* ImageLayerComposite::GetCompositableHost() { diff --git a/gfx/layers/composite/ImageLayerComposite.h b/gfx/layers/composite/ImageLayerComposite.h index d6b8c31e6a..9aa226d57a 100644 --- a/gfx/layers/composite/ImageLayerComposite.h +++ b/gfx/layers/composite/ImageLayerComposite.h @@ -43,14 +43,7 @@ public: virtual Layer* GetLayer() override; - virtual void SetLayerManager(LayerManagerComposite* aManager) override - { - LayerComposite::SetLayerManager(aManager); - mManager = aManager; - if (mImageHost) { - mImageHost->SetCompositor(mCompositor); - } - } + virtual void SetLayerManager(LayerManagerComposite* aManager) override; virtual void RenderLayer(const gfx::IntRect& aClipRect) override; @@ -66,6 +59,8 @@ public: virtual const char* Name() const override { return "ImageLayerComposite"; } + virtual bool IsOpaque() override; + protected: virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override; @@ -73,7 +68,7 @@ private: gfx::Filter GetEffectFilter(); private: - RefPtr mImageHost; + RefPtr mImageHost; }; } // namespace layers diff --git a/gfx/layers/composite/LayerManagerComposite.cpp b/gfx/layers/composite/LayerManagerComposite.cpp index d45dcdeb31..7c5bad25b7 100644 --- a/gfx/layers/composite/LayerManagerComposite.cpp +++ b/gfx/layers/composite/LayerManagerComposite.cpp @@ -349,7 +349,7 @@ LayerManagerComposite::PostProcessLayers(Layer* aLayer, if (integerTranslation && !aLayer->HasMaskLayers() && aLayer->IsOpaqueForVisibility()) { - if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) { + if (aLayer->IsOpaque()) { localOpaque.OrWith(composite->GetFullyRenderedRegion()); } localOpaque.MoveBy(*integerTranslation); @@ -1053,7 +1053,7 @@ LayerManagerComposite::RenderToPresentationSurface() AndroidBridge::Bridge()->SetPresentationSurface(surface); } - CompositorOGL* compositor = static_cast(mCompositor.get()); + CompositorOGL* compositor = mCompositor->AsCompositorOGL(); GLContext* gl = compositor->gl(); GLContextEGL* egl = GLContextEGL::Cast(gl); @@ -1064,7 +1064,7 @@ LayerManagerComposite::RenderToPresentationSurface() const IntSize windowSize = AndroidBridge::Bridge()->GetNativeWindowSize(window); #elif defined(MOZ_WIDGET_GONK) - CompositorOGL* compositor = static_cast(mCompositor.get()); + CompositorOGL* compositor = mCompositor->AsCompositorOGL(); nsScreenGonk* screen = static_cast(mCompositor->GetWidget())->GetScreen(); if (!screen->IsPrimaryScreen()) { // Only primary screen support mirroring diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index fb2897f154..e66b9cfda3 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -442,10 +442,13 @@ BufferTextureHost::SetCompositor(Compositor* aCompositor) if (mCompositor == aCompositor) { return; } - RefPtr it = mFirstSource; - while (it) { - it->SetCompositor(aCompositor); - it = it->GetNextSibling(); + if (aCompositor && mCompositor && + aCompositor->GetBackendType() == mCompositor->GetBackendType()) { + RefPtr it = mFirstSource; + while (it) { + it->SetCompositor(aCompositor); + it = it->GetNextSibling(); + } } if (mFirstSource && mFirstSource->IsOwnedBy(this)) { mFirstSource->SetOwner(nullptr); diff --git a/gfx/layers/composite/X11TextureHost.cpp b/gfx/layers/composite/X11TextureHost.cpp index 58702780db..7ca42426dd 100644 --- a/gfx/layers/composite/X11TextureHost.cpp +++ b/gfx/layers/composite/X11TextureHost.cpp @@ -41,14 +41,12 @@ X11TextureHost::Lock() switch (mCompositor->GetBackendType()) { case LayersBackend::LAYERS_BASIC: mTextureSource = - new X11TextureSourceBasic(static_cast(mCompositor.get()), - mSurface); + new X11TextureSourceBasic(mCompositor->AsBasicCompositor(), mSurface); break; #ifdef GL_PROVIDER_GLX case LayersBackend::LAYERS_OPENGL: mTextureSource = - new X11TextureSourceOGL(static_cast(mCompositor.get()), - mSurface); + new X11TextureSourceOGL(mCompositor->AsCompositorOGL(), mSurface); break; #endif default: diff --git a/gfx/layers/d3d11/CompositorD3D11.h b/gfx/layers/d3d11/CompositorD3D11.h index 1260b2f21e..1626960570 100644 --- a/gfx/layers/d3d11/CompositorD3D11.h +++ b/gfx/layers/d3d11/CompositorD3D11.h @@ -45,6 +45,8 @@ public: CompositorD3D11(CompositorBridgeParent* aParent, nsIWidget* aWidget); ~CompositorD3D11(); + virtual CompositorD3D11* AsCompositorD3D11() override { return this; } + virtual bool Initialize() override; virtual void Destroy() override {} virtual void DetachWidget() override { mWidget = nullptr; } diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index 049a081f37..80afad7e39 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -638,11 +638,24 @@ DXGITextureHostD3D11::GetDevice() return device; } +static CompositorD3D11* AssertD3D11Compositor(Compositor* aCompositor) +{ + CompositorD3D11* compositor = aCompositor ? aCompositor->AsCompositorD3D11() + : nullptr; + MOZ_DIAGNOSTIC_ASSERT(!!compositor); + return compositor; +} + void DXGITextureHostD3D11::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - mCompositor = static_cast(aCompositor); + CompositorD3D11* d3dCompositor = AssertD3D11Compositor(aCompositor); + if (!d3dCompositor) { + mCompositor = nullptr; + mTextureSource = nullptr; + return; + } + mCompositor = d3dCompositor; if (mTextureSource) { mTextureSource->SetCompositor(aCompositor); } @@ -751,8 +764,14 @@ DXGIYCbCrTextureHostD3D11::GetDevice() void DXGIYCbCrTextureHostD3D11::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - mCompositor = static_cast(aCompositor); + mCompositor = AssertD3D11Compositor(aCompositor); + if (!mCompositor) { + mTextureSources[0] = nullptr; + mTextureSources[1] = nullptr; + mTextureSources[2] = nullptr; + return; + } + if (mTextureSources[0]) { mTextureSources[0]->SetCompositor(aCompositor); } @@ -978,8 +997,11 @@ DataTextureSourceD3D11::GetTileRect() void DataTextureSourceD3D11::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - mCompositor = static_cast(aCompositor); + CompositorD3D11* d3dCompositor = AssertD3D11Compositor(aCompositor); + if (!d3dCompositor) { + return; + } + mCompositor = d3dCompositor; if (mNextSibling) { mNextSibling->SetCompositor(aCompositor); } diff --git a/gfx/layers/d3d9/CompositorD3D9.h b/gfx/layers/d3d9/CompositorD3D9.h index 45ec6580b4..f4e1f3bcb7 100644 --- a/gfx/layers/d3d9/CompositorD3D9.h +++ b/gfx/layers/d3d9/CompositorD3D9.h @@ -24,6 +24,8 @@ public: CompositorD3D9(CompositorBridgeParent* aParent, nsIWidget *aWidget); ~CompositorD3D9(); + virtual CompositorD3D9* AsCompositorD3D9() override { return this; } + virtual bool Initialize() override; virtual void Destroy() override {} virtual void DetachWidget() override { mWidget = nullptr; } diff --git a/gfx/layers/d3d9/TextureD3D9.cpp b/gfx/layers/d3d9/TextureD3D9.cpp index 9948143559..12057b0cd4 100644 --- a/gfx/layers/d3d9/TextureD3D9.cpp +++ b/gfx/layers/d3d9/TextureD3D9.cpp @@ -493,11 +493,22 @@ DataTextureSourceD3D9::Update(gfx::DataSourceSurface* aSurface, return true; } +static CompositorD3D9* AssertD3D9Compositor(Compositor* aCompositor) +{ + CompositorD3D9* compositor = aCompositor ? aCompositor->AsCompositorD3D9() + : nullptr; + MOZ_DIAGNOSTIC_ASSERT(!!compositor); + return compositor; +} + void DataTextureSourceD3D9::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - CompositorD3D9* d3dCompositor = static_cast(aCompositor); + CompositorD3D9* d3dCompositor = AssertD3D9Compositor(aCompositor); + if (!d3dCompositor) { + Reset(); + return; + } if (mCompositor && mCompositor != d3dCompositor) { Reset(); } @@ -910,7 +921,11 @@ TextureHostD3D9::GetDevice() void TextureHostD3D9::SetCompositor(Compositor* aCompositor) { - mCompositor = static_cast(aCompositor); + mCompositor = AssertD3D9Compositor(aCompositor); + if (!mCompositor) { + mTextureSource = nullptr; + return; + } if (mTextureSource) { mTextureSource->SetCompositor(aCompositor); } @@ -1031,8 +1046,10 @@ DXGITextureHostD3D9::Unlock() void DXGITextureHostD3D9::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - mCompositor = static_cast(aCompositor); + mCompositor = AssertD3D9Compositor(aCompositor); + if (!mCompositor) { + mTextureSource = nullptr; + } } void @@ -1066,8 +1083,12 @@ DXGIYCbCrTextureHostD3D9::GetDevice() void DXGIYCbCrTextureHostD3D9::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - mCompositor = static_cast(aCompositor); + mCompositor = AssertD3D9Compositor(aCompositor); + if (!mCompositor) { + mTextureSources[0] = nullptr; + mTextureSources[1] = nullptr; + mTextureSources[2] = nullptr; + } } bool diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index c81284d8bd..700fa1e604 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -844,7 +844,7 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray* aReplies, } common.maskLayerParent() = nullptr; common.animations() = mutant->GetAnimations(); - common.invalidRegion() = mutant->GetInvalidRegion(); + common.invalidRegion() = mutant->GetInvalidRegion().GetRegion(); common.scrollMetadata() = mutant->GetAllScrollMetadata(); for (size_t i = 0; i < mutant->GetAncestorMaskLayerCount(); i++) { auto layer = Shadow(mutant->GetAncestorMaskLayerAt(i)->AsShadowableLayer()); diff --git a/gfx/layers/opengl/CompositingRenderTargetOGL.cpp b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp index 1d9ad66c1b..af745ff3e8 100644 --- a/gfx/layers/opengl/CompositingRenderTargetOGL.cpp +++ b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp @@ -81,7 +81,7 @@ already_AddRefed CompositingRenderTargetOGL::Dump(Compositor* aCompositor) { MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED); - CompositorOGL* compositorOGL = static_cast(aCompositor); + CompositorOGL* compositorOGL = aCompositor->AsCompositorOGL(); return ReadBackSurface(mGL, mTextureHandle, true, compositorOGL->GetFBOFormat()); } #endif diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h index 84c3ad6bf9..b32d627455 100644 --- a/gfx/layers/opengl/CompositorOGL.h +++ b/gfx/layers/opengl/CompositorOGL.h @@ -201,6 +201,8 @@ protected: virtual ~CompositorOGL(); public: + virtual CompositorOGL* AsCompositorOGL() override { return this; } + virtual already_AddRefed CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override; diff --git a/gfx/layers/opengl/GLManager.cpp b/gfx/layers/opengl/GLManager.cpp index a70e4efb6e..3cf30aa801 100644 --- a/gfx/layers/opengl/GLManager.cpp +++ b/gfx/layers/opengl/GLManager.cpp @@ -62,8 +62,7 @@ private: GLManager::CreateGLManager(LayerManagerComposite* aManager) { if (aManager && aManager->GetCompositor()->GetBackendType() == LayersBackend::LAYERS_OPENGL) { - return new GLManagerCompositor(static_cast( - aManager->GetCompositor())); + return new GLManagerCompositor(aManager->GetCompositor()->AsCompositorOGL()); } return nullptr; } diff --git a/gfx/layers/opengl/GrallocTextureHost.cpp b/gfx/layers/opengl/GrallocTextureHost.cpp index 70319208a1..7b771d4df0 100644 --- a/gfx/layers/opengl/GrallocTextureHost.cpp +++ b/gfx/layers/opengl/GrallocTextureHost.cpp @@ -130,8 +130,7 @@ GrallocTextureHostOGL::~GrallocTextureHostOGL() void GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - mCompositor = static_cast(aCompositor); + mCompositor = AssertGLCompositor(aCompositor); if (mGLTextureSource) { mGLTextureSource->SetCompositor(mCompositor); } diff --git a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp index 7fa69134e8..86442d861a 100644 --- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp +++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp @@ -48,7 +48,7 @@ MacIOSurfaceTextureHostOGL::CreateTextureSourceForPlane(size_t aPlane) bool MacIOSurfaceTextureHostOGL::Lock() { - if (!mCompositor || !mSurface) { + if (!gl() || !gl()->MakeCurrent() || !mSurface) { return false; } @@ -68,11 +68,18 @@ MacIOSurfaceTextureHostOGL::Lock() void MacIOSurfaceTextureHostOGL::SetCompositor(Compositor* aCompositor) { - CompositorOGL* glCompositor = static_cast(aCompositor); - mCompositor = glCompositor; - if (mTextureSource) { - mTextureSource->SetCompositor(glCompositor); + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + mTextureSource = nullptr; + mCompositor = nullptr; + return; } + + if (mCompositor != glCompositor) { + // Cannot share GL texture identifiers across compositors. + mTextureSource = nullptr; + } + mCompositor = glCompositor; } gfx::SurfaceFormat @@ -94,12 +101,19 @@ MacIOSurfaceTextureHostOGL::GetSize() const { mSurface->GetDevicePixelHeight()); } +gl::GLContext* +MacIOSurfaceTextureHostOGL::gl() const +{ + return mCompositor ? mCompositor->gl() : nullptr; +} + MacIOSurfaceTextureSourceOGL::MacIOSurfaceTextureSourceOGL( CompositorOGL* aCompositor, MacIOSurface* aSurface) : mCompositor(aCompositor) , mSurface(aSurface) { + MOZ_ASSERT(aCompositor); MOZ_COUNT_CTOR(MacIOSurfaceTextureSourceOGL); } @@ -125,23 +139,24 @@ MacIOSurfaceTextureSourceOGL::GetFormat() const void MacIOSurfaceTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) { - if (!gl()) { - NS_WARNING("Trying to bind a texture without a GLContext"); + gl::GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { + NS_WARNING("Trying to bind a texture without a working GLContext"); return; } GLuint tex = mCompositor->GetTemporaryTexture(GetTextureTarget(), aTextureUnit); - gl()->fActiveTexture(aTextureUnit); - gl()->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, tex); - mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl())->GetCGLContext()); - ApplyFilterToBoundTexture(gl(), aFilter, LOCAL_GL_TEXTURE_RECTANGLE_ARB); + gl->fActiveTexture(aTextureUnit); + gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, tex); + mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext()); + ApplyFilterToBoundTexture(gl, aFilter, LOCAL_GL_TEXTURE_RECTANGLE_ARB); } void MacIOSurfaceTextureSourceOGL::SetCompositor(Compositor* aCompositor) { - mCompositor = static_cast(aCompositor); - if (mNextSibling) { + mCompositor = AssertGLCompositor(aCompositor); + if (mCompositor && mNextSibling) { mNextSibling->SetCompositor(aCompositor); } } diff --git a/gfx/layers/opengl/TextureHostOGL.cpp b/gfx/layers/opengl/TextureHostOGL.cpp index d54a1afeb7..b92893bbcc 100644 --- a/gfx/layers/opengl/TextureHostOGL.cpp +++ b/gfx/layers/opengl/TextureHostOGL.cpp @@ -138,7 +138,7 @@ TextureImageTextureSourceOGL::Update(gfx::DataSourceSurface* aSurface, { GLContext *gl = mCompositor->gl(); MOZ_ASSERT(gl); - if (!gl) { + if (!gl || !gl->MakeCurrent()) { NS_WARNING("trying to update TextureImageTextureSourceOGL without a GLContext"); return false; } @@ -230,13 +230,23 @@ TextureImageTextureSourceOGL::CopyTo(const gfx::IntRect& aSourceRect, dest->mTexImage->MarkValid(); } +CompositorOGL* AssertGLCompositor(Compositor* aCompositor) +{ + CompositorOGL* compositor = aCompositor ? aCompositor->AsCompositorOGL() + : nullptr; + MOZ_ASSERT(!!compositor); + return compositor; +} + void TextureImageTextureSourceOGL::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - CompositorOGL* glCompositor = static_cast(aCompositor); - - if (!glCompositor || (mCompositor != glCompositor)) { + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + DeallocateDeviceData(); + return; + } + if (mCompositor != glCompositor) { DeallocateDeviceData(); mCompositor = glCompositor; } @@ -317,8 +327,9 @@ GLTextureSource::DeallocateDeviceData() void GLTextureSource::DeleteTextureHandle() { - if (mTextureHandle != 0 && gl() && gl()->MakeCurrent()) { - gl()->fDeleteTextures(1, &mTextureHandle); + GLContext* gl = this->gl(); + if (mTextureHandle != 0 && gl && gl->MakeCurrent()) { + gl->fDeleteTextures(1, &mTextureHandle); } mTextureHandle = 0; } @@ -326,21 +337,29 @@ GLTextureSource::DeleteTextureHandle() void GLTextureSource::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) { - MOZ_ASSERT(gl()); MOZ_ASSERT(mTextureHandle != 0); - if (!gl()) { + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { return; } - gl()->fActiveTexture(aTextureUnit); - gl()->fBindTexture(mTextureTarget, mTextureHandle); - ApplyFilterToBoundTexture(gl(), aFilter, mTextureTarget); + gl->fActiveTexture(aTextureUnit); + gl->fBindTexture(mTextureTarget, mTextureHandle); + ApplyFilterToBoundTexture(gl, aFilter, mTextureTarget); } void GLTextureSource::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - mCompositor = static_cast(aCompositor); + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + return; + } + + if (mCompositor && mCompositor != glCompositor) { + gfxCriticalError() << "GLTextureSource does not support changing compositors"; + } + mCompositor = glCompositor; + if (mNextSibling) { mNextSibling->SetCompositor(aCompositor); } @@ -382,31 +401,37 @@ SurfaceTextureSource::SurfaceTextureSource(CompositorOGL* aCompositor, void SurfaceTextureSource::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) { - if (!gl()) { + MOZ_ASSERT(mSurfTex); + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { NS_WARNING("Trying to bind a texture without a GLContext"); return; } - gl()->fActiveTexture(aTextureUnit); + gl->fActiveTexture(aTextureUnit); // SurfaceTexture spams us if there are any existing GL errors, so // we'll clear them here in order to avoid that. - gl()->FlushErrors(); + gl->FlushErrors(); mSurfTex->UpdateTexImage(); - ApplyFilterToBoundTexture(gl(), aFilter, mTextureTarget); + ApplyFilterToBoundTexture(gl, aFilter, mTextureTarget); } void SurfaceTextureSource::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - if (mCompositor != aCompositor) { + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + DeallocateDeviceData(); + return; + } + if (mCompositor != glCompositor) { DeallocateDeviceData(); } - mCompositor = static_cast(aCompositor); + mCompositor = glCompositor; } bool @@ -424,12 +449,20 @@ SurfaceTextureSource::gl() const gfx::Matrix4x4 SurfaceTextureSource::GetTextureTransform() { + MOZ_ASSERT(mSurfTex); + gfx::Matrix4x4 ret; mSurfTex->GetTransformMatrix(ret); return ret; } +void +SurfaceTextureSource::DeallocateDeviceData() +{ + mSurfTex = nullptr; +} + //////////////////////////////////////////////////////////////////////// SurfaceTextureHost::SurfaceTextureHost(TextureFlags aFlags, @@ -455,7 +488,9 @@ SurfaceTextureHost::gl() const bool SurfaceTextureHost::Lock() { - if (!mCompositor) { + MOZ_ASSERT(mSurfTex); + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { return false; } @@ -471,20 +506,24 @@ SurfaceTextureHost::Lock() mSize); } - return NS_SUCCEEDED(mSurfTex->Attach(gl())); + return NS_SUCCEEDED(mSurfTex->Attach(gl)); } void SurfaceTextureHost::Unlock() { + MOZ_ASSERT(mSurfTex); mSurfTex->Detach(); } void SurfaceTextureHost::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - CompositorOGL* glCompositor = static_cast(aCompositor); + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + DeallocateDeviceData(); + return; + } mCompositor = glCompositor; if (mTextureSource) { mTextureSource->SetCompositor(glCompositor); @@ -494,8 +533,16 @@ SurfaceTextureHost::SetCompositor(Compositor* aCompositor) gfx::SurfaceFormat SurfaceTextureHost::GetFormat() const { - MOZ_ASSERT(mTextureSource); - return mTextureSource->GetFormat(); + return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN; +} + +void +SurfaceTextureHost::DeallocateDeviceData() +{ + if (mTextureSource) { + mTextureSource->DeallocateDeviceData(); + } + mSurfTex = nullptr; } #endif // MOZ_WIDGET_ANDROID @@ -524,29 +571,29 @@ EGLImageTextureSource::EGLImageTextureSource(CompositorOGL* aCompositor, void EGLImageTextureSource::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) { - if (!gl()) { + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { NS_WARNING("Trying to bind a texture without a GLContext"); return; } - MOZ_ASSERT(DoesEGLContextSupportSharingWithEGLImage(gl()), + MOZ_ASSERT(DoesEGLContextSupportSharingWithEGLImage(gl), "EGLImage not supported or disabled in runtime"); GLuint tex = mCompositor->GetTemporaryTexture(mTextureTarget, aTextureUnit); - gl()->fActiveTexture(aTextureUnit); - gl()->fBindTexture(mTextureTarget, tex); + gl->fActiveTexture(aTextureUnit); + gl->fBindTexture(mTextureTarget, tex); - gl()->fEGLImageTargetTexture2D(mTextureTarget, mImage); + gl->fEGLImageTargetTexture2D(mTextureTarget, mImage); - ApplyFilterToBoundTexture(gl(), aFilter, mTextureTarget); + ApplyFilterToBoundTexture(gl, aFilter, mTextureTarget); } void EGLImageTextureSource::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - mCompositor = static_cast(aCompositor); + mCompositor = AssertGLCompositor(aCompositor); } bool @@ -595,7 +642,8 @@ EGLImageTextureHost::gl() const bool EGLImageTextureHost::Lock() { - if (!mCompositor) { + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { return false; } @@ -636,8 +684,12 @@ EGLImageTextureHost::Unlock() void EGLImageTextureHost::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - CompositorOGL* glCompositor = static_cast(aCompositor); + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + mCompositor = nullptr; + mTextureSource = nullptr; + return; + } mCompositor = glCompositor; if (mTextureSource) { mTextureSource->SetCompositor(glCompositor); @@ -648,7 +700,7 @@ gfx::SurfaceFormat EGLImageTextureHost::GetFormat() const { MOZ_ASSERT(mTextureSource); - return mTextureSource->GetFormat(); + return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN; } // @@ -680,14 +732,17 @@ GLTextureHost::gl() const bool GLTextureHost::Lock() { - if (!mCompositor) { + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { return false; } if (mSync) { - gl()->MakeCurrent(); - gl()->fWaitSync(mSync, 0, LOCAL_GL_TIMEOUT_IGNORED); - gl()->fDeleteSync(mSync); + if (!gl->MakeCurrent()) { + return false; + } + gl->fWaitSync(mSync, 0, LOCAL_GL_TIMEOUT_IGNORED); + gl->fDeleteSync(mSync); mSync = 0; } @@ -707,8 +762,12 @@ GLTextureHost::Lock() void GLTextureHost::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(aCompositor); - CompositorOGL* glCompositor = static_cast(aCompositor); + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + mCompositor = nullptr; + mTextureSource = nullptr; + return; + } mCompositor = glCompositor; if (mTextureSource) { mTextureSource->SetCompositor(glCompositor); @@ -719,7 +778,7 @@ gfx::SurfaceFormat GLTextureHost::GetFormat() const { MOZ_ASSERT(mTextureSource); - return mTextureSource->GetFormat(); + return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN; } } // namespace layers diff --git a/gfx/layers/opengl/TextureHostOGL.h b/gfx/layers/opengl/TextureHostOGL.h index 5d6334a1fb..1b5b04a71e 100644 --- a/gfx/layers/opengl/TextureHostOGL.h +++ b/gfx/layers/opengl/TextureHostOGL.h @@ -365,8 +365,7 @@ public: virtual GLenum GetWrapMode() const override { return mWrapMode; } - // We don't own anything. - virtual void DeallocateDeviceData() override {} + virtual void DeallocateDeviceData() override; virtual void SetCompositor(Compositor* aCompositor) override; @@ -374,7 +373,7 @@ public: protected: RefPtr mCompositor; - mozilla::gl::AndroidSurfaceTexture* const mSurfTex; + RefPtr mSurfTex; const gfx::SurfaceFormat mFormat; const GLenum mTextureTarget; const GLenum mWrapMode; @@ -390,8 +389,7 @@ public: virtual ~SurfaceTextureHost(); - // We don't own anything. - virtual void DeallocateDeviceData() override {} + virtual void DeallocateDeviceData() override; virtual void SetCompositor(Compositor* aCompositor) override; @@ -419,7 +417,7 @@ public: virtual const char* Name() { return "SurfaceTextureHost"; } protected: - mozilla::gl::AndroidSurfaceTexture* const mSurfTex; + RefPtr mSurfTex; const gfx::IntSize mSize; RefPtr mCompositor; RefPtr mTextureSource; @@ -523,6 +521,8 @@ protected: RefPtr mTextureSource; }; +CompositorOGL* AssertGLCompositor(Compositor* aCompositor); + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/opengl/X11TextureSourceOGL.cpp b/gfx/layers/opengl/X11TextureSourceOGL.cpp index 2f1a8de647..248e220efe 100644 --- a/gfx/layers/opengl/X11TextureSourceOGL.cpp +++ b/gfx/layers/opengl/X11TextureSourceOGL.cpp @@ -76,12 +76,14 @@ X11TextureSourceOGL::GetFormat() const { void X11TextureSourceOGL::SetCompositor(Compositor* aCompositor) { - MOZ_ASSERT(!aCompositor || aCompositor->GetBackendType() == LayersBackend::LAYERS_OPENGL); - if (mCompositor == aCompositor) { + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (mCompositor == glCompositor) { return; } DeallocateDeviceData(); - mCompositor = static_cast(aCompositor); + if (glCompositor) { + mCompositor = glCompositor; + } } gl::GLContext* diff --git a/gfx/src/PingPongRegion.h b/gfx/src/PingPongRegion.h new file mode 100644 index 0000000000..d3bdcae1b4 --- /dev/null +++ b/gfx/src/PingPongRegion.h @@ -0,0 +1,63 @@ +/* -*- 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/. */ + +#ifndef PingPongRegion_h__ +#define PingPongRegion_h__ + +/* This class uses a pair of regions and swaps between them while + * accumulating to avoid the heap allocations associated with + * modifying a region in place. + * + * It is sizeof(T)*2 + sizeof(T*) and can use end up using + * approximately double the amount of memory as using single + * region so use it sparingly. + */ + +template +class PingPongRegion +{ + typedef typename T::RectType RectType; +public: + PingPongRegion() + { + rgn = &rgn1; + } + + void SubOut(const RectType& aOther) + { + T* nextRgn = nextRegion(); + nextRgn->Sub(*rgn, aOther); + rgn = nextRgn; + } + + void OrWith(const RectType& aOther) + { + T* nextRgn = nextRegion(); + nextRgn->Or(*rgn, aOther); + rgn = nextRgn; + } + + T& Region() + { + return *rgn; + } + +private: + + T* nextRegion() + { + if (rgn == &rgn1) { + return &rgn2; + } else { + return &rgn1; + } + } + + T* rgn; + T rgn1; + T rgn2; +}; + +#endif diff --git a/gfx/src/TiledRegion.cpp b/gfx/src/TiledRegion.cpp new file mode 100644 index 0000000000..fb14012fa7 --- /dev/null +++ b/gfx/src/TiledRegion.cpp @@ -0,0 +1,357 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "TiledRegion.h" + +#include + +#include "mozilla/fallible.h" + +namespace mozilla { +namespace gfx { + +static const int32_t kTileSize = 256; + +/** + * TiledRegionImpl stores an array of non-empty rectangles (pixman_box32_ts) to + * represent the region. Each rectangle is contained in a single tile; + * rectangles never cross tile boundaries. The rectangles are sorted by their + * tile's origin in top-to-bottom, left-to-right order. + * (Note that this can mean that a rectangle r1 can come before another + * rectangle r2 even if r2.y1 < r1.y1, as long as the two rects are in the same + * row of tiles and r1.x1 < r2.x1.) + * Empty tiles take up no space in the array - there is no rectangle stored for + * them. As a result, any algorithm that needs to deal with empty tiles will + * iterate through the mRects array and compare the positions of two + * consecutive rects to figure out whether there are any empty tiles between + * them. + */ + +static pixman_box32_t +IntersectionOfNonEmptyBoxes(const pixman_box32_t& aBox1, + const pixman_box32_t& aBox2) +{ + return pixman_box32_t { + std::max(aBox1.x1, aBox2.x1), + std::max(aBox1.y1, aBox2.y1), + std::min(aBox1.x2, aBox2.x2), + std::min(aBox1.y2, aBox2.y2) + }; +} + +// A TileIterator points to a specific tile inside a certain tile range, or to +// the end of the tile range. Advancing a TileIterator will move to the next +// tile inside the range (or to the range end). The next tile is either the +// tile to the right of the current one, or the first tile of the next tile +// row if the current tile is already the last tile in the row. +class TileIterator { +public: + TileIterator(const pixman_box32_t& aTileBounds, const IntPoint& aPosition) + : mTileBounds(aTileBounds) + , mPos(aPosition) + {} + + bool operator!=(const TileIterator& aOther) { return mPos != aOther.mPos; } + bool operator==(const TileIterator& aOther) { return mPos == aOther.mPos; } + + IntPoint operator*() const { return mPos; } + + const TileIterator& operator++() { + mPos.x += kTileSize; + if (mPos.x >= mTileBounds.x2) { + mPos.x = mTileBounds.x1; + mPos.y += kTileSize; + } + return *this; + } + + TileIterator& operator=(const IntPoint& aPosition) + { + mPos = aPosition; + return *this; + } + + bool IsBeforeTileContainingPoint(const IntPoint& aPoint) const + { + return (mPos.y + kTileSize) <= aPoint.y || + (mPos.y <= aPoint.y && (mPos.x + kTileSize) <= aPoint.x); + } + + bool IsAtTileContainingPoint(const IntPoint& aPoint) const + { + return mPos.y <= aPoint.y && aPoint.y < (mPos.y + kTileSize) && + mPos.x <= aPoint.x && aPoint.x < (mPos.x + kTileSize); + + } + + pixman_box32_t IntersectionWith(const pixman_box32_t& aRect) const + { + pixman_box32_t tile = { mPos.x, mPos.y, + mPos.x + kTileSize, mPos.y + kTileSize }; + return IntersectionOfNonEmptyBoxes(tile, aRect); + } + +private: + const pixman_box32_t& mTileBounds; + IntPoint mPos; +}; + +// A TileRange describes a range of tiles contained inside a certain tile +// bounds (which is a rectangle that includes all tiles that you're +// interested in). The tile range can start and end at any point inside a +// tile row. +// The tile range end is described by the tile that starts at the bottom +// left corner of the tile bounds, i.e. the first tile under the tile +// bounds. +class TileRange { +public: + // aTileBounds, aStart and aEnd need to be aligned with the tile grid. + TileRange(const pixman_box32_t& aTileBounds, + const IntPoint& aStart, const IntPoint& aEnd) + : mTileBounds(aTileBounds) + , mStart(aStart) + , mEnd(aEnd) + {} + // aTileBounds needs to be aligned with the tile grid. + explicit TileRange(const pixman_box32_t& aTileBounds) + : mTileBounds(aTileBounds) + , mStart(mTileBounds.x1, mTileBounds.y1) + , mEnd(mTileBounds.x1, mTileBounds.y2) + {} + + TileIterator Begin() const { return TileIterator(mTileBounds, mStart); } + TileIterator End() const { return TileIterator(mTileBounds, mEnd); } + + // The number of tiles in this tile range. + size_t Length() const + { + if (mEnd.y == mStart.y) { + return (mEnd.x - mStart.x) / kTileSize; + } + size_t numberOfFullRows = (mEnd.y - mStart.y) / kTileSize - 1; + return ((mTileBounds.x2 - mStart.x) + + (mTileBounds.x2 - mTileBounds.x1) * numberOfFullRows + + (mEnd.x - mTileBounds.x1)) / kTileSize; + } + + // If aTileOrigin does not describe a tile inside our tile bounds, move it + // to the next tile that you'd encounter by "advancing" a tile iterator + // inside these tile bounds. If aTileOrigin is after the last tile inside + // our tile bounds, move it to the range end tile. + // The result of this method is a valid end tile for a tile range with our + // tile bounds. + IntPoint MoveIntoBounds(const IntPoint& aTileOrigin) const + { + IntPoint p = aTileOrigin; + if (p.x < mTileBounds.x1) { + p.x = mTileBounds.x1; + } else if (p.x >= mTileBounds.x2) { + p.x = mTileBounds.x1; + p.y += kTileSize; + } + if (p.y < mTileBounds.y1) { + p.y = mTileBounds.y1; + p.x = mTileBounds.x1; + } else if (p.y >= mTileBounds.y2) { + // There's only one valid state after the end of the tile range, and that's + // the bottom left point of the tile bounds. + p.x = mTileBounds.x1; + p.y = mTileBounds.y2; + } + return p; + } + +private: + const pixman_box32_t& mTileBounds; + const IntPoint mStart; + const IntPoint mEnd; +}; + +static IntPoint +TileContainingPoint(const IntPoint& aPoint) +{ + return IntPoint(RoundDownToMultiple(aPoint.x, kTileSize), + RoundDownToMultiple(aPoint.y, kTileSize)); +} + +enum class IterationAction : uint8_t { + CONTINUE, + STOP +}; + +enum class IterationEndReason : uint8_t { + NOT_STOPPED, + STOPPED +}; + +template< + typename HandleEmptyTilesFunction, + typename HandleNonEmptyTileFunction, + typename RectArrayT> +IterationEndReason ProcessIntersectedTiles(const pixman_box32_t& aRect, + RectArrayT& aRectArray, + HandleEmptyTilesFunction aHandleEmptyTiles, + HandleNonEmptyTileFunction aHandleNonEmptyTile) +{ + pixman_box32_t tileBounds = { + RoundDownToMultiple(aRect.x1, kTileSize), + RoundDownToMultiple(aRect.y1, kTileSize), + RoundUpToMultiple(aRect.x2, kTileSize), + RoundUpToMultiple(aRect.y2, kTileSize) + }; + + TileRange tileRange(tileBounds); + TileIterator rangeEnd = tileRange.End(); + + // tileIterator points to the next tile in tileRange, or to rangeEnd if we're + // done. + TileIterator tileIterator = tileRange.Begin(); + + // We iterate over the rectangle array. Depending on the position of the + // rectangle we encounter, we may need to advance tileIterator by zero, one, + // or more tiles: + // - Zero if the rectangle we encountered is outside the tiles that + // intersect aRect. + // - One if the rectangle is in the exact tile that we're interested in next + // (i.e. the tile that tileIterator points at). + // - More than one if the encountered rectangle is in a tile that's further + // to the right or to the bottom than tileIterator. In that case there is + // at least one empty tile between the last rectangle we encountered and + // the current one. + for (size_t i = 0; i < aRectArray.Length() && tileIterator != rangeEnd; i++) { + MOZ_ASSERT(aRectArray[i].x1 < aRectArray[i].x2 && aRectArray[i].y1 < aRectArray[i].y2, "empty rect"); + IntPoint rectOrigin(aRectArray[i].x1, aRectArray[i].y1); + if (tileIterator.IsBeforeTileContainingPoint(rectOrigin)) { + IntPoint tileOrigin = TileContainingPoint(rectOrigin); + IntPoint afterEmptyTiles = tileRange.MoveIntoBounds(tileOrigin); + TileRange emptyTiles(tileBounds, *tileIterator, afterEmptyTiles); + if (aHandleEmptyTiles(aRectArray, i, emptyTiles) == IterationAction::STOP) { + return IterationEndReason::STOPPED; + } + tileIterator = afterEmptyTiles; + if (tileIterator == rangeEnd) { + return IterationEndReason::NOT_STOPPED; + } + } + if (tileIterator.IsAtTileContainingPoint(rectOrigin)) { + pixman_box32_t rectIntersection = tileIterator.IntersectionWith(aRect); + if (aHandleNonEmptyTile(aRectArray, i, rectIntersection) == IterationAction::STOP) { + return IterationEndReason::STOPPED; + } + ++tileIterator; + } + } + + if (tileIterator != rangeEnd) { + // We've looked at all of our existing rectangles but haven't covered all + // of the tiles that we're interested in yet. So we need to deal with the + // remaining tiles now. + size_t endIndex = aRectArray.Length(); + TileRange emptyTiles(tileBounds, *tileIterator, *rangeEnd); + if (aHandleEmptyTiles(aRectArray, endIndex, emptyTiles) == IterationAction::STOP) { + return IterationEndReason::STOPPED; + } + } + return IterationEndReason::NOT_STOPPED; +} + +static pixman_box32_t +UnionBoundsOfNonEmptyBoxes(const pixman_box32_t& aBox1, + const pixman_box32_t& aBox2) +{ + return { std::min(aBox1.x1, aBox2.x1), + std::min(aBox1.y1, aBox2.y1), + std::max(aBox1.x2, aBox2.x2), + std::max(aBox1.y2, aBox2.y2) }; +} + +// Returns true when adding the rectangle was successful, and false if +// allocation failed. +// When this returns false, our internal state might not be consistent and we +// need to be cleared. +bool +TiledRegionImpl::AddRect(const pixman_box32_t& aRect) +{ + // We are adding a rectangle that can span multiple tiles. + // For each empty tile that aRect intersects, we need to add the intersection + // of aRect with that tile to mRects, respecting the order of mRects. + // For each tile that already has a rectangle, we need to enlarge that + // existing rectangle to include the intersection of aRect with the tile. + return ProcessIntersectedTiles(aRect, mRects, + [&aRect](nsTArray& rects, size_t& rectIndex, TileRange emptyTiles) { + if (!rects.InsertElementsAt(rectIndex, emptyTiles.Length(), fallible)) { + return IterationAction::STOP; + } + for (TileIterator tileIt = emptyTiles.Begin(); + tileIt != emptyTiles.End(); + ++tileIt, ++rectIndex) { + rects[rectIndex] = tileIt.IntersectionWith(aRect); + } + return IterationAction::CONTINUE; + }, + [](nsTArray& rects, size_t rectIndex, const pixman_box32_t& rectIntersectionWithTile) { + rects[rectIndex] = + UnionBoundsOfNonEmptyBoxes(rects[rectIndex], rectIntersectionWithTile); + return IterationAction::CONTINUE; + }) == IterationEndReason::NOT_STOPPED; +} + +static bool +NonEmptyBoxesIntersect(const pixman_box32_t& aBox1, const pixman_box32_t& aBox2) +{ + return aBox1.x1 < aBox2.x2 && aBox2.x1 < aBox1.x2 && + aBox1.y1 < aBox2.y2 && aBox2.y1 < aBox1.y2; +} + +bool +TiledRegionImpl::Intersects(const pixman_box32_t& aRect) const +{ + // aRect intersects this region if it intersects any of our rectangles. + return ProcessIntersectedTiles(aRect, mRects, + [](const nsTArray& rects, size_t& rectIndex, TileRange emptyTiles) { + // Ignore empty tiles and keep on iterating. + return IterationAction::CONTINUE; + }, + [](const nsTArray& rects, size_t rectIndex, const pixman_box32_t& rectIntersectionWithTile) { + if (NonEmptyBoxesIntersect(rects[rectIndex], rectIntersectionWithTile)) { + // Found an intersecting rectangle, so aRect intersects this region. + return IterationAction::STOP; + } + return IterationAction::CONTINUE; + }) == IterationEndReason::STOPPED; +} + +static bool +NonEmptyBoxContainsNonEmptyBox(const pixman_box32_t& aBox1, const pixman_box32_t& aBox2) +{ + return aBox1.x1 <= aBox2.x1 && aBox2.x2 <= aBox1.x2 && + aBox1.y1 <= aBox2.y1 && aBox2.y2 <= aBox1.y2; +} + +bool +TiledRegionImpl::Contains(const pixman_box32_t& aRect) const +{ + // aRect is contained in this region if aRect does not intersect any empty + // tiles and, for each non-empty tile, if the intersection of aRect with that + // tile is contained in the existing rectangle we have in that tile. + return ProcessIntersectedTiles(aRect, mRects, + [](const nsTArray& rects, size_t& rectIndex, TileRange emptyTiles) { + // Found an empty tile that intersects aRect, so aRect is not contained + // in this region. + return IterationAction::STOP; + }, + [](const nsTArray& rects, size_t rectIndex, const pixman_box32_t& rectIntersectionWithTile) { + if (!NonEmptyBoxContainsNonEmptyBox(rects[rectIndex], rectIntersectionWithTile)) { + // Our existing rectangle in this tile does not cover the part of aRect that + // intersects this tile, so aRect is not contained in this region. + return IterationAction::STOP; + } + return IterationAction::CONTINUE; + }) == IterationEndReason::NOT_STOPPED; +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/src/TiledRegion.h b/gfx/src/TiledRegion.h new file mode 100644 index 0000000000..10e8844944 --- /dev/null +++ b/gfx/src/TiledRegion.h @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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_GFX_TILEDREGION_H_ +#define MOZILLA_GFX_TILEDREGION_H_ + +#include "mozilla/ArrayView.h" +#include "mozilla/gfx/Rect.h" +#include "mozilla/Move.h" +#include "nsRegion.h" +#include "pixman.h" + +namespace mozilla { +namespace gfx { + +// See TiledRegion.cpp for documentation on TiledRegionImpl. +class TiledRegionImpl { +public: + void Clear() { mRects.Clear(); } + bool AddRect(const pixman_box32_t& aRect); + bool Intersects(const pixman_box32_t& aRect) const; + bool Contains(const pixman_box32_t& aRect) const; + operator ArrayView() const { return ArrayView(mRects); } + +private: + nsTArray mRects; +}; + +/** + * A auto-simplifying region type that supports one rectangle per tile. + * The virtual tile grid is anchored at (0, 0) and has quadratic tiles whose + * size is hard-coded as kTileSize in TiledRegion.cpp. + * A TiledRegion starts out empty. You can add rectangles or (regular) regions + * into it by calling Add(). Add() is a mutating union operation (similar to + * OrWith on nsRegion) that's *not* exact, because it will enlarge the region as + * necessary to satisfy the "one rectangle per tile" requirement. + * Tiled regions convert implicitly to the underlying regular region type. + * The only way to remove parts from a TiledRegion is by calling SetEmpty(). + */ +template +class TiledRegion { +public: + typedef typename RegionT::RectType RectT; + + TiledRegion() + : mCoversBounds(false) + {} + + TiledRegion(const TiledRegion& aOther) + : mBounds(aOther.mBounds) + , mImpl(aOther.mImpl) + , mCoversBounds(false) + {} + + TiledRegion(TiledRegion&& aOther) + : mBounds(aOther.mBounds) + , mImpl(Move(aOther.mImpl)) + , mCoversBounds(false) + {} + + RegionT GetRegion() const + { + if (mBounds.IsEmpty()) { + return RegionT(); + } + if (mCoversBounds) { + // Rect limit hit or allocation failed, treat as 1 rect. + return RegionT(mBounds); + } + return RegionT(mImpl); + } + + TiledRegion& operator=(const TiledRegion& aOther) + { + if (&aOther != this) { + mBounds = aOther.mBounds; + mImpl = aOther.mImpl; + mCoversBounds = aOther.mCoversBounds; + } + return *this; + } + + void Add(const RectT& aRect) + { + if (aRect.IsEmpty()) { + return; + } + + mBounds = mBounds.Union(aRect); + + if (mCoversBounds) { + return; + } + if (ExceedsMaximumSize()) { + FallBackToBounds(); + return; + } + + if (!mImpl.AddRect(RectToBox(aRect))) { + FallBackToBounds(); + } + } + + void Add(const RegionT& aRegion) + { + mBounds = mBounds.Union(aRegion.GetBounds()); + + if (mCoversBounds) { + return; + } + if (ExceedsMaximumSize()) { + FallBackToBounds(); + return; + } + + for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { + RectT r = iter.Get(); + MOZ_ASSERT(!r.IsEmpty()); + if (!mImpl.AddRect(RectToBox(r))) { + FallBackToBounds(); + return; + } + } + } + + bool IsEmpty() const { return mBounds.IsEmpty(); } + + void SetEmpty() + { + mBounds.SetEmpty(); + mImpl.Clear(); + mCoversBounds = false; + } + + RectT GetBounds() const { return mBounds; } + + bool Intersects(const RectT& aRect) const + { + if (!mBounds.Intersects(aRect)) { + return false; + } + if (mCoversBounds) { + return true; + } + + return mImpl.Intersects(RectToBox(aRect)); + } + + bool Contains(const RectT& aRect) const + { + if (!mBounds.Contains(aRect)) { + return false; + } + if (mCoversBounds) { + return true; + } + return mImpl.Contains(RectToBox(aRect)); + } + +private: + + bool ExceedsMaximumSize() const + { + // This stops us from allocating insane numbers of tiles. + return mBounds.width >= 50 * 256 || mBounds.height >= 50 * 256; + } + + void FallBackToBounds() + { + mCoversBounds = true; + mImpl.Clear(); + } + + static pixman_box32_t RectToBox(const RectT& aRect) + { + return { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() }; + } + + RectT mBounds; + TiledRegionImpl mImpl; + + // mCoversBounds is true if we bailed out due to a large number of tiles. + // mCoversBounds being true means that this TiledRegion is just a simple + // rectangle (our mBounds). + // Once set to true, the TiledRegion will stay in this state until SetEmpty + // is called. + bool mCoversBounds; +}; + +typedef TiledRegion TiledIntRegion; + +} // namespace gfx +} // namespace mozilla + +#endif /* MOZILLA_GFX_TILEDREGION_H_ */ diff --git a/gfx/src/moz.build b/gfx/src/moz.build index 87d22b63d9..5dacae872d 100644 --- a/gfx/src/moz.build +++ b/gfx/src/moz.build @@ -37,6 +37,7 @@ EXPORTS += [ 'nsSize.h', 'nsThemeConstants.h', 'nsTransform2D.h', + 'PingPongRegion.h', 'RegionBuilder.h', ] @@ -45,6 +46,10 @@ EXPORTS.mozilla += [ 'ArrayView.h', ] +EXPORTS.mozilla.gfx += [ + 'TiledRegion.h', +] + if CONFIG['MOZ_X11']: EXPORTS.mozilla += ['X11Util.h'] SOURCES += [ @@ -65,6 +70,7 @@ UNIFIED_SOURCES += [ 'nsThebesFontEnumerator.cpp', 'nsThebesGfxFactory.cpp', 'nsTransform2D.cpp', + 'TiledRegion.cpp', ] # nsDeviceContext.cpp cannot be built in unified mode because it pulls in OS X system headers. diff --git a/gfx/tests/gtest/TestRegion.cpp b/gfx/tests/gtest/TestRegion.cpp index 1c6b640245..9d39471be7 100644 --- a/gfx/tests/gtest/TestRegion.cpp +++ b/gfx/tests/gtest/TestRegion.cpp @@ -5,13 +5,16 @@ #include +#include "PingPongRegion.h" #include "gtest/gtest.h" #include "gtest/MozGTestBench.h" #include "nsRect.h" #include "nsRegion.h" #include "RegionBuilder.h" +#include "mozilla/gfx/TiledRegion.h" using namespace std; +using namespace mozilla::gfx; class TestLargestRegion { public: @@ -563,15 +566,142 @@ TEST(Gfx, RegionVisitEdges) { TestVisit(r); } +} +TEST(Gfx, PingPongRegion) { + nsRect rects[] = { + nsRect(4, 1, 61, 49), + nsRect(115, 1, 99, 49), + nsRect(115, 49, 99, 1), + nsRect(12, 50, 11, 5), + nsRect(25, 50, 28, 5), + nsRect(115, 50, 99, 5), + nsRect(115, 55, 99, 12), + }; + + // Test accumulations of various sizes to make sure + // the ping-pong behavior of PingPongRegion is working. + for (size_t size = 0; size < mozilla::ArrayLength(rects); size++) { + // bug 1130978. + nsRegion r; + PingPongRegion ar; + for (size_t i = 0; i < size; i++) { + r.Or(r, rects[i]); + ar.OrWith(rects[i]); + EXPECT_TRUE(ar.Region().IsEqual(r)); + } + + for (size_t i = 0; i < size; i++) { + ar.SubOut(rects[i]); + r.SubOut(rects[i]); + EXPECT_TRUE(ar.Region().IsEqual(r)); + } + } +} + +// The TiledRegion tests use nsIntRect / IntRegion because nsRect doesn't have +// InflateToMultiple which is required by TiledRegion. +TEST(Gfx, TiledRegionNoSimplification2Rects) { + // Add two rectangles, both rectangles are completely inside + // different tiles. + nsIntRegion region; + region.OrWith(nsIntRect(50, 50, 50, 50)); + region.OrWith(nsIntRect(300, 50, 50, 50)); + + TiledIntRegion tiledRegion; + tiledRegion.Add(nsIntRect(50, 50, 50, 50)); + tiledRegion.Add(nsIntRect(300, 50, 50, 50)); + + // No simplification should have happened. + EXPECT_TRUE(region.IsEqual(tiledRegion.GetRegion())); +} + +TEST(Gfx, TiledRegionNoSimplification1Region) { + // Add two rectangles, both rectangles are completely inside + // different tiles. + nsIntRegion region; + region.OrWith(nsIntRect(50, 50, 50, 50)); + region.OrWith(nsIntRect(300, 50, 50, 50)); + + TiledIntRegion tiledRegion; + tiledRegion.Add(region); + + // No simplification should have happened. + EXPECT_TRUE(region.IsEqual(tiledRegion.GetRegion())); +} + +TEST(Gfx, TiledRegionWithSimplification3Rects) { + // Add three rectangles. The first two rectangles are completely inside + // different tiles, but the third rectangle intersects both tiles. + TiledIntRegion tiledRegion; + tiledRegion.Add(nsIntRect(50, 50, 50, 50)); + tiledRegion.Add(nsIntRect(300, 50, 50, 50)); + tiledRegion.Add(nsIntRect(250, 70, 10, 10)); + + // Both tiles should have simplified their rectangles, and those two + // rectangles are adjacent to each other, so they just build up one rect. + EXPECT_TRUE(tiledRegion.GetRegion().IsEqual(nsIntRect(50, 50, 300, 50))); +} + +TEST(Gfx, TiledRegionWithSimplification1Region) { + // Add three rectangles. The first two rectangles are completely inside + // different tiles, but the third rectangle intersects both tiles. + nsIntRegion region; + region.OrWith(nsIntRect(50, 50, 50, 50)); + region.OrWith(nsIntRect(300, 50, 50, 50)); + region.OrWith(nsIntRect(250, 70, 10, 10)); + + TiledIntRegion tiledRegion; + tiledRegion.Add(region); + + // Both tiles should have simplified their rectangles, and those two + // rectangles are adjacent to each other, so they just build up one rect. + EXPECT_TRUE(tiledRegion.GetRegion().IsEqual(nsIntRect(50, 50, 300, 50))); +} + +TEST(Gfx, TiledRegionContains) { + // Add three rectangles. The first two rectangles are completely inside + // different tiles, but the third rectangle intersects both tiles. + TiledIntRegion tiledRegion; + tiledRegion.Add(nsIntRect(50, 50, 50, 50)); + tiledRegion.Add(nsIntRect(300, 50, 50, 50)); + tiledRegion.Add(nsIntRect(250, 70, 10, 10)); + + // Both tiles should have simplified their rectangles, and those two + // rectangles are adjacent to each other, so they just build up one rect. + EXPECT_TRUE(tiledRegion.Contains(nsIntRect(50, 50, 300, 50))); + EXPECT_TRUE(tiledRegion.Contains(nsIntRect(50, 50, 50, 50))); + EXPECT_FALSE(tiledRegion.Contains(nsIntRect(50, 50, 301, 50))); +} + +TEST(Gfx, TiledRegionIntersects) { + // Add three rectangles. The first two rectangles are completely inside + // different tiles, but the third rectangle intersects both tiles. + TiledIntRegion tiledRegion; + tiledRegion.Add(nsIntRect(50, 50, 50, 50)); + tiledRegion.Add(nsIntRect(300, 50, 50, 50)); + tiledRegion.Add(nsIntRect(250, 70, 10, 10)); + + // Both tiles should have simplified their rectangles, and those two + // rectangles are adjacent to each other, so they just build up one rect. + EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(50, 50, 300, 50))); + EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(200, 10, 10, 50))); + EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(50, 50, 301, 50))); + EXPECT_FALSE(tiledRegion.Intersects(nsIntRect(0, 0, 50, 500))); } MOZ_GTEST_BENCH(GfxBench, RegionOr, []{ const int size = 5000; + nsRegion r; for (int i = 0; i < size; i++) { r = r.Or(r, nsRect(i, i, i + 10, i + 10)); } + + nsIntRegion rInt; + for (int i = 0; i < size; i++) { + rInt = rInt.Or(rInt, nsIntRect(i, i, i + 10, i + 10)); + } }); MOZ_GTEST_BENCH(GfxBench, RegionAnd, []{ @@ -584,7 +714,7 @@ MOZ_GTEST_BENCH(GfxBench, RegionAnd, []{ } }); -void TestExec() { +void BenchRegionBuilderOr() { const int size = 5000; RegionBuilder r; @@ -601,6 +731,26 @@ void TestExec() { } MOZ_GTEST_BENCH(GfxBench, RegionBuilderOr, []{ - TestExec(); + BenchRegionBuilderOr(); +}); + +void BenchPingPongRegionOr() { + const int size = 5000; + + PingPongRegion r; + for (int i = 0; i < size; i++) { + r.OrWith(nsRect(i, i, i + 10, i + 10)); + } + r.Region(); + + PingPongRegion rInt; + for (int i = 0; i < size; i++) { + rInt.OrWith(nsIntRect(i, i, i + 10, i + 10)); + } + rInt.Region(); +} + +MOZ_GTEST_BENCH(GfxBench, PingPongRegionOr, []{ + BenchPingPongRegionOr(); }); diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index d0477c94b3..b9072dbfb8 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -76,6 +76,7 @@ var ignoreCallees = { "mozilla::CycleCollectedJSRuntime.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC. "PLDHashTableOps.hashKey" : true, "z_stream_s.zfree" : true, + "z_stream_s.zalloc" : true, "GrGLInterface.fCallback" : true, "std::strstreambuf._M_alloc_fun" : true, "std::strstreambuf._M_free_fun" : true, diff --git a/js/src/jit/ExecutableAllocator.h b/js/src/jit/ExecutableAllocator.h index a5e39117a7..8d64d4314a 100644 --- a/js/src/jit/ExecutableAllocator.h +++ b/js/src/jit/ExecutableAllocator.h @@ -307,8 +307,11 @@ class ExecutableAllocator ExecutableAllocator(const ExecutableAllocator&) = delete; void operator=(const ExecutableAllocator&) = delete; +#ifdef NON_WRITABLE_JIT_CODE MOZ_WARN_UNUSED_RESULT static bool reprotectRegion(void*, size_t, ProtectionSetting); +#endif + void reprotectAll(ProtectionSetting); // These are strong references; they keep pools alive. diff --git a/js/src/jit/ExecutableAllocatorPosix.cpp b/js/src/jit/ExecutableAllocatorPosix.cpp index b2be5abcfe..488556c66e 100644 --- a/js/src/jit/ExecutableAllocatorPosix.cpp +++ b/js/src/jit/ExecutableAllocatorPosix.cpp @@ -78,10 +78,11 @@ ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc) static const unsigned FLAGS_RW = PROT_READ | PROT_WRITE; static const unsigned FLAGS_RX = PROT_READ | PROT_EXEC; +#if defined(NON_WRITABLE_JIT_CODE) + bool ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting setting) { - MOZ_ASSERT(NON_WRITABLE_JIT_CODE); MOZ_ASSERT(pageSize); // Calculate the start of the page containing this region, @@ -98,6 +99,8 @@ ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting return !mprotect(pageStart, size, (setting == Writable) ? FLAGS_RW : FLAGS_RX); } +#endif // defined(NON_WRITABLE_JIT_CODE) + /* static */ unsigned ExecutableAllocator::initialProtectionFlags(ProtectionSetting protection) { diff --git a/js/src/jit/ExecutableAllocatorWin.cpp b/js/src/jit/ExecutableAllocatorWin.cpp index 6219298802..bad54e4134 100644 --- a/js/src/jit/ExecutableAllocatorWin.cpp +++ b/js/src/jit/ExecutableAllocatorWin.cpp @@ -239,10 +239,11 @@ ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc) DeallocateExecutableMemory(alloc.pages, alloc.size, pageSize); } +#if defined(NON_WRITABLE_JIT_CODE) + bool ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting setting) { - MOZ_ASSERT(NON_WRITABLE_JIT_CODE); MOZ_ASSERT(pageSize); // Calculate the start of the page containing this region, @@ -261,6 +262,8 @@ ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting return VirtualProtect(pageStart, size, flags, &oldProtect); } +#endif // defined(NON_WRITABLE_JIT_CODE) + /* static */ unsigned ExecutableAllocator::initialProtectionFlags(ProtectionSetting protection) { diff --git a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp index 2230089a0e..197c7740b7 100644 --- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp +++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp @@ -467,6 +467,9 @@ MacroAssemblerMIPSShared::ma_b(Register lhs, T rhs, wasm::JumpTarget target, Con template void MacroAssemblerMIPSShared::ma_b(Register lhs, Register rhs, wasm::JumpTarget target, Condition c, JumpKind jumpKind); +template void MacroAssemblerMIPSShared::ma_b(Register lhs, Imm32 rhs, + wasm::JumpTarget target, Condition c, + JumpKind jumpKind); template void MacroAssemblerMIPSShared::ma_b(Register lhs, ImmTag rhs, wasm::JumpTarget target, Condition c, JumpKind jumpKind); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 822e18c11d..4be9177e8a 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1078,8 +1078,6 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen) if (!src) return nullptr; - bool exprBody = fun->isExprBody(); - // The source data for functions created by calling the Function // constructor is only the function's body. This depends on the fact, // asserted below, that in Function("function f() {}"), the inner @@ -1092,7 +1090,7 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen) // Functions created with the constructor can't be arrow functions or // expression closures. MOZ_ASSERT_IF(funCon, !fun->isArrow()); - MOZ_ASSERT_IF(funCon, !exprBody); + MOZ_ASSERT_IF(funCon, !fun->isExprBody()); MOZ_ASSERT_IF(!funCon && !fun->isArrow(), src->length() > 0 && src->latin1OrTwoByteChar(0) == '('); @@ -1464,11 +1462,6 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti if (cx->zone()->needsIncrementalBarrier()) LazyScript::writeBarrierPre(lazy); - // Suppress GC for now although we should be able to remove this by - // making 'lazy' a Rooted (which requires adding a - // THING_ROOT_LAZY_SCRIPT). - AutoSuppressGC suppressGC(cx); - RootedScript script(cx, lazy->maybeScript()); // Only functions without inner functions or direct eval are diff --git a/js/src/json.cpp b/js/src/json.cpp index 5c446b7eb9..4150e9fe4f 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -372,6 +372,9 @@ JO(JSContext* cx, HandleObject obj, StringifyContext* scx) bool wroteMember = false; RootedId id(cx); for (size_t i = 0, len = propertyList.length(); i < len; i++) { + if (!CheckForInterrupt(cx)) + return false; + /* * Steps 8a-8b. Note that the call to Str is broken up into 1) getting * the property; 2) processing for toJSON, calling the replacer, and @@ -459,6 +462,9 @@ JA(JSContext* cx, HandleObject obj, StringifyContext* scx) /* Steps 7-10. */ RootedValue outputValue(cx); for (uint32_t i = 0; i < length; i++) { + if (!CheckForInterrupt(cx)) + return false; + /* * Steps 8a-8c. Again note how the call to the spec's Str method * is broken up into getting the property, running it past toJSON @@ -776,6 +782,9 @@ Walk(JSContext* cx, HandleObject holder, HandleId name, HandleValue reviver, Mut RootedId id(cx); RootedValue newElement(cx); for (uint32_t i = 0; i < length; i++) { + if (!CheckForInterrupt(cx)) + return false; + if (!IndexToId(cx, i, &id)) return false; @@ -806,6 +815,9 @@ Walk(JSContext* cx, HandleObject holder, HandleId name, HandleValue reviver, Mut RootedId id(cx); RootedValue newElement(cx); for (size_t i = 0, len = keys.length(); i < len; i++) { + if (!CheckForInterrupt(cx)) + return false; + /* Step 2b(ii)(1). */ id = keys[i]; if (!Walk(cx, obj, id, reviver, &newElement)) diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 19d4f0f32d..6e3fa463f7 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -2220,6 +2220,7 @@ ScriptSource::~ScriptSource() } }; + MOZ_ASSERT(refs == 0); MOZ_ASSERT_IF(inCompressedSourceSet, data.is()); DestroyMatcher dm(*this); diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h index 71c8f6744d..e7f9e4a9b0 100644 --- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -87,9 +87,10 @@ SetFrameArgumentsObject(JSContext* cx, AbstractFramePtr frame, inline JSFunction* LazyScript::functionDelazifying(JSContext* cx) const { - if (function_ && !function_->getOrCreateScript(cx)) + Rooted self(cx, this); + if (self->function_ && !self->function_->getOrCreateScript(cx)) return nullptr; - return function_; + return self->function_; } } // namespace js diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 5c4fd4d5fa..92b3792943 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -68,7 +68,6 @@ #include "nsContentCreatorFunctions.h" #include "mozilla/dom/FormData.h" -#include "nsHostObjectProtocolHandler.h" #include "nsHostObjectURI.h" #include "nsGlobalWindowCommands.h" #include "nsIControllerCommandTable.h" @@ -311,10 +310,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(txNodeSetAdaptor, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMSerializer) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsXMLHttpRequest, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(FormData) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsBlobProtocolHandler) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaStreamProtocolHandler) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaSourceProtocolHandler) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsFontTableProtocolHandler) NS_GENERIC_FACTORY_CONSTRUCTOR(nsHostObjectURI) NS_GENERIC_FACTORY_CONSTRUCTOR(DOMParser) NS_GENERIC_FACTORY_CONSTRUCTOR(Exception) @@ -766,10 +761,6 @@ NS_DEFINE_NAMED_CID(TRANSFORMIIX_XPATH_EVALUATOR_CID); NS_DEFINE_NAMED_CID(TRANSFORMIIX_NODESET_CID); NS_DEFINE_NAMED_CID(NS_XMLSERIALIZER_CID); NS_DEFINE_NAMED_CID(NS_FORMDATA_CID); -NS_DEFINE_NAMED_CID(NS_BLOBPROTOCOLHANDLER_CID); -NS_DEFINE_NAMED_CID(NS_MEDIASTREAMPROTOCOLHANDLER_CID); -NS_DEFINE_NAMED_CID(NS_MEDIASOURCEPROTOCOLHANDLER_CID); -NS_DEFINE_NAMED_CID(NS_FONTTABLEPROTOCOLHANDLER_CID); NS_DEFINE_NAMED_CID(NS_HOSTOBJECTURI_CID); NS_DEFINE_NAMED_CID(NS_XMLHTTPREQUEST_CID); NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID); @@ -1081,10 +1072,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { { &kTRANSFORMIIX_NODESET_CID, false, nullptr, txNodeSetAdaptorConstructor }, { &kNS_XMLSERIALIZER_CID, false, nullptr, nsDOMSerializerConstructor }, { &kNS_FORMDATA_CID, false, nullptr, FormDataConstructor }, - { &kNS_BLOBPROTOCOLHANDLER_CID, false, nullptr, nsBlobProtocolHandlerConstructor }, - { &kNS_MEDIASTREAMPROTOCOLHANDLER_CID, false, nullptr, nsMediaStreamProtocolHandlerConstructor }, - { &kNS_MEDIASOURCEPROTOCOLHANDLER_CID, false, nullptr, nsMediaSourceProtocolHandlerConstructor }, - { &kNS_FONTTABLEPROTOCOLHANDLER_CID, false, nullptr, nsFontTableProtocolHandlerConstructor }, { &kNS_HOSTOBJECTURI_CID, false, nullptr, nsHostObjectURIConstructor }, { &kNS_XMLHTTPREQUEST_CID, false, nullptr, nsXMLHttpRequestConstructor }, { &kNS_DOMPARSER_CID, false, nullptr, DOMParserConstructor }, @@ -1254,10 +1241,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { TRANSFORMIIX_NODESET_CONTRACTID, &kTRANSFORMIIX_NODESET_CID }, { NS_XMLSERIALIZER_CONTRACTID, &kNS_XMLSERIALIZER_CID }, { NS_FORMDATA_CONTRACTID, &kNS_FORMDATA_CID }, - { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX BLOBURI_SCHEME, &kNS_BLOBPROTOCOLHANDLER_CID }, - { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MEDIASTREAMURI_SCHEME, &kNS_MEDIASTREAMPROTOCOLHANDLER_CID }, - { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MEDIASOURCEURI_SCHEME, &kNS_MEDIASOURCEPROTOCOLHANDLER_CID }, - { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX FONTTABLEURI_SCHEME, &kNS_FONTTABLEPROTOCOLHANDLER_CID }, { NS_XMLHTTPREQUEST_CONTRACTID, &kNS_XMLHTTPREQUEST_CID }, { NS_DOMPARSER_CONTRACTID, &kNS_DOMPARSER_CID }, { XPC_EXCEPTION_CONTRACTID, &kNS_XPCEXCEPTION_CID }, diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 6ebfdac631..fda0cea33c 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -2996,7 +2996,9 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI, // to report the redirect timing info nsCOMPtr loadInfo; GetLoadInfo(getter_AddRefs(loadInfo)); - if (loadInfo) { + // TYPE_DOCUMENT loads don't have a loadingPrincipal, so we can't set + // AllRedirectsPassTimingAllowCheck on them. + if (loadInfo && loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT) { nsCOMPtr principal = loadInfo->LoadingPrincipal(); newTimedChannel->SetAllRedirectsPassTimingAllowCheck( mAllRedirectsPassTimingAllowCheck && diff --git a/netwerk/streamconv/converters/nsTXTToHTMLConv.cpp b/netwerk/streamconv/converters/nsTXTToHTMLConv.cpp index be5f674018..c478b876cc 100644 --- a/netwerk/streamconv/converters/nsTXTToHTMLConv.cpp +++ b/netwerk/streamconv/converters/nsTXTToHTMLConv.cpp @@ -73,7 +73,8 @@ nsTXTToHTMLConv::OnStartRequest(nsIRequest* request, nsISupports *aContext) if (NS_FAILED(rv)) return rv; nsCOMPtr inputData; - rv = NS_NewStringInputStream(getter_AddRefs(inputData), mBuffer); + NS_LossyConvertUTF16toASCII asciiData(mBuffer); + rv = NS_NewCStringInputStream(getter_AddRefs(inputData), asciiData); if (NS_FAILED(rv)) return rv; rv = mListener->OnDataAvailable(request, aContext, @@ -102,8 +103,8 @@ nsTXTToHTMLConv::OnStopRequest(nsIRequest* request, nsISupports *aContext, mBuffer.AppendLiteral("\n"); nsCOMPtr inputData; - - rv = NS_NewStringInputStream(getter_AddRefs(inputData), mBuffer); + NS_LossyConvertUTF16toASCII asciiData(mBuffer); + rv = NS_NewCStringInputStream(getter_AddRefs(inputData), asciiData); if (NS_FAILED(rv)) return rv; rv = mListener->OnDataAvailable(request, aContext, @@ -179,8 +180,8 @@ nsTXTToHTMLConv::OnDataAvailable(nsIRequest* request, nsISupports *aContext, if (!pushBuffer.IsEmpty()) { nsCOMPtr inputData; - - rv = NS_NewStringInputStream(getter_AddRefs(inputData), pushBuffer); + NS_LossyConvertUTF16toASCII asciiData(pushBuffer); + rv = NS_NewCStringInputStream(getter_AddRefs(inputData), asciiData); if (NS_FAILED(rv)) return rv; diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 50a67b1768..3045df4129 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -2652,6 +2652,7 @@ nsChildView::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, } aInvalidRegion = mBasicCompositorImage->GetUpdateRegion(); + *aBufferMode = BufferMode::BUFFER_NONE; return drawTarget.forget(); } @@ -2666,6 +2667,7 @@ nsChildView::EndRemoteDrawing() void nsChildView::CleanupRemoteDrawing() { + nsBaseWidget::CleanupRemoteDrawing(); mBasicCompositorImage = nullptr; mCornerMaskImage = nullptr; mResizerImage = nullptr; diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index 4d12ad6211..a39dea8e8b 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -1353,18 +1353,45 @@ CompositorBridgeChild* nsBaseWidget::GetRemoteRenderer() return mCompositorBridgeChild; } -already_AddRefed nsBaseWidget::StartRemoteDrawing() +already_AddRefed +nsBaseWidget::StartRemoteDrawing() { return nullptr; } +void +nsBaseWidget::CleanupRemoteDrawing() +{ + mLastBackBuffer = nullptr; +} + already_AddRefed nsBaseWidget::CreateBackBufferDrawTarget(mozilla::gfx::DrawTarget* aScreenTarget, - const LayoutDeviceIntRect& aRect) + const LayoutDeviceIntRect& aRect, + const bool aInitModeClear) { MOZ_ASSERT(aScreenTarget); - gfx::SurfaceFormat format = aScreenTarget->GetFormat() == gfx::SurfaceFormat::B8G8R8X8 ? gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::B8G8R8A8; - return aScreenTarget->CreateSimilarDrawTarget(aRect.ToUnknownRect().Size(), format); + gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8; + gfx::IntSize size = aRect.ToUnknownRect().Size(); + gfx::IntSize clientSize(GetClientSize().ToUnknownSize()); + + RefPtr target; + // Re-use back buffer if possible + if (mLastBackBuffer && + mLastBackBuffer->GetBackendType() == aScreenTarget->GetBackendType() && + mLastBackBuffer->GetFormat() == format && + size <= mLastBackBuffer->GetSize() && + mLastBackBuffer->GetSize() <= clientSize) { + target = mLastBackBuffer; + target->SetTransform(gfx::Matrix()); + if (aInitModeClear) { + target->ClearRect(gfx::Rect(0, 0, size.width, size.height)); + } + } else { + target = aScreenTarget->CreateSimilarDrawTarget(size, format); + mLastBackBuffer = target; + } + return target.forget(); } //------------------------------------------------------------------------- diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h index c7750f944b..a37deb35d3 100644 --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -33,6 +33,10 @@ class Accessible; } #endif +namespace gfx { +class DrawTarget; +} // namespace gfx + namespace layers { class BasicLayerManager; class CompositorBridgeChild; @@ -91,6 +95,7 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference protected: typedef base::Thread Thread; + typedef mozilla::gfx::DrawTarget DrawTarget; typedef mozilla::layers::BasicLayerManager BasicLayerManager; typedef mozilla::layers::BufferMode BufferMode; typedef mozilla::layers::CompositorBridgeChild CompositorBridgeChild; @@ -167,9 +172,10 @@ public: virtual void DrawWindowOverlay(LayerManagerComposite* aManager, LayoutDeviceIntRect aRect) override {} virtual already_AddRefed StartRemoteDrawing() override; virtual void EndRemoteDrawing() override { }; - virtual void CleanupRemoteDrawing() override { }; + virtual void CleanupRemoteDrawing() override; virtual already_AddRefed CreateBackBufferDrawTarget(mozilla::gfx::DrawTarget* aScreenTarget, - const LayoutDeviceIntRect& aRect) override; + const LayoutDeviceIntRect& aRect, + const bool aInitModeClear) override; virtual void UpdateThemeGeometries(const nsTArray& aThemeGeometries) override {} NS_IMETHOD SetModal(bool aModal) override; virtual uint32_t GetMaxTouchPoints() const override; @@ -515,6 +521,8 @@ protected: RefPtr mCompositorVsyncDispatcher; RefPtr mAPZC; RefPtr mAPZEventState; + // Back buffer of BasicCompositor + RefPtr mLastBackBuffer; SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback; RefPtr mShutdownObserver; RefPtr mTextEventDispatcher; diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index d5ea6a16b8..a6beea163c 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -1298,7 +1298,8 @@ class nsIWidget : public nsISupports { * Create DrawTarget used as BackBuffer of the screen */ virtual already_AddRefed CreateBackBufferDrawTarget(mozilla::gfx::DrawTarget* aScreenTarget, - const LayoutDeviceIntRect& aRect) = 0; + const LayoutDeviceIntRect& aRect, + const bool aInitModeClear) = 0; /** * A hook for the widget to prepare a Compositor, during the latter's initialization. diff --git a/widget/tests/test_bug673301.xul b/widget/tests/test_bug673301.xul index 37b5b800d6..a5736b86fa 100644 --- a/widget/tests/test_bug673301.xul +++ b/widget/tests/test_bug673301.xul @@ -33,6 +33,8 @@ transferable.setTransferData("text/unicode", document, 4); clipboard.setData(transferable, null, Components.interfaces.nsIClipboard.kGlobalClipboard); +transferable.setTransferData("text/unicode", null, 0); + SimpleTest.ok(true, "Didn't crash setting non-text data for text/unicode type"); diff --git a/xpcom/io/nsStringStream.cpp b/xpcom/io/nsStringStream.cpp index 1f13e12a57..fb7a203ce0 100644 --- a/xpcom/io/nsStringStream.cpp +++ b/xpcom/io/nsStringStream.cpp @@ -406,14 +406,6 @@ NS_NewByteInputStream(nsIInputStream** aStreamResult, return NS_OK; } -nsresult -NS_NewStringInputStream(nsIInputStream** aStreamResult, - const nsAString& aStringToRead) -{ - NS_LossyConvertUTF16toASCII data(aStringToRead); // truncates high-order bytes - return NS_NewCStringInputStream(aStreamResult, data); -} - nsresult NS_NewCStringInputStream(nsIInputStream** aStreamResult, const nsACString& aStringToRead) diff --git a/xpcom/io/nsStringStream.h b/xpcom/io/nsStringStream.h index 2af1124e2e..8c09530ebf 100644 --- a/xpcom/io/nsStringStream.h +++ b/xpcom/io/nsStringStream.h @@ -52,18 +52,6 @@ NS_NewByteInputStream(nsIInputStream** aStreamResult, const char* aStringToRead, int32_t aLength = -1, nsAssignmentType aAssignment = NS_ASSIGNMENT_DEPEND); -/** - * Factory method to get an nsInputStream from an nsAString. Result will - * implement nsIStringInputStream and nsISeekableStream. - * - * The given string data will be converted to a single-byte data buffer via - * truncation (i.e., the high-order byte of each character will be discarded). - * This could result in data-loss, so be careful when using this function. - */ -extern nsresult -NS_NewStringInputStream(nsIInputStream** aStreamResult, - const nsAString& aStringToRead); - /** * Factory method to get an nsInputStream from an nsACString. Result will * implement nsIStringInputStream and nsISeekableStream.