Merge inbound to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-08-14 08:59:59 -04:00
commit 3af2c3a70e
137 changed files with 2229 additions and 1439 deletions

View File

@ -1186,7 +1186,7 @@ nsContextMenu.prototype = {
// Save URL of clicked-on frame.
saveFrame: function () {
saveDocument(this.target.ownerDocument);
saveBrowser(this.browser, false, this.frameOuterWindowID);
},
// Helper function to wait for appropriate MIME-type headers and

View File

@ -55,6 +55,8 @@ support-files =
file_mixedContentFromOnunload.html
file_mixedContentFromOnunload_test1.html
file_mixedContentFromOnunload_test2.html
file_mixedContentFramesOnHttp.html
file_mixedPassiveContent.html
file_bug970276_popup1.html
file_bug970276_popup2.html
file_bug970276_favicon1.ico
@ -271,6 +273,9 @@ tags = mcb
tags = mcb
skip-if = buildapp == "mulet" || e10s # Bug 1093642 - test manipulates content and relies on content focus
[browser_mixedContentFromOnunload.js]
tags = mcb
[browser_mixedContentFramesOnHttp.js]
tags = mcb
[browser_bug970746.js]
[browser_bug1015721.js]
skip-if = os == 'win' || e10s # Bug 1159268 - Need a content-process safe version of synthesizeWheel
@ -488,6 +493,7 @@ skip-if = buildapp == 'mulet'
skip-if = e10s # Bug 1094240 - has findbar-related failures
[browser_registerProtocolHandler_notification.js]
[browser_no_mcb_on_http_site.js]
tags = mcb
[browser_bug1104165-switchtab-decodeuri.js]
[browser_bug1003461-switchtab-override.js]
[browser_bug1024133-switchtab-override-keynav.js]

View File

@ -0,0 +1,52 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*
* Test for Bug 1182551 -
*
* This test has a top level HTTP page with an HTTPS iframe. The HTTPS iframe
* includes an HTTP image. We check that the top level security state is
* STATE_IS_INSECURE. The mixed content from the iframe shouldn't "upgrade"
* the HTTP top level page to broken HTTPS.
*/
const gHttpTestRoot = "http://example.com/browser/browser/base/content/test/general/";
let gTestBrowser = null;
function SecStateTestsCompleted() {
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function test() {
waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [
["security.mixed_content.block_active_content", true],
["security.mixed_content.block_display_content", false]
]}, SecStateTests);
}
function SecStateTests() {
let url = gHttpTestRoot + "file_mixedContentFramesOnHttp.html";
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
whenLoaded(gTestBrowser, SecStateTest1);
gTestBrowser.contentWindow.location = url;
}
// The http page loads an https frame with an http image.
function SecStateTest1() {
// check security state is insecure
isSecurityState("insecure");
SecStateTestsCompleted();
}
function whenLoaded(aElement, aCallback) {
aElement.addEventListener("load", function onLoad() {
aElement.removeEventListener("load", onLoad, true);
executeSoon(aCallback);
}, true);
}

View File

@ -70,35 +70,6 @@ function SecStateTest2B() {
SecStateTestsCompleted();
}
// Compares the security state of the page with what is expected
function isSecurityState(expectedState) {
let ui = gTestBrowser.securityUI;
if (!ui) {
ok(false, "No security UI to get the security state");
return;
}
const wpl = Components.interfaces.nsIWebProgressListener;
// determine the security state
let isSecure = ui.state & wpl.STATE_IS_SECURE;
let isBroken = ui.state & wpl.STATE_IS_BROKEN;
let isInsecure = ui.state & wpl.STATE_IS_INSECURE;
let actualState;
if (isSecure && !(isBroken || isInsecure)) {
actualState = "secure";
} else if (isBroken && !(isSecure || isInsecure)) {
actualState = "broken";
} else if (isInsecure && !(isSecure || isBroken)) {
actualState = "insecure";
} else {
actualState = "unknown";
}
is(expectedState, actualState, "Expected state " + expectedState + " and the actual state is " + actualState + ".");
}
function whenLoaded(aElement, aCallback) {
aElement.addEventListener("load", function onLoad() {
aElement.removeEventListener("load", onLoad, true);

View File

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<!--
Test for https://bugzilla.mozilla.org/show_bug.cgi?id=1182551
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1182551</title>
</head>
<body>
<p>Test for Bug 1182551. This is an HTTP top level page. We include an HTTPS iframe that loads mixed passive content.</p>
<iframe src="https://example.org/browser/browser/base/content/test/general/file_mixedPassiveContent.html"></iframe>
</body>
</html>

View File

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<!--
Test for https://bugzilla.mozilla.org/show_bug.cgi?id=1182551
-->
<head>
<meta charset="utf-8">
<title>HTTPS page with HTTP image</title>
</head>
<body>
<img src="http://mochi.test:8888/tests/image/test/mochitest/blue.png">
</body>
</html>

View File

@ -961,3 +961,32 @@ function promiseNewSearchEngine(basename) {
});
});
}
// Compares the security state of the page with what is expected
function isSecurityState(expectedState) {
let ui = gTestBrowser.securityUI;
if (!ui) {
ok(false, "No security UI to get the security state");
return;
}
const wpl = Components.interfaces.nsIWebProgressListener;
// determine the security state
let isSecure = ui.state & wpl.STATE_IS_SECURE;
let isBroken = ui.state & wpl.STATE_IS_BROKEN;
let isInsecure = ui.state & wpl.STATE_IS_INSECURE;
let actualState;
if (isSecure && !(isBroken || isInsecure)) {
actualState = "secure";
} else if (isBroken && !(isSecure || isInsecure)) {
actualState = "broken";
} else if (isInsecure && !(isSecure || isBroken)) {
actualState = "insecure";
} else {
actualState = "unknown";
}
is(expectedState, actualState, "Expected state " + expectedState + " and the actual state is " + actualState + ".");
}

View File

@ -48,7 +48,7 @@ public class CodeGenerator {
" \"" + cls.getName().replace('.', '/') + "\";\n" +
"\n" +
"protected:\n" +
" using Class::Class;\n" +
" " + unqualifiedName + "(jobject instance) : Class(instance) {}\n" +
"\n");
cpp.append(

View File

@ -170,7 +170,9 @@ function checkIsWidgetScript(testMozbrowserEvent) {
request.onerror = onError;
if (testMozbrowserEvent) {
content.window.open("about:blank"); /* test mozbrowseropenwindow */
var win = content.window.open("about:blank"); /* test mozbrowseropenwindow */
/*Close new window to avoid mochitest "unable to restore focus" failures.*/
win.close();
content.window.scrollTo(4000, 4000); /* test mozbrowser(async)scroll */
}
}

View File

@ -3849,6 +3849,30 @@ nsContentUtils::MatchElementId(nsIContent *aContent, const nsAString& aId)
return MatchElementId(aContent, id);
}
/* static */
nsIDocument*
nsContentUtils::GetSubdocumentWithOuterWindowId(nsIDocument *aDocument,
uint64_t aOuterWindowId)
{
if (!aDocument || !aOuterWindowId) {
return nullptr;
}
nsCOMPtr<nsPIDOMWindow> window = nsGlobalWindow::GetOuterWindowWithId(aOuterWindowId);
if (!window) {
return nullptr;
}
nsCOMPtr<nsIDocument> foundDoc = window->GetDoc();
if (nsContentUtils::ContentIsCrossDocDescendantOf(foundDoc, aDocument)) {
// Note that ContentIsCrossDocDescendantOf will return true if
// foundDoc == aDocument.
return foundDoc;
}
return nullptr;
}
// Convert the string from the given encoding to Unicode.
/* static */
nsresult

View File

@ -325,6 +325,23 @@ public:
*/
static uint16_t ReverseDocumentPosition(uint16_t aDocumentPosition);
/**
* Returns a subdocument for aDocument with a particular outer window ID.
*
* @param aDocument
* The document whose subdocuments will be searched.
* @param aOuterWindowID
* The outer window ID for the subdocument to be found. This must
* be a value greater than 0.
* @return nsIDocument*
* A pointer to the found nsIDocument. nullptr if the subdocument
* cannot be found, or if either aDocument or aOuterWindowId were
* invalid. If the outer window ID belongs to aDocument itself, this
* will return a pointer to aDocument.
*/
static nsIDocument* GetSubdocumentWithOuterWindowId(nsIDocument *aDocument,
uint64_t aOuterWindowId);
static uint32_t CopyNewlineNormalizedUnicodeTo(const nsAString& aSource,
uint32_t aSrcOffset,
char16_t* aDest,

View File

@ -2885,18 +2885,31 @@ nsFrameLoader::InitializeBrowserAPI()
}
NS_IMETHODIMP
nsFrameLoader::StartPersistence(nsIWebBrowserPersistDocumentReceiver* aRecv)
nsFrameLoader::StartPersistence(uint64_t aOuterWindowID,
nsIWebBrowserPersistDocumentReceiver* aRecv)
{
if (!aRecv) {
return NS_ERROR_INVALID_POINTER;
}
if (mRemoteBrowser) {
return mRemoteBrowser->StartPersistence(aRecv);
return mRemoteBrowser->StartPersistence(aOuterWindowID, aRecv);
}
if (mDocShell) {
nsCOMPtr<nsIDocument> doc = do_GetInterface(mDocShell);
NS_ENSURE_STATE(doc);
nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(mDocShell);
nsCOMPtr<nsIDocument> foundDoc;
if (aOuterWindowID) {
foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
} else {
foundDoc = rootDoc;
}
if (!foundDoc) {
aRecv->OnError(NS_ERROR_NO_CONTENT);
} else {
nsCOMPtr<nsIWebBrowserPersistDocument> pdoc =
new mozilla::WebBrowserPersistLocalDocument(doc);
new mozilla::WebBrowserPersistLocalDocument(foundDoc);
aRecv->OnDocumentReady(pdoc);
return NS_OK;
}
return NS_ERROR_NO_CONTENT;
return NS_OK;
}

View File

@ -117,7 +117,7 @@ DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
}
event->SetTrusted(true);
// Dispatch the event.
*aStatus = nsEventStatus_eConsumeNoDefault;
// We don't initialize aStatus here, as our callers have already done so.
nsresult rv =
EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr,
static_cast<Event*>(event),
@ -177,7 +177,7 @@ BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement,
return BrowserElementParent::OPEN_WINDOW_CANCELLED;
}
nsEventStatus status;
nsEventStatus status = nsEventStatus_eIgnore;
bool dispatchSucceeded =
DispatchCustomDOMEvent(aOpenerFrameElement,
NS_LITERAL_STRING("mozbrowseropenwindow"),

View File

@ -121,7 +121,7 @@ both:
*/
PRenderFrame();
PWebBrowserPersistDocument();
PWebBrowserPersistDocument(uint64_t aOuterWindowID);
parent:
/**
@ -524,6 +524,7 @@ parent:
child:
NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);
parent:
/**

View File

@ -3118,16 +3118,28 @@ TabChildGlobal::GetGlobalJSObject()
}
PWebBrowserPersistDocumentChild*
TabChild::AllocPWebBrowserPersistDocumentChild()
TabChild::AllocPWebBrowserPersistDocumentChild(const uint64_t& aOuterWindowID)
{
return new WebBrowserPersistDocumentChild();
}
bool
TabChild::RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor)
TabChild::RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor,
const uint64_t& aOuterWindowID)
{
nsCOMPtr<nsIDocument> doc = GetDocument();
static_cast<WebBrowserPersistDocumentChild*>(aActor)->Start(doc);
nsCOMPtr<nsIDocument> rootDoc = GetDocument();
nsCOMPtr<nsIDocument> foundDoc;
if (aOuterWindowID) {
foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
} else {
foundDoc = rootDoc;
}
if (!foundDoc) {
aActor->SendInitFailure(NS_ERROR_NO_CONTENT);
} else {
static_cast<WebBrowserPersistDocumentChild*>(aActor)->Start(foundDoc);
}
return true;
}

View File

@ -495,8 +495,9 @@ public:
virtual ScreenIntSize GetInnerSize() override;
virtual PWebBrowserPersistDocumentChild* AllocPWebBrowserPersistDocumentChild() override;
virtual bool RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor) override;
virtual PWebBrowserPersistDocumentChild* AllocPWebBrowserPersistDocumentChild(const uint64_t& aOuterWindowID) override;
virtual bool RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor,
const uint64_t& aOuterWindowID) override;
virtual bool DeallocPWebBrowserPersistDocumentChild(PWebBrowserPersistDocumentChild* aActor) override;
protected:

View File

@ -3368,7 +3368,7 @@ TabParent::AsyncPanZoomEnabled() const
}
PWebBrowserPersistDocumentParent*
TabParent::AllocPWebBrowserPersistDocumentParent()
TabParent::AllocPWebBrowserPersistDocumentParent(const uint64_t& aOuterWindowID)
{
return new WebBrowserPersistDocumentParent();
}
@ -3381,11 +3381,12 @@ TabParent::DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentPar
}
NS_IMETHODIMP
TabParent::StartPersistence(nsIWebBrowserPersistDocumentReceiver* aRecv)
TabParent::StartPersistence(uint64_t aOuterWindowID,
nsIWebBrowserPersistDocumentReceiver* aRecv)
{
auto* actor = new WebBrowserPersistDocumentParent();
actor->SetOnReady(aRecv);
return SendPWebBrowserPersistDocumentConstructor(actor)
return SendPWebBrowserPersistDocumentConstructor(actor, aOuterWindowID)
? NS_OK : NS_ERROR_FAILURE;
// (The actor will be destroyed on constructor failure.)
}

View File

@ -435,7 +435,7 @@ public:
int32_t& aDragAreaX, int32_t& aDragAreaY);
layout::RenderFrameParent* GetRenderFrame();
virtual PWebBrowserPersistDocumentParent* AllocPWebBrowserPersistDocumentParent() override;
virtual PWebBrowserPersistDocumentParent* AllocPWebBrowserPersistDocumentParent(const uint64_t& aOuterWindowID) override;
virtual bool DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor) override;
protected:

View File

@ -565,6 +565,15 @@ nsJSChannel::Open2(nsIInputStream** aStream)
NS_IMETHODIMP
nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
{
#ifdef DEBUG
{
nsCOMPtr<nsILoadInfo> loadInfo = nsIChannel::GetLoadInfo();
MOZ_ASSERT(!loadInfo || loadInfo->GetSecurityMode() == 0 ||
loadInfo->GetInitialSecurityCheckDone(),
"security flags in loadInfo but asyncOpen2() not called");
}
#endif
NS_ENSURE_ARG(aListener);
// First make sure that we have a usable inner window; we'll want to make

View File

@ -47,6 +47,15 @@ private:
Atomic<bool> mRevoked;
};
enum class ListenerMode : int8_t {
// Allow at most one listener. Move will be used when possible
// to pass the event data to save copy.
Exclusive,
// This is the default. Event data will always be copied when passed
// to the listeners.
NonExclusive
};
namespace detail {
/**
@ -111,7 +120,7 @@ private:
} // namespace detail
template <typename T> class MediaEventSource;
template <typename T, ListenerMode> class MediaEventSource;
/**
* Not thread-safe since this is not meant to be shared and therefore only
@ -120,7 +129,7 @@ template <typename T> class MediaEventSource;
* listener from an event source.
*/
class MediaEventListener {
template <typename T>
template <typename T, ListenerMode>
friend class MediaEventSource;
public:
@ -154,7 +163,7 @@ private:
/**
* A generic and thread-safe class to implement the observer pattern.
*/
template <typename EventType>
template <typename EventType, ListenerMode Mode = ListenerMode::NonExclusive>
class MediaEventSource {
static_assert(!IsReference<EventType>::value, "Ref-type not supported!");
typedef typename detail::EventTypeTraits<EventType>::ArgType ArgType;
@ -246,6 +255,7 @@ class MediaEventSource {
MediaEventListener
ConnectInternal(Target* aTarget, const Function& aFunction) {
MutexAutoLock lock(mMutex);
MOZ_ASSERT(Mode == ListenerMode::NonExclusive || mListeners.IsEmpty());
auto l = mListeners.AppendElement();
l->reset(new ListenerImpl<Target, Function>(aTarget, aFunction));
return MediaEventListener((*l)->Token());
@ -346,8 +356,8 @@ private:
* and event publisher. Mostly used as a member variable to publish events
* to the listeners.
*/
template <typename EventType>
class MediaEventProducer : public MediaEventSource<EventType> {
template <typename EventType, ListenerMode Mode = ListenerMode::NonExclusive>
class MediaEventProducer : public MediaEventSource<EventType, Mode> {
public:
void Notify(const EventType& aEvent) {
this->NotifyInternal(aEvent);

View File

@ -805,7 +805,6 @@ MediaFormatReader::NotifyError(TrackType aTrack)
LOGV("%s Decoding error", TrackTypeToStr(aTrack));
auto& decoder = GetDecoderData(aTrack);
decoder.mError = true;
decoder.mNeedDraining = true;
ScheduleUpdate(aTrack);
}
@ -1144,7 +1143,7 @@ MediaFormatReader::Update(TrackType aTrack)
} else if (decoder.mDemuxEOS) {
decoder.RejectPromise(END_OF_STREAM, __func__);
}
} else if (decoder.mError && !decoder.mDecoder) {
} else if (decoder.mError) {
decoder.RejectPromise(DECODE_ERROR, __func__);
return;
} else if (decoder.mWaitingForData) {

View File

@ -1008,13 +1008,13 @@ ChannelMediaResource::CacheClientSeek(int64_t aOffset, bool aResume)
mOffset = aOffset;
// Don't report close of the channel because the channel is not closed for
// download ended, but for internal changes in the read position.
mIgnoreClose = true;
// Don't create a new channel if we are still suspended. The channel will
// be recreated when we are resumed.
if (mSuspendCount > 0) {
// Close the existing channel to force the channel to be recreated at
// the correct offset upon resume.
if (mChannel) {
mIgnoreClose = true;
CloseChannel();
}
return NS_OK;
}

View File

@ -16,7 +16,7 @@
#include "mozilla/Move.h"
#include "nsContentUtils.h"
#include "mozilla/EMEUtils.h"
#include "mozilla/Base64.h"
#include "GMPUtils.h"
#include "nsPrintfCString.h"
namespace mozilla {
@ -143,14 +143,7 @@ MediaKeySession::UpdateKeyStatusMap()
nsPrintfCString("MediaKeySession[%p,'%s'] key statuses change {",
this, NS_ConvertUTF16toUTF8(mSessionId).get()));
for (const CDMCaps::KeyStatus& status : keyStatuses) {
nsAutoCString base64KeyId;
nsDependentCSubstring rawKeyId(reinterpret_cast<const char*>(status.mId.Elements()),
status.mId.Length());
nsresult rv = Base64Encode(rawKeyId, base64KeyId);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
message.Append(nsPrintfCString(" (%s,%s)", base64KeyId.get(),
message.Append(nsPrintfCString(" (%s,%s)", ToBase64(status.mId).get(),
MediaKeyStatusValues::strings[status.mStatus].value));
}
message.Append(" }");
@ -197,17 +190,9 @@ MediaKeySession::GenerateRequest(const nsAString& aInitDataType,
}
// Convert initData to base64 for easier logging.
// Note: UpdateSession() Move()s the data out of the array, so we have
// Note: CreateSession() Move()s the data out of the array, so we have
// to copy it here.
nsAutoCString base64InitData;
if (EME_LOG_ENABLED()) {
nsDependentCSubstring rawInitData(reinterpret_cast<const char*>(data.Elements()),
data.Length());
if (NS_FAILED(Base64Encode(rawInitData, base64InitData))) {
NS_WARNING("Failed to base64 encode initData for logging");
}
}
nsAutoCString base64InitData(ToBase64(data));
PromiseId pid = mKeys->StorePromise(promise);
mKeys->GetCDMProxy()->CreateSession(Token(),
mSessionType,
@ -296,14 +281,7 @@ MediaKeySession::Update(const ArrayBufferViewOrArrayBuffer& aResponse, ErrorResu
// Convert response to base64 for easier logging.
// Note: UpdateSession() Move()s the data out of the array, so we have
// to copy it here.
nsAutoCString base64Response;
if (EME_LOG_ENABLED()) {
nsDependentCSubstring rawResponse(reinterpret_cast<const char*>(data.Elements()),
data.Length());
if (NS_FAILED(Base64Encode(rawResponse, base64Response))) {
NS_WARNING("Failed to base64 encode response for logging");
}
}
nsAutoCString base64Response(ToBase64(data));
PromiseId pid = mKeys->StorePromise(promise);
mKeys->GetCDMProxy()->UpdateSession(mSessionId,
@ -400,16 +378,10 @@ MediaKeySession::DispatchKeyMessage(MediaKeyMessageType aMessageType,
const nsTArray<uint8_t>& aMessage)
{
if (EME_LOG_ENABLED()) {
nsAutoCString base64MsgData;
nsDependentCSubstring rawMsgData(reinterpret_cast<const char*>(aMessage.Elements()),
aMessage.Length());
if (NS_FAILED(Base64Encode(rawMsgData, base64MsgData))) {
NS_WARNING("Failed to base64 encode message for logging");
}
EME_LOG("MediaKeySession[%p,'%s'] DispatchKeyMessage() type=%s message(base64)='%s'",
this, NS_ConvertUTF16toUTF8(mSessionId).get(),
MediaKeyMessageTypeValues::strings[uint32_t(aMessageType)].value,
base64MsgData.get());
ToBase64(aMessage).get());
}
nsRefPtr<MediaKeyMessageEvent> event(

View File

@ -35,13 +35,6 @@ static BOOL CALLBACK EnumDisplayMonitorsCallback(HMONITOR hMonitor, HDC hdc,
failureMsgs->push_back("FAIL GetMonitorInfoA call failed");
}
DISPLAY_DEVICEA dd;
ZeroMemory(&dd, sizeof(dd));
dd.cb = sizeof(dd);
if (!EnumDisplayDevicesA(miex.szDevice, 0, &dd, 1)) {
failureMsgs->push_back("FAIL EnumDisplayDevicesA call failed");
}
ULONG numVideoOutputs = 0;
IOPMVideoOutput** opmVideoOutputArray = nullptr;
HRESULT hr = sOPMGetVideoOutputsFromHMONITORProc(hMonitor,
@ -57,6 +50,13 @@ static BOOL CALLBACK EnumDisplayMonitorsCallback(HMONITOR hMonitor, HDC hdc,
return true;
}
DISPLAY_DEVICEA dd;
ZeroMemory(&dd, sizeof(dd));
dd.cb = sizeof(dd);
if (!EnumDisplayDevicesA(miex.szDevice, 0, &dd, 1)) {
failureMsgs->push_back("FAIL EnumDisplayDevicesA call failed");
}
for (ULONG i = 0; i < numVideoOutputs; ++i) {
OPM_RANDOM_NUMBER opmRandomNumber;
BYTE* certificate = nullptr;

View File

@ -19,6 +19,7 @@ namespace mozilla {
extern PRLogModuleInfo* GetGMPLog();
#define LOGV(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Verbose, msg)
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
@ -48,6 +49,8 @@ GMPAudioDecoderParent::InitDecode(GMPAudioCodecType aCodecType,
nsTArray<uint8_t>& aExtraData,
GMPAudioDecoderCallbackProxy* aCallback)
{
LOGD(("GMPAudioDecoderParent[%p]::InitDecode()", this));
if (mIsOpen) {
NS_WARNING("Trying to re-init an in-use GMP audio decoder!");
return NS_ERROR_FAILURE;
@ -78,6 +81,8 @@ GMPAudioDecoderParent::InitDecode(GMPAudioCodecType aCodecType,
nsresult
GMPAudioDecoderParent::Decode(GMPAudioSamplesImpl& aEncodedSamples)
{
LOGV(("GMPAudioDecoderParent[%p]::Decode() timestamp=%lld",
this, aEncodedSamples.TimeStamp()));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP Audio decoder!");
@ -100,6 +105,8 @@ GMPAudioDecoderParent::Decode(GMPAudioSamplesImpl& aEncodedSamples)
nsresult
GMPAudioDecoderParent::Reset()
{
LOGD(("GMPAudioDecoderParent[%p]::Reset()", this));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP Audio decoder!");
return NS_ERROR_FAILURE;
@ -120,6 +127,8 @@ GMPAudioDecoderParent::Reset()
nsresult
GMPAudioDecoderParent::Drain()
{
LOGD(("GMPAudioDecoderParent[%p]::Drain()", this));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP Audio decoder!");
return NS_ERROR_FAILURE;
@ -141,7 +150,7 @@ GMPAudioDecoderParent::Drain()
nsresult
GMPAudioDecoderParent::Close()
{
LOGD(("%s: %p", __FUNCTION__, this));
LOGD(("GMPAudioDecoderParent[%p]::Close()", this));
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
// Ensure if we've received a Close while waiting for a ResetComplete
@ -166,7 +175,7 @@ GMPAudioDecoderParent::Close()
nsresult
GMPAudioDecoderParent::Shutdown()
{
LOGD(("%s: %p", __FUNCTION__, this));
LOGD(("GMPAudioDecoderParent[%p]::Shutdown()", this));
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
if (mShuttingDown) {
@ -197,6 +206,8 @@ GMPAudioDecoderParent::Shutdown()
void
GMPAudioDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
{
LOGD(("GMPAudioDecoderParent[%p]::ActorDestroy(reason=%d)", this, aWhy));
mIsOpen = false;
mActorDestroyed = true;
@ -220,6 +231,9 @@ GMPAudioDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
bool
GMPAudioDecoderParent::RecvDecoded(const GMPAudioDecodedSampleData& aDecoded)
{
LOGV(("GMPAudioDecoderParent[%p]::RecvDecoded() timestamp=%lld",
this, aDecoded.mTimeStamp()));
if (!mCallback) {
return false;
}
@ -235,6 +249,8 @@ GMPAudioDecoderParent::RecvDecoded(const GMPAudioDecodedSampleData& aDecoded)
bool
GMPAudioDecoderParent::RecvInputDataExhausted()
{
LOGV(("GMPAudioDecoderParent[%p]::RecvInputDataExhausted()", this));
if (!mCallback) {
return false;
}
@ -248,6 +264,8 @@ GMPAudioDecoderParent::RecvInputDataExhausted()
bool
GMPAudioDecoderParent::RecvDrainComplete()
{
LOGD(("GMPAudioDecoderParent[%p]::RecvDrainComplete()", this));
if (!mCallback) {
return false;
}
@ -266,6 +284,8 @@ GMPAudioDecoderParent::RecvDrainComplete()
bool
GMPAudioDecoderParent::RecvResetComplete()
{
LOGD(("GMPAudioDecoderParent[%p]::RecvResetComplete()", this));
if (!mCallback) {
return false;
}
@ -284,6 +304,8 @@ GMPAudioDecoderParent::RecvResetComplete()
bool
GMPAudioDecoderParent::RecvError(const GMPErr& aError)
{
LOGD(("GMPAudioDecoderParent[%p]::RecvError(error=%d)", this, aError));
if (!mCallback) {
return false;
}
@ -302,6 +324,8 @@ GMPAudioDecoderParent::RecvError(const GMPErr& aError)
bool
GMPAudioDecoderParent::RecvShutdown()
{
LOGD(("GMPAudioDecoderParent[%p]::RecvShutdown()", this));
Shutdown();
return true;
}
@ -309,6 +333,8 @@ GMPAudioDecoderParent::RecvShutdown()
bool
GMPAudioDecoderParent::Recv__delete__()
{
LOGD(("GMPAudioDecoderParent[%p]::Recv__delete__()", this));
if (mPlugin) {
// Ignore any return code. It is OK for this to fail without killing the process.
mPlugin->AudioDecoderDestroyed(this);
@ -321,6 +347,8 @@ GMPAudioDecoderParent::Recv__delete__()
void
GMPAudioDecoderParent::UnblockResetAndDrain()
{
LOGD(("GMPAudioDecoderParent[%p]::UnblockResetAndDrain()", this));
if (!mCallback) {
MOZ_ASSERT(!mIsAwaitingResetComplete);
MOZ_ASSERT(!mIsAwaitingDrainComplete);

View File

@ -9,6 +9,17 @@
#include "mozilla/unused.h"
namespace mozilla {
#ifdef LOG
#undef LOG
#endif
extern PRLogModuleInfo* GetGMPLog();
#define LOGV(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Verbose, msg)
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
namespace gmp {
GMPDecryptorParent::GMPDecryptorParent(GMPContentParent* aPlugin)
@ -32,6 +43,8 @@ GMPDecryptorParent::~GMPDecryptorParent()
nsresult
GMPDecryptorParent::Init(GMPDecryptorProxyCallback* aCallback)
{
LOGD(("GMPDecryptorParent[%p]::Init()", this));
if (mIsOpen) {
NS_WARNING("Trying to re-use an in-use GMP decrypter!");
return NS_ERROR_FAILURE;
@ -51,6 +64,9 @@ GMPDecryptorParent::CreateSession(uint32_t aCreateSessionToken,
const nsTArray<uint8_t>& aInitData,
GMPSessionType aSessionType)
{
LOGD(("GMPDecryptorParent[%p]::CreateSession(token=%u, promiseId=%u, aInitData='%s')",
this, aCreateSessionToken, aPromiseId, ToBase64(aInitData).get()));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@ -64,6 +80,8 @@ void
GMPDecryptorParent::LoadSession(uint32_t aPromiseId,
const nsCString& aSessionId)
{
LOGD(("GMPDecryptorParent[%p]::LoadSession(sessionId='%s', promiseId=%u)",
this, aSessionId.get(), aPromiseId));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@ -78,6 +96,9 @@ GMPDecryptorParent::UpdateSession(uint32_t aPromiseId,
const nsCString& aSessionId,
const nsTArray<uint8_t>& aResponse)
{
LOGD(("GMPDecryptorParent[%p]::UpdateSession(sessionId='%s', promiseId=%u response='%s')",
this, aSessionId.get(), aPromiseId, ToBase64(aResponse).get()));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@ -91,6 +112,9 @@ void
GMPDecryptorParent::CloseSession(uint32_t aPromiseId,
const nsCString& aSessionId)
{
LOGD(("GMPDecryptorParent[%p]::CloseSession(sessionId='%s', promiseId=%u)",
this, aSessionId.get(), aPromiseId));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@ -104,6 +128,9 @@ void
GMPDecryptorParent::RemoveSession(uint32_t aPromiseId,
const nsCString& aSessionId)
{
LOGD(("GMPDecryptorParent[%p]::RemoveSession(sessionId='%s', promiseId=%u)",
this, aSessionId.get(), aPromiseId));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@ -117,6 +144,9 @@ void
GMPDecryptorParent::SetServerCertificate(uint32_t aPromiseId,
const nsTArray<uint8_t>& aServerCert)
{
LOGD(("GMPDecryptorParent[%p]::SetServerCertificate(promiseId=%u)",
this, aPromiseId));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@ -131,6 +161,8 @@ GMPDecryptorParent::Decrypt(uint32_t aId,
const CryptoSample& aCrypto,
const nsTArray<uint8_t>& aBuffer)
{
LOGV(("GMPDecryptorParent[%p]::Decrypt(id=%d)", this, aId));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@ -152,6 +184,9 @@ bool
GMPDecryptorParent::RecvSetSessionId(const uint32_t& aCreateSessionId,
const nsCString& aSessionId)
{
LOGD(("GMPDecryptorParent[%p]::RecvSetSessionId(token=%u, sessionId='%s')",
this, aCreateSessionId, aSessionId.get()));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@ -164,6 +199,9 @@ bool
GMPDecryptorParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
const bool& aSuccess)
{
LOGD(("GMPDecryptorParent[%p]::RecvResolveLoadSessionPromise(promiseId=%u)",
this, aPromiseId));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@ -175,6 +213,9 @@ GMPDecryptorParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
bool
GMPDecryptorParent::RecvResolvePromise(const uint32_t& aPromiseId)
{
LOGD(("GMPDecryptorParent[%p]::RecvResolvePromise(promiseId=%u)",
this, aPromiseId));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@ -206,6 +247,9 @@ GMPDecryptorParent::RecvRejectPromise(const uint32_t& aPromiseId,
const GMPDOMException& aException,
const nsCString& aMessage)
{
LOGD(("GMPDecryptorParent[%p]::RecvRejectPromise(promiseId=%u, exception=%d, msg='%s')",
this, aException, aMessage.get()));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@ -219,6 +263,9 @@ GMPDecryptorParent::RecvSessionMessage(const nsCString& aSessionId,
const GMPSessionMessageType& aMessageType,
nsTArray<uint8_t>&& aMessage)
{
LOGD(("GMPDecryptorParent[%p]::RecvSessionMessage(sessionId='%s', type=%d, msg='%s')",
this, aSessionId.get(), aMessageType, ToBase64(aMessage).get()));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@ -231,6 +278,9 @@ bool
GMPDecryptorParent::RecvExpirationChange(const nsCString& aSessionId,
const double& aExpiryTime)
{
LOGD(("GMPDecryptorParent[%p]::RecvExpirationChange(sessionId='%s', expiry=%lf)",
this, aSessionId.get(), aExpiryTime));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@ -242,6 +292,9 @@ GMPDecryptorParent::RecvExpirationChange(const nsCString& aSessionId,
bool
GMPDecryptorParent::RecvSessionClosed(const nsCString& aSessionId)
{
LOGD(("GMPDecryptorParent[%p]::RecvSessionClosed(sessionId='%s')",
this, aSessionId.get()));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@ -256,6 +309,10 @@ GMPDecryptorParent::RecvSessionError(const nsCString& aSessionId,
const uint32_t& aSystemCode,
const nsCString& aMessage)
{
LOGD(("GMPDecryptorParent[%p]::RecvSessionError(sessionId='%s', exception=%d, sysCode=%d, msg='%s')",
this, aSessionId.get(),
aException, aSystemCode, aMessage.get()));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@ -272,6 +329,9 @@ GMPDecryptorParent::RecvKeyStatusChanged(const nsCString& aSessionId,
InfallibleTArray<uint8_t>&& aKeyId,
const GMPMediaKeyStatus& aStatus)
{
LOGD(("GMPDecryptorParent[%p]::RecvKeyStatusChanged(sessionId='%s', keyId=%s, status=%d)",
this, aSessionId.get(), ToBase64(aKeyId).get(), aStatus));
if (mIsOpen) {
mCallback->KeyStatusChanged(aSessionId, aKeyId, aStatus);
}
@ -281,6 +341,8 @@ GMPDecryptorParent::RecvKeyStatusChanged(const nsCString& aSessionId,
bool
GMPDecryptorParent::RecvSetCaps(const uint64_t& aCaps)
{
LOGD(("GMPDecryptorParent[%p]::RecvSetCaps(caps=0x%llx)", this, aCaps));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@ -294,6 +356,9 @@ GMPDecryptorParent::RecvDecrypted(const uint32_t& aId,
const GMPErr& aErr,
InfallibleTArray<uint8_t>&& aBuffer)
{
LOGV(("GMPDecryptorParent[%p]::RecvDecrypted(id=%d, err=%d)",
this, aId, aErr));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@ -305,6 +370,8 @@ GMPDecryptorParent::RecvDecrypted(const uint32_t& aId,
bool
GMPDecryptorParent::RecvShutdown()
{
LOGD(("GMPDecryptorParent[%p]::RecvShutdown()", this));
Shutdown();
return true;
}
@ -313,7 +380,9 @@ GMPDecryptorParent::RecvShutdown()
void
GMPDecryptorParent::Close()
{
LOGD(("GMPDecryptorParent[%p]::Close()", this));
MOZ_ASSERT(mGMPThread == NS_GetCurrentThread());
// Consumer is done with us; we can shut down. No more callbacks should
// be made to mCallback. Note: do this before Shutdown()!
mCallback = nullptr;
@ -328,6 +397,7 @@ GMPDecryptorParent::Close()
void
GMPDecryptorParent::Shutdown()
{
LOGD(("GMPDecryptorParent[%p]::Shutdown()", this));
MOZ_ASSERT(mGMPThread == NS_GetCurrentThread());
if (mShuttingDown) {
@ -351,6 +421,8 @@ GMPDecryptorParent::Shutdown()
void
GMPDecryptorParent::ActorDestroy(ActorDestroyReason aWhy)
{
LOGD(("GMPDecryptorParent[%p]::ActorDestroy(reason=%d)", this, aWhy));
mIsOpen = false;
mActorDestroyed = true;
if (mCallback) {
@ -367,6 +439,8 @@ GMPDecryptorParent::ActorDestroy(ActorDestroyReason aWhy)
bool
GMPDecryptorParent::Recv__delete__()
{
LOGD(("GMPDecryptorParent[%p]::Recv__delete__()", this));
if (mPlugin) {
mPlugin->DecryptorDestroyed(this);
mPlugin = nullptr;

View File

@ -30,11 +30,6 @@ extern PRLogModuleInfo* GetGMPLog();
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
#ifdef __CLASS__
#undef __CLASS__
#endif
#define __CLASS__ "GMPStorageParent"
namespace gmp {
// We store the records in files in the profile dir.
@ -165,7 +160,7 @@ public:
{
MOZ_ASSERT(!IsOpen(aRecordName));
nsresult rv;
Record* record = nullptr;
Record* record = nullptr;
if (!mRecords.Get(aRecordName, &record)) {
// New file.
nsAutoString filename;
@ -254,7 +249,7 @@ public:
return GMPClosedErr;
}
Record* record = nullptr;
Record* record = nullptr;
mRecords.Get(aRecordName, &record);
MOZ_ASSERT(record && !!record->mFileDesc); // IsOpen() guarantees this.
@ -581,6 +576,8 @@ GMPStorageParent::GMPStorageParent(const nsCString& aNodeId,
nsresult
GMPStorageParent::Init()
{
LOGD(("GMPStorageParent[%p]::Init()", this));
if (NS_WARN_IF(mNodeId.IsEmpty())) {
return NS_ERROR_FAILURE;
}
@ -611,6 +608,9 @@ GMPStorageParent::Init()
bool
GMPStorageParent::RecvOpen(const nsCString& aRecordName)
{
LOGD(("GMPStorageParent[%p]::RecvOpen(record='%s')",
this, aRecordName.get()));
if (mShutdown) {
return false;
}
@ -618,23 +618,30 @@ GMPStorageParent::RecvOpen(const nsCString& aRecordName)
if (mNodeId.EqualsLiteral("null")) {
// Refuse to open storage if the page is opened from local disk,
// or shared across origin.
NS_WARNING("Refusing to open storage for null NodeId");
LOGD(("GMPStorageParent[%p]::RecvOpen(record='%s') failed; null nodeId",
this, aRecordName.get()));
unused << SendOpenComplete(aRecordName, GMPGenericErr);
return true;
}
if (aRecordName.IsEmpty()) {
LOGD(("GMPStorageParent[%p]::RecvOpen(record='%s') failed; record name empty",
this, aRecordName.get()));
unused << SendOpenComplete(aRecordName, GMPGenericErr);
return true;
}
if (mStorage->IsOpen(aRecordName)) {
LOGD(("GMPStorageParent[%p]::RecvOpen(record='%s') failed; record in use",
this, aRecordName.get()));
unused << SendOpenComplete(aRecordName, GMPRecordInUse);
return true;
}
auto err = mStorage->Open(aRecordName);
MOZ_ASSERT(GMP_FAILED(err) || mStorage->IsOpen(aRecordName));
LOGD(("GMPStorageParent[%p]::RecvOpen(record='%s') complete; rv=%d",
this, aRecordName.get(), err));
unused << SendOpenComplete(aRecordName, err);
return true;
@ -643,7 +650,8 @@ GMPStorageParent::RecvOpen(const nsCString& aRecordName)
bool
GMPStorageParent::RecvRead(const nsCString& aRecordName)
{
LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
LOGD(("GMPStorageParent[%p]::RecvRead(record='%s')",
this, aRecordName.get()));
if (mShutdown) {
return false;
@ -651,9 +659,14 @@ GMPStorageParent::RecvRead(const nsCString& aRecordName)
nsTArray<uint8_t> data;
if (!mStorage->IsOpen(aRecordName)) {
LOGD(("GMPStorageParent[%p]::RecvRead(record='%s') failed; record not open",
this, aRecordName.get()));
unused << SendReadComplete(aRecordName, GMPClosedErr, data);
} else {
unused << SendReadComplete(aRecordName, mStorage->Read(aRecordName, data), data);
GMPErr rv = mStorage->Read(aRecordName, data);
LOGD(("GMPStorageParent[%p]::RecvRead(record='%s') read %d bytes rv=%d",
this, aRecordName.get(), data.Length(), rv));
unused << SendReadComplete(aRecordName, rv, data);
}
return true;
@ -663,23 +676,32 @@ bool
GMPStorageParent::RecvWrite(const nsCString& aRecordName,
InfallibleTArray<uint8_t>&& aBytes)
{
LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
LOGD(("GMPStorageParent[%p]::RecvWrite(record='%s') %d bytes",
this, aRecordName.get(), aBytes.Length()));
if (mShutdown) {
return false;
}
if (!mStorage->IsOpen(aRecordName)) {
LOGD(("GMPStorageParent[%p]::RecvWrite(record='%s') failed record not open",
this, aRecordName.get()));
unused << SendWriteComplete(aRecordName, GMPClosedErr);
return true;
}
if (aBytes.Length() > GMP_MAX_RECORD_SIZE) {
LOGD(("GMPStorageParent[%p]::RecvWrite(record='%s') failed record too big",
this, aRecordName.get()));
unused << SendWriteComplete(aRecordName, GMPQuotaExceededErr);
return true;
}
unused << SendWriteComplete(aRecordName, mStorage->Write(aRecordName, aBytes));
GMPErr rv = mStorage->Write(aRecordName, aBytes);
LOGD(("GMPStorageParent[%p]::RecvWrite(record='%s') write complete rv=%d",
this, aRecordName.get(), rv));
unused << SendWriteComplete(aRecordName, rv);
return true;
}
@ -687,14 +709,16 @@ GMPStorageParent::RecvWrite(const nsCString& aRecordName,
bool
GMPStorageParent::RecvGetRecordNames()
{
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
if (mShutdown) {
return true;
}
nsTArray<nsCString> recordNames;
GMPErr status = mStorage->GetRecordNames(recordNames);
LOGD(("GMPStorageParent[%p]::RecvGetRecordNames() status=%d numRecords=%d",
this, status, recordNames.Length()));
unused << SendRecordNames(recordNames, status);
return true;
@ -703,7 +727,8 @@ GMPStorageParent::RecvGetRecordNames()
bool
GMPStorageParent::RecvClose(const nsCString& aRecordName)
{
LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
LOGD(("GMPStorageParent[%p]::RecvClose(record='%s')",
this, aRecordName.get()));
if (mShutdown) {
return true;
@ -717,14 +742,14 @@ GMPStorageParent::RecvClose(const nsCString& aRecordName)
void
GMPStorageParent::ActorDestroy(ActorDestroyReason aWhy)
{
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
LOGD(("GMPStorageParent[%p]::ActorDestroy(reason=%d)", this, aWhy));
Shutdown();
}
void
GMPStorageParent::Shutdown()
{
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
LOGD(("GMPStorageParent[%p]::Shutdown()", this));
if (mShutdown) {
return;

View File

@ -10,6 +10,7 @@
#include "nsCOMPtr.h"
#include "nsLiteralString.h"
#include "nsCRTGlue.h"
#include "mozilla/Base64.h"
namespace mozilla {
@ -50,4 +51,17 @@ SplitAt(const char* aDelims,
}
}
nsCString
ToBase64(const nsTArray<uint8_t>& aBytes)
{
nsAutoCString base64;
nsDependentCSubstring raw(reinterpret_cast<const char*>(aBytes.Elements()),
aBytes.Length());
nsresult rv = Base64Encode(raw, base64);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_LITERAL_CSTRING("[Base64EncodeFailed]");
}
return base64;
}
} // namespace mozilla

View File

@ -34,6 +34,9 @@ SplitAt(const char* aDelims,
const nsACString& aInput,
nsTArray<nsCString>& aOutTokens);
nsCString
ToBase64(const nsTArray<uint8_t>& aBytes);
} // namespace mozilla
#endif

View File

@ -23,6 +23,7 @@ namespace mozilla {
extern PRLogModuleInfo* GetGMPLog();
#define LOGV(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Verbose, msg)
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
@ -67,7 +68,7 @@ GMPVideoDecoderParent::Host()
void
GMPVideoDecoderParent::Close()
{
LOGD(("%s: %p", __FUNCTION__, this));
LOGD(("GMPVideoDecoderParent[%p]::Close()", this));
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
// Ensure if we've received a Close while waiting for a ResetComplete
@ -92,6 +93,8 @@ GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings,
GMPVideoDecoderCallbackProxy* aCallback,
int32_t aCoreCount)
{
LOGD(("GMPVideoDecoderParent[%p]::InitDecode()", this));
if (mActorDestroyed) {
NS_WARNING("Trying to use a destroyed GMP video decoder!");
return NS_ERROR_FAILURE;
@ -123,6 +126,10 @@ GMPVideoDecoderParent::Decode(GMPUniquePtr<GMPVideoEncodedFrame> aInputFrame,
const nsTArray<uint8_t>& aCodecSpecificInfo,
int64_t aRenderTimeMs)
{
LOGV(("GMPVideoDecoderParent[%p]::Decode() timestamp=%lld keyframe=%d",
this, aInputFrame->TimeStamp(),
aInputFrame->FrameType() == kGMPKeyFrame));
if (!mIsOpen) {
NS_WARNING("Trying to use an dead GMP video decoder");
return NS_ERROR_FAILURE;
@ -158,6 +165,8 @@ GMPVideoDecoderParent::Decode(GMPUniquePtr<GMPVideoEncodedFrame> aInputFrame,
nsresult
GMPVideoDecoderParent::Reset()
{
LOGD(("GMPVideoDecoderParent[%p]::Reset()", this));
if (!mIsOpen) {
NS_WARNING("Trying to use an dead GMP video decoder");
return NS_ERROR_FAILURE;
@ -178,6 +187,8 @@ GMPVideoDecoderParent::Reset()
nsresult
GMPVideoDecoderParent::Drain()
{
LOGD(("GMPVideoDecoderParent[%p]::Drain()", this));
if (!mIsOpen) {
NS_WARNING("Trying to use an dead GMP video decoder");
return NS_ERROR_FAILURE;
@ -211,7 +222,7 @@ GMPVideoDecoderParent::GetDisplayName() const
nsresult
GMPVideoDecoderParent::Shutdown()
{
LOGD(("%s: %p", __FUNCTION__, this));
LOGD(("GMPVideoDecoderParent[%p]::Shutdown()", this));
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
if (mShuttingDown) {
@ -242,6 +253,8 @@ GMPVideoDecoderParent::Shutdown()
void
GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
{
LOGD(("GMPVideoDecoderParent[%p]::ActorDestroy reason=%d", this, aWhy));
mIsOpen = false;
mActorDestroyed = true;
mVideoHost.DoneWithAPI();
@ -267,12 +280,17 @@ GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
bool
GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame)
{
LOGV(("GMPVideoDecoderParent[%p]::RecvDecoded() timestamp=%lld",
this, aDecodedFrame.mTimestamp()));
if (!mCallback) {
return false;
}
if (!GMPVideoi420FrameImpl::CheckFrameData(aDecodedFrame)) {
LOG(LogLevel::Error, ("%s: Decoded frame corrupt, ignoring", __FUNCTION__));
LOG(LogLevel::Error,
("GMPVideoDecoderParent[%p]::RecvDecoded() "
"timestamp=%lld decoded frame corrupt, ignoring"));
return false;
}
auto f = new GMPVideoi420FrameImpl(aDecodedFrame, &mVideoHost);
@ -312,6 +330,8 @@ GMPVideoDecoderParent::RecvReceivedDecodedFrame(const uint64_t& aPictureId)
bool
GMPVideoDecoderParent::RecvInputDataExhausted()
{
LOGV(("GMPVideoDecoderParent[%p]::RecvInputDataExhausted()", this));
if (!mCallback) {
return false;
}
@ -325,6 +345,8 @@ GMPVideoDecoderParent::RecvInputDataExhausted()
bool
GMPVideoDecoderParent::RecvDrainComplete()
{
LOGD(("GMPVideoDecoderParent[%p]::RecvDrainComplete()", this));
if (!mCallback) {
return false;
}
@ -343,6 +365,8 @@ GMPVideoDecoderParent::RecvDrainComplete()
bool
GMPVideoDecoderParent::RecvResetComplete()
{
LOGD(("GMPVideoDecoderParent[%p]::RecvResetComplete()", this));
if (!mCallback) {
return false;
}
@ -361,6 +385,8 @@ GMPVideoDecoderParent::RecvResetComplete()
bool
GMPVideoDecoderParent::RecvError(const GMPErr& aError)
{
LOGD(("GMPVideoDecoderParent[%p]::RecvError(error=%d)", this, aError));
if (!mCallback) {
return false;
}
@ -379,6 +405,8 @@ GMPVideoDecoderParent::RecvError(const GMPErr& aError)
bool
GMPVideoDecoderParent::RecvShutdown()
{
LOGD(("GMPVideoDecoderParent[%p]::RecvShutdown()", this));
Shutdown();
return true;
}
@ -415,6 +443,8 @@ GMPVideoDecoderParent::AnswerNeedShmem(const uint32_t& aFrameBufferSize,
bool
GMPVideoDecoderParent::Recv__delete__()
{
LOGD(("GMPVideoDecoderParent[%p]::Recv__delete__()", this));
if (mPlugin) {
// Ignore any return code. It is OK for this to fail without killing the process.
mPlugin->VideoDecoderDestroyed(this);
@ -427,6 +457,8 @@ GMPVideoDecoderParent::Recv__delete__()
void
GMPVideoDecoderParent::UnblockResetAndDrain()
{
LOGD(("GMPVideoDecoderParent[%p]::UnblockResetAndDrain()", this));
if (!mCallback) {
MOZ_ASSERT(!mIsAwaitingResetComplete);
MOZ_ASSERT(!mIsAwaitingDrainComplete);

View File

@ -308,14 +308,10 @@ MediaSource::EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, Er
}
switch (aError.Value()) {
case MediaSourceEndOfStreamError::Network:
// TODO: If media element has a readyState of:
// HAVE_NOTHING -> run resource fetch algorithm
// > HAVE_NOTHING -> run "interrupted" steps of resource fetch
mDecoder->NetworkError();
break;
case MediaSourceEndOfStreamError::Decode:
// TODO: If media element has a readyState of:
// HAVE_NOTHING -> run "unsupported" steps of resource fetch
// > HAVE_NOTHING -> run "corrupted" steps of resource fetch
mDecoder->DecodeError();
break;
default:
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);

View File

@ -550,6 +550,15 @@ SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult&
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
// If the HTMLMediaElement.error attribute is not null, then throw an
// InvalidStateError exception and abort these steps.
if (!mMediaSource->GetDecoder() ||
mMediaSource->GetDecoder()->IsEndedOrShutdown()) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
mMediaSource->SetReadyState(MediaSourceReadyState::Open);
}

View File

@ -978,6 +978,13 @@ TrackBuffersManager::OnDemuxerInitDone(nsresult)
// 6. Set first initialization segment received flag to true.
mFirstInitializationSegmentReceived = true;
} else {
// Check that audio configuration hasn't changed as this is something
// we do not support yet (bug 1185827).
if (mAudioTracks.mNumTracks &&
(info.mAudio.mChannels != mAudioTracks.mInfo->GetAsAudioInfo()->mChannels ||
info.mAudio.mRate != mAudioTracks.mInfo->GetAsAudioInfo()->mRate)) {
RejectAppend(NS_ERROR_FAILURE, __func__);
}
mAudioTracks.mLastInfo = new SharedTrackInfo(info.mAudio, streamID);
mVideoTracks.mLastInfo = new SharedTrackInfo(info.mVideo, streamID);
}

View File

@ -393,6 +393,10 @@ nsresult MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface)
#define HANDLE_DECODER_ERROR() \
if (NS_FAILED(res)) { \
NS_WARNING("exiting decoder loop due to exception"); \
if (mDraining) { \
ENVOKE_CALLBACK(DrainComplete); \
mDraining = false; \
} \
ENVOKE_CALLBACK(Error); \
break; \
}

View File

@ -55,8 +55,8 @@ bool nsMixedContentBlocker::sBlockMixedDisplay = false;
class nsMixedContentEvent : public nsRunnable
{
public:
nsMixedContentEvent(nsISupports *aContext, MixedContentTypes aType)
: mContext(aContext), mType(aType)
nsMixedContentEvent(nsISupports *aContext, MixedContentTypes aType, bool aRootHasSecureConnection)
: mContext(aContext), mType(aType), mRootHasSecureConnection(aRootHasSecureConnection)
{}
NS_IMETHOD Run()
@ -85,6 +85,21 @@ public:
nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot);
NS_ASSERTION(rootDoc, "No root document from document shell root tree item.");
// Get eventSink and the current security state from the docShell
nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
NS_ASSERTION(eventSink, "No eventSink from docShell.");
nsCOMPtr<nsIDocShell> rootShell = do_GetInterface(sameTypeRoot);
NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
uint32_t state = nsIWebProgressListener::STATE_IS_BROKEN;
nsCOMPtr<nsISecureBrowserUI> securityUI;
rootShell->GetSecurityUI(getter_AddRefs(securityUI));
// If there is no securityUI, document doesn't have a security state to
// update. But we still want to set the document flags, so we don't return
// early.
nsresult stateRV;
if (securityUI) {
stateRV = securityUI->GetState(&state);
}
if (mType == eMixedScript) {
// See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
@ -94,18 +109,26 @@ public:
rootDoc->SetHasMixedActiveContentLoaded(true);
// Update the security UI in the tab with the allowed mixed active content
nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
if (eventSink) {
// If mixed display content is loaded, make sure to include that in the state.
if (rootDoc->GetHasMixedDisplayContentLoaded()) {
eventSink->OnSecurityChange(mContext,
(nsIWebProgressListener::STATE_IS_BROKEN |
nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
if (securityUI) {
// Bug 1182551 - before changing the security state to broken, check
// that the root is actually secure.
if (mRootHasSecureConnection) {
// If mixed display content is loaded, make sure to include that in the state.
if (rootDoc->GetHasMixedDisplayContentLoaded()) {
eventSink->OnSecurityChange(mContext,
(nsIWebProgressListener::STATE_IS_BROKEN |
nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
} else {
eventSink->OnSecurityChange(mContext,
(nsIWebProgressListener::STATE_IS_BROKEN |
nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
}
} else {
eventSink->OnSecurityChange(mContext,
(nsIWebProgressListener::STATE_IS_BROKEN |
nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
// root not secure, mixed active content loaded in an https subframe
if (NS_SUCCEEDED(stateRV)) {
eventSink->OnSecurityChange(mContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
}
}
}
@ -117,17 +140,25 @@ public:
rootDoc->SetHasMixedDisplayContentLoaded(true);
// Update the security UI in the tab with the allowed mixed display content.
nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
if (eventSink) {
if (securityUI) {
// Bug 1182551 - before changing the security state to broken, check
// that the root is actually secure.
if (mRootHasSecureConnection) {
// If mixed active content is loaded, make sure to include that in the state.
if (rootDoc->GetHasMixedActiveContentLoaded()) {
eventSink->OnSecurityChange(mContext,
(nsIWebProgressListener::STATE_IS_BROKEN |
nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
if (rootDoc->GetHasMixedActiveContentLoaded()) {
eventSink->OnSecurityChange(mContext,
(nsIWebProgressListener::STATE_IS_BROKEN |
nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
} else {
eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
}
} else {
eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
// root not secure, mixed display content loaded in an https subframe
if (NS_SUCCEEDED(stateRV)) {
eventSink->OnSecurityChange(mContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
}
}
}
}
@ -141,6 +172,9 @@ private:
// The type of mixed content detected, e.g. active or display
const MixedContentTypes mType;
// Indicates whether the top level load is https or not.
bool mRootHasSecureConnection;
};
@ -673,7 +707,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
NS_ASSERTION(eventSink, "No eventSink from docShell.");
nsCOMPtr<nsIDocShell> rootShell = do_GetInterface(sameTypeRoot);
NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
uint32_t State = nsIWebProgressListener::STATE_IS_BROKEN;
uint32_t state = nsIWebProgressListener::STATE_IS_BROKEN;
nsCOMPtr<nsISecureBrowserUI> securityUI;
rootShell->GetSecurityUI(getter_AddRefs(securityUI));
// If there is no securityUI, document doesn't have a security state.
@ -682,7 +716,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
*aDecision = nsIContentPolicy::ACCEPT;
return NS_OK;
}
nsresult stateRV = securityUI->GetState(&State);
nsresult stateRV = securityUI->GetState(&state);
// If the content is display content, and the pref says display content should be blocked, block it.
if (sBlockMixedDisplay && classification == eMixedDisplay) {
@ -712,7 +746,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
// User has overriden the pref and the root is not https;
// mixed display content was allowed on an https subframe.
if (NS_SUCCEEDED(stateRV)) {
eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
}
}
} else {
@ -720,7 +754,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) {
rootDoc->SetHasMixedDisplayContentBlocked(true);
eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT));
eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT));
}
}
return NS_OK;
@ -755,7 +789,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
// User has already overriden the pref and the root is not https;
// mixed active content was allowed on an https subframe.
if (NS_SUCCEEDED(stateRV)) {
eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
}
return NS_OK;
}
@ -772,7 +806,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
// The user has not overriden the pref, so make sure they still have an option by calling eventSink
// which will invoke the doorhanger
if (NS_SUCCEEDED(stateRV)) {
eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT));
eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT));
}
return NS_OK;
}
@ -786,7 +820,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
// Fire the event from a script runner as it is unsafe to run script
// from within ShouldLoad
nsContentUtils::AddScriptRunner(
new nsMixedContentEvent(aRequestingContext, classification));
new nsMixedContentEvent(aRequestingContext, classification, rootHasSecureConnection));
*aDecision = ACCEPT;
return NS_OK;
}

View File

@ -1,4 +1,5 @@
[DEFAULT]
tags = mcb
support-files =
file_bug803225_test_mailto.html
file_frameNavigation.html

View File

@ -52,11 +52,6 @@ ServiceWorker::ServiceWorker(nsPIDOMWindow* aWindow,
MOZ_ASSERT(aInfo);
MOZ_ASSERT(mSharedWorker);
if (aWindow) {
mDocument = aWindow->GetExtantDoc();
mWindow = aWindow->GetOuterWindow();
}
// This will update our state too.
mInfo->AppendWorker(this);
}
@ -74,7 +69,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorker)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorker, DOMEventTargetHelper,
mSharedWorker, mDocument, mWindow)
mSharedWorker)
JSObject*
ServiceWorker::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
@ -95,20 +90,22 @@ ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetWorkerPrivate();
MOZ_ASSERT(workerPrivate);
if (State() == ServiceWorkerState::Redundant) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
MOZ_ASSERT(mDocument && mWindow,
"Cannot call PostMessage on a ServiceWorker object that doesn't "
"have a window");
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetParentObject());
if (!window || !window->GetExtantDoc()) {
NS_WARNING("Trying to call post message from an invalid dom object.");
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsAutoPtr<ServiceWorkerClientInfo> clientInfo(
new ServiceWorkerClientInfo(mDocument, mWindow));
WorkerPrivate* workerPrivate = GetWorkerPrivate();
MOZ_ASSERT(workerPrivate);
nsAutoPtr<ServiceWorkerClientInfo> clientInfo(new ServiceWorkerClientInfo(window->GetExtantDoc()));
workerPrivate->PostMessageToServiceWorker(aCx, aMessage, aTransferable,
clientInfo, aRv);

View File

@ -11,7 +11,6 @@
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState.
class nsIDocument;
class nsPIDOMWindow;
namespace mozilla {
@ -92,10 +91,6 @@ private:
// can be released and recreated as required rather than re-implement some of
// the SharedWorker logic.
nsRefPtr<SharedWorker> mSharedWorker;
// We need to keep the document and window alive for PostMessage to be able
// to access them.
nsCOMPtr<nsIDocument> mDocument;
nsCOMPtr<nsPIDOMWindow> mWindow;
};
} // namespace workers

View File

@ -29,8 +29,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClient)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc,
nsPIDOMWindow* aWindow)
ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc)
: mWindowId(0)
{
MOZ_ASSERT(aDoc);
@ -55,7 +54,7 @@ ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc,
NS_WARNING("Failed to get focus information.");
}
nsRefPtr<nsGlobalWindow> outerWindow = static_cast<nsGlobalWindow*>(aWindow);
nsRefPtr<nsGlobalWindow> outerWindow = static_cast<nsGlobalWindow*>(aDoc->GetWindow());
MOZ_ASSERT(outerWindow);
if (!outerWindow->IsTopLevelWindow()) {
mFrameType = FrameType::Nested;

View File

@ -15,7 +15,6 @@
#include "mozilla/dom/ClientBinding.h"
class nsIDocument;
class nsPIDOMWindow;
namespace mozilla {
namespace dom {
@ -32,7 +31,7 @@ class ServiceWorkerClientInfo final
friend class ServiceWorkerWindowClient;
public:
ServiceWorkerClientInfo(nsIDocument* aDoc, nsPIDOMWindow* aWindow);
explicit ServiceWorkerClientInfo(nsIDocument* aDoc);
private:
nsString mClientId;

View File

@ -3896,7 +3896,7 @@ ServiceWorkerManager::DispatchFetchEvent(const OriginAttributes& aOriginAttribut
MOZ_ASSERT(aDoc);
aRv = GetDocumentController(aDoc->GetInnerWindow(), failRunnable,
getter_AddRefs(serviceWorker));
clientInfo = new ServiceWorkerClientInfo(aDoc, aDoc->GetWindow());
clientInfo = new ServiceWorkerClientInfo(aDoc);
} else {
nsCOMPtr<nsIChannel> internalChannel;
aRv = aChannel->GetChannel(getter_AddRefs(internalChannel));
@ -4276,7 +4276,7 @@ EnumControlledDocuments(nsISupports* aKey,
return PL_DHASH_NEXT;
}
ServiceWorkerClientInfo clientInfo(document, document->GetWindow());
ServiceWorkerClientInfo clientInfo(document);
data->mDocuments.AppendElement(clientInfo);
return PL_DHASH_NEXT;

View File

@ -90,9 +90,14 @@ public:
UniquePtr<ServiceWorkerClientInfo> clientInfo;
if (window) {
nsContentUtils::DispatchChromeEvent(window->GetExtantDoc(), window->GetOuterWindow(), NS_LITERAL_STRING("DOMServiceWorkerFocusClient"), true, true);
clientInfo.reset(new ServiceWorkerClientInfo(window->GetDocument(),
window->GetOuterWindow()));
nsCOMPtr<nsIDocument> doc = window->GetDocument();
if (doc) {
nsContentUtils::DispatchChromeEvent(doc,
window->GetOuterWindow(),
NS_LITERAL_STRING("DOMServiceWorkerFocusClient"),
true, true);
clientInfo.reset(new ServiceWorkerClientInfo(doc));
}
}
DispatchResult(Move(clientInfo));

View File

@ -901,7 +901,8 @@ nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream,
for (; interfaceCount > 0; interfaceCount--) {
nsIID iid;
aStream->ReadID(&iid);
rv = aStream->ReadID(&iid);
NS_ENSURE_SUCCESS(rv, rv);
mInterfaceTable.Put(iid, mBinding);
}

View File

@ -240,9 +240,6 @@ NS_NewXULDocument(nsIXULDocument** result)
return NS_ERROR_NULL_POINTER;
XULDocument* doc = new XULDocument();
if (! doc)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(doc);
nsresult rv;
@ -449,8 +446,6 @@ XULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
// the interesting work happens below XULDocument::EndLoad, from
// the call there to mCurrentPrototype->NotifyLoadDone().
*aDocListener = new CachedChromeStreamListener(this, loaded);
if (! *aDocListener)
return NS_ERROR_OUT_OF_MEMORY;
}
else {
bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
@ -1635,9 +1630,6 @@ XULDocument::AddElementToDocumentPre(Element* aElement)
// later.
if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) {
BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement);
if (! hookup)
return NS_ERROR_OUT_OF_MEMORY;
rv = AddForwardReference(hookup);
if (NS_FAILED(rv)) return rv;
}
@ -1668,9 +1660,6 @@ XULDocument::AddElementToDocumentPost(Element* aElement)
}
else {
TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement);
if (! hookup)
return NS_ERROR_OUT_OF_MEMORY;
rv = AddForwardReference(hookup);
if (NS_FAILED(rv))
return rv;
@ -1875,7 +1864,6 @@ XULDocument::Init()
// Create our command dispatcher and hook it up.
mCommandDispatcher = new nsXULCommandDispatcher(this);
NS_ENSURE_TRUE(mCommandDispatcher, NS_ERROR_OUT_OF_MEMORY);
if (gRefCnt++ == 0) {
// ensure that the XUL prototype cache is instantiated successfully,
@ -2007,7 +1995,6 @@ XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
// Create a XUL content sink, a parser, and kick off a load for
// the overlay.
nsRefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl();
if (!sink) return NS_ERROR_OUT_OF_MEMORY;
rv = sink->Init(this, mCurrentPrototype);
NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
@ -2195,9 +2182,6 @@ XULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype,
nsIContent* aElement)
{
Entry* entry = new Entry;
if (! entry)
return NS_ERROR_OUT_OF_MEMORY;
entry->mPrototype = aPrototype;
entry->mElement = aElement;
NS_IF_ADDREF(entry->mElement);
@ -2652,9 +2636,6 @@ XULDocument::LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic,
// and will let us recover from a missing overlay.
ParserObserver* parserObserver =
new ParserObserver(this, mCurrentPrototype);
if (! parserObserver)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(parserObserver);
parser->Parse(aURI, parserObserver);
NS_RELEASE(parserObserver);
@ -3632,8 +3613,6 @@ XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
OverlayForwardReference* fwdref =
new OverlayForwardReference(this, element);
if (! fwdref)
return NS_ERROR_OUT_OF_MEMORY;
// transferring ownership to ya...
rv = AddForwardReference(fwdref);

View File

@ -310,11 +310,7 @@ nsXULCommandDispatcher::AddCommandUpdater(nsIDOMElement* aElement,
#endif
// If we get here, this is a new updater. Append it to the list.
updater = new Updater(aElement, aEvents, aTargets);
if (! updater)
return NS_ERROR_OUT_OF_MEMORY;
*link = updater;
*link = new Updater(aElement, aEvents, aTargets);
return NS_OK;
}

View File

@ -52,9 +52,6 @@ NS_NewXULControllers(nsISupports* aOuter, REFNSIID aIID, void** aResult)
return NS_ERROR_NO_AGGREGATION;
nsXULControllers* controllers = new nsXULControllers();
if (! controllers)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv;
NS_ADDREF(controllers);
rv = controllers->QueryInterface(aIID, aResult);
@ -121,7 +118,6 @@ NS_IMETHODIMP
nsXULControllers::InsertControllerAt(uint32_t aIndex, nsIController *controller)
{
nsXULControllerData* controllerData = new nsXULControllerData(++mCurControllerID, controller);
if (!controllerData) return NS_ERROR_OUT_OF_MEMORY;
#ifdef DEBUG
nsXULControllerData** inserted =
#endif
@ -165,7 +161,6 @@ nsXULControllers::AppendController(nsIController *controller)
{
// This assigns controller IDs starting at 1 so we can use 0 to test if an ID was obtained
nsXULControllerData* controllerData = new nsXULControllerData(++mCurControllerID, controller);
if (!controllerData) return NS_ERROR_OUT_OF_MEMORY;
#ifdef DEBUG
nsXULControllerData** appended =

View File

@ -2278,16 +2278,15 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
// Read Node Info
uint32_t number = 0;
nsresult rv = aStream->Read32(&number);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
mNodeInfo = aNodeInfos->SafeElementAt(number, nullptr);
if (!mNodeInfo) {
return NS_ERROR_UNEXPECTED;
}
// Read Attributes
nsresult tmp = aStream->Read32(&number);
if (NS_FAILED(tmp)) {
rv = tmp;
}
rv = aStream->Read32(&number);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
mNumAttributes = int32_t(number);
if (mNumAttributes > 0) {
@ -2298,10 +2297,8 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
nsAutoString attributeValue;
for (uint32_t i = 0; i < mNumAttributes; ++i) {
tmp = aStream->Read32(&number);
if (NS_FAILED(tmp)) {
rv = tmp;
}
rv = aStream->Read32(&number);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
mozilla::dom::NodeInfo* ni = aNodeInfos->SafeElementAt(number, nullptr);
if (!ni) {
return NS_ERROR_UNEXPECTED;
@ -2309,31 +2306,25 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
mAttributes[i].mName.SetTo(ni);
tmp = aStream->ReadString(attributeValue);
if (NS_FAILED(tmp)) {
rv = tmp;
}
tmp = SetAttrAt(i, attributeValue, aDocumentURI);
if (NS_FAILED(tmp)) {
rv = tmp;
}
rv = aStream->ReadString(attributeValue);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
rv = SetAttrAt(i, attributeValue, aDocumentURI);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
}
}
tmp = aStream->Read32(&number);
if (NS_FAILED(tmp)) {
rv = tmp;
}
rv = aStream->Read32(&number);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
uint32_t numChildren = int32_t(number);
if (numChildren > 0) {
mChildren.SetCapacity(numChildren);
if (!mChildren.SetCapacity(numChildren, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
for (uint32_t i = 0; i < numChildren; i++) {
tmp = aStream->Read32(&number);
if (NS_FAILED(tmp)) {
rv = tmp;
}
rv = aStream->Read32(&number);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
Type childType = (Type)number;
nsRefPtr<nsXULPrototypeNode> child;
@ -2341,64 +2332,43 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
switch (childType) {
case eType_Element:
child = new nsXULPrototypeElement();
child->mType = childType;
tmp = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
aNodeInfos);
if (NS_FAILED(tmp)) {
rv = tmp;
}
rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
aNodeInfos);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
break;
case eType_Text:
child = new nsXULPrototypeText();
child->mType = childType;
tmp = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
aNodeInfos);
if (NS_FAILED(tmp)) {
rv = tmp;
}
rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
aNodeInfos);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
break;
case eType_PI:
child = new nsXULPrototypePI();
child->mType = childType;
tmp = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
aNodeInfos);
if (NS_FAILED(tmp)) {
rv = tmp;
}
rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
aNodeInfos);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
break;
case eType_Script: {
// language version/options obtained during deserialization.
nsXULPrototypeScript* script = new nsXULPrototypeScript(0, 0);
child = script;
child->mType = childType;
nsRefPtr<nsXULPrototypeScript> script = new nsXULPrototypeScript(0, 0);
tmp = aStream->ReadBoolean(&script->mOutOfLine);
if (NS_FAILED(tmp)) {
rv = tmp;
}
if (! script->mOutOfLine) {
tmp = script->Deserialize(aStream, aProtoDoc, aDocumentURI,
aNodeInfos);
if (NS_FAILED(tmp)) {
rv = tmp;
}
rv = aStream->ReadBoolean(&script->mOutOfLine);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
if (!script->mOutOfLine) {
rv = script->Deserialize(aStream, aProtoDoc, aDocumentURI,
aNodeInfos);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
} else {
nsCOMPtr<nsISupports> supports;
tmp = aStream->ReadObject(true, getter_AddRefs(supports));
rv = aStream->ReadObject(true, getter_AddRefs(supports));
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
script->mSrcURI = do_QueryInterface(supports);
if (NS_FAILED(tmp)) {
rv = tmp;
}
tmp = script->DeserializeOutOfLine(aStream, aProtoDoc);
if (NS_FAILED(tmp)) {
rv = tmp;
}
rv = script->DeserializeOutOfLine(aStream, aProtoDoc);
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
}
// If we failed to deserialize, consider deleting 'script'?
child = script.forget();
break;
}
default:
@ -2407,6 +2377,7 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
}
MOZ_ASSERT(child, "Don't append null to mChildren");
MOZ_ASSERT(child->mType == childType);
mChildren.AppendElement(child);
// Oh dear. Something failed during the deserialization.
@ -2417,7 +2388,7 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
// death. So, let's just fail now, and propagate that failure
// upward so that the ChromeProtocolHandler knows it can't use
// a cached chrome channel for this.
if (NS_FAILED(rv))
if (NS_WARN_IF(NS_FAILED(rv)))
return rv;
}
}
@ -2617,13 +2588,16 @@ nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
nsIURI* aDocumentURI,
const nsTArray<nsRefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
{
nsresult rv;
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr ||
!mScriptObject,
"prototype script not well-initialized when deserializing?!");
// Read basic prototype data
aStream->Read32(&mLineNo);
aStream->Read32(&mLangVersion);
rv = aStream->Read32(&mLineNo);
if (NS_FAILED(rv)) return rv;
rv = aStream->Read32(&mLangVersion);
if (NS_FAILED(rv)) return rv;
AutoSafeJSContext cx;
JS::Rooted<JSObject*> global(cx, xpc::CompilationScope());
@ -2631,8 +2605,8 @@ nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
JSAutoCompartment ac(cx, global);
JS::Rooted<JSScript*> newScriptObject(cx);
nsresult rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx,
newScriptObject.address());
rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx,
newScriptObject.address());
NS_ENSURE_SUCCESS(rv, rv);
Set(newScriptObject);
return NS_OK;
@ -2862,11 +2836,11 @@ nsXULPrototypeText::Deserialize(nsIObjectInputStream* aStream,
nsIURI* aDocumentURI,
const nsTArray<nsRefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
{
nsresult rv;
rv = aStream->ReadString(mValue);
return rv;
nsresult rv = aStream->ReadString(mValue);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
//----------------------------------------------------------------------
@ -2905,10 +2879,9 @@ nsXULPrototypePI::Deserialize(nsIObjectInputStream* aStream,
nsresult rv;
rv = aStream->ReadString(mTarget);
nsresult tmp = aStream->ReadString(mData);
if (NS_FAILED(tmp)) {
rv = tmp;
}
if (NS_FAILED(rv)) return rv;
rv = aStream->ReadString(mData);
if (NS_FAILED(rv)) return rv;
return rv;
}

View File

@ -54,8 +54,6 @@ nsresult
nsXULPrototypeDocument::Init()
{
mNodeInfoManager = new nsNodeInfoManager();
NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_OUT_OF_MEMORY);
return mNodeInfoManager->Init(nullptr);
}
@ -149,8 +147,6 @@ nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream)
mNodeInfoManager->SetDocumentPrincipal(principal);
mRoot = new nsXULPrototypeElement();
if (! mRoot)
return NS_ERROR_OUT_OF_MEMORY;
// mozilla::dom::NodeInfo table
nsTArray<nsRefPtr<mozilla::dom::NodeInfo>> nodeInfos;
@ -208,10 +204,6 @@ nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream)
if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_PI) {
nsRefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
if (! pi) {
rv = NS_ERROR_OUT_OF_MEMORY;
break;
}
tmp = pi->Deserialize(aStream, this, mURI, &nodeInfos);
if (NS_FAILED(tmp)) {

View File

@ -163,24 +163,34 @@ NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetContentDisposition(nsAString& aCD)
{
nsCOMPtr<nsIDOMWindow> window = mDocument->GetDefaultView();
NS_ENSURE_STATE(window);
if (NS_WARN_IF(!window)) {
aCD.SetIsVoid(true);
return NS_OK;
}
nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
NS_ENSURE_STATE(utils);
return utils->GetDocumentMetadata(
if (NS_WARN_IF(!utils)) {
aCD.SetIsVoid(true);
return NS_OK;
}
nsresult rv = utils->GetDocumentMetadata(
NS_LITERAL_STRING("content-disposition"), aCD);
if (NS_WARN_IF(NS_FAILED(rv))) {
aCD.SetIsVoid(true);
}
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetCacheKey(uint32_t* aKey)
{
nsCOMPtr<nsISHEntry> history;
nsresult rv = GetHistory(getter_AddRefs(history));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(history);
nsCOMPtr<nsISHEntry> history = GetHistory();
if (!history) {
*aKey = 0;
return NS_OK;
}
nsCOMPtr<nsISupports> abstractKey;
rv = history->GetCacheKey(getter_AddRefs(abstractKey));
NS_ENSURE_SUCCESS(rv, rv);
if (!abstractKey) {
nsresult rv = history->GetCacheKey(getter_AddRefs(abstractKey));
if (NS_WARN_IF(NS_FAILED(rv)) || !abstractKey) {
*aKey = 0;
return NS_OK;
}
@ -195,29 +205,37 @@ WebBrowserPersistLocalDocument::GetCacheKey(uint32_t* aKey)
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetPostData(nsIInputStream** aStream)
{
nsCOMPtr<nsISHEntry> history;
nsresult rv = GetHistory(getter_AddRefs(history));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(history);
nsCOMPtr<nsISHEntry> history = GetHistory();
if (!history) {
*aStream = nullptr;
return NS_OK;
}
return history->GetPostData(aStream);
}
nsresult
WebBrowserPersistLocalDocument::GetHistory(nsISHEntry** aHistory)
already_AddRefed<nsISHEntry>
WebBrowserPersistLocalDocument::GetHistory()
{
nsCOMPtr<nsIDOMWindow> window = mDocument->GetDefaultView();
NS_ENSURE_STATE(window);
if (NS_WARN_IF(!window)) {
return nullptr;
}
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
NS_ENSURE_STATE(webNav);
if (NS_WARN_IF(!webNav)) {
return nullptr;
}
nsCOMPtr<nsIWebPageDescriptor> desc = do_QueryInterface(webNav);
NS_ENSURE_STATE(desc);
if (NS_WARN_IF(!desc)) {
return nullptr;
}
nsCOMPtr<nsISupports> curDesc;
nsresult rv = desc->GetCurrentDescriptor(getter_AddRefs(curDesc));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(curDesc);
// This can fail if, e.g., the document is a Print Preview.
if (NS_FAILED(rv) || NS_WARN_IF(!curDesc)) {
return nullptr;
}
nsCOMPtr<nsISHEntry> history = do_QueryInterface(curDesc);
history.forget(aHistory);
return NS_OK;
return history.forget();
}
const nsCString&
@ -328,7 +346,10 @@ ResourceReader::OnWalkSubframe(nsIDOMNode* aNode)
NS_ENSURE_STATE(loader);
++mOutstandingDocuments;
nsresult rv = loader->StartPersistence(this);
// Pass in 0 as the outer window ID so that we start
// persisting the root of this subframe, and not some other
// subframe child of this subframe.
nsresult rv = loader->StartPersistence(0, this);
if (NS_FAILED(rv)) {
if (rv == NS_ERROR_NO_CONTENT) {
// Just ignore frames with no content document.

View File

@ -41,7 +41,7 @@ private:
nsresult GetDocEncoder(const nsACString& aContentType,
uint32_t aEncoderFlags,
nsIDocumentEncoder** aEncoder);
nsresult GetHistory(nsISHEntry** aHistory);
already_AddRefed<nsISHEntry> GetHistory();
virtual ~WebBrowserPersistLocalDocument();
};

View File

@ -37,7 +37,11 @@ WebBrowserPersistResourcesChild::VisitDocument(nsIWebBrowserPersistDocument* aDo
{
auto* subActor = new WebBrowserPersistDocumentChild();
dom::PBrowserChild* grandManager = Manager()->Manager();
if (!grandManager->SendPWebBrowserPersistDocumentConstructor(subActor)) {
// As a consequence of how PWebBrowserPersistDocumentConstructor can be
// sent by both the parent and the child, we must pass the outerWindowID
// argument here. We pass 0, though note that this argument is actually
// just ignored when passed up to the parent from the child.
if (!grandManager->SendPWebBrowserPersistDocumentConstructor(subActor, 0)) {
// NOTE: subActor is freed at this point.
return NS_ERROR_FAILURE;
}

View File

@ -22,9 +22,20 @@ interface nsIWebBrowserPersistDocumentReceiver;
* @see nsIWebBrowserPersistDocumentReceiver
* @see nsIWebBrowserPersistDocument
* @see nsIWebBrowserPersist
*
* @param aOuterWindowID
* The outer window ID of the subframe we'd like to persist.
* If set at 0, nsIWebBrowserPersistable will attempt to persist
* the top-level document. If the outer window ID is for a subframe
* that does not exist, or is not held beneath the nsIWebBrowserPersistable,
* aRecv's onError method will be called with NS_ERROR_NO_CONTENT.
* @param aRecv
* The nsIWebBrowserPersistDocumentReceiver is a callback that
* will be fired once the document is ready for persisting.
*/
[scriptable, function, uuid(24d0dc9e-b970-4cca-898f-cbba03abaa73)]
[scriptable, uuid(f4c3fa8e-83e9-49f8-ac6f-951fc7541fe4)]
interface nsIWebBrowserPersistable : nsISupports
{
void startPersistence(in nsIWebBrowserPersistDocumentReceiver aRecv);
void startPersistence(in unsigned long long aOuterWindowID,
in nsIWebBrowserPersistDocumentReceiver aRecv);
};

View File

@ -37,6 +37,7 @@ EXPORTS.mozilla.gfx += [
'ScaleFactor.h',
'ScaleFactors2D.h',
'SourceSurfaceCairo.h',
'StackArray.h',
'Tools.h',
'Types.h',
'UserData.h',

View File

@ -281,6 +281,9 @@ ImageHost::Composite(LayerComposite* aLayer,
TimedImage* img = &mImages[imageIndex];
// Make sure the front buffer has a compositor
img->mFrontBuffer->SetCompositor(GetCompositor());
if (img->mTextureSource) {
img->mTextureSource->SetCompositor(GetCompositor());
}
{
AutoLockCompositableHost autoLock(this);

View File

@ -18,6 +18,7 @@
#include "gfxPrefs.h"
#include "gfxCrashReporterUtils.h"
#include "gfxVR.h"
#include "mozilla/gfx/StackArray.h"
#include "mozilla/EnumeratedArray.h"
@ -1091,23 +1092,20 @@ CompositorD3D11::EndFrame()
DXGI_PRESENT_PARAMETERS params;
PodZero(&params);
params.DirtyRectsCount = mInvalidRegion.GetNumRects();
std::vector<RECT> rects;
rects.reserve(params.DirtyRectsCount);
StackArray<RECT, 4> rects(params.DirtyRectsCount);
nsIntRegionRectIterator iter(mInvalidRegion);
const IntRect* r;
uint32_t i = 0;
while ((r = iter.Next()) != nullptr) {
RECT rect;
rect.left = r->x;
rect.top = r->y;
rect.bottom = r->YMost();
rect.right = r->XMost();
rects.push_back(rect);
rects[i].left = r->x;
rects[i].top = r->y;
rects[i].bottom = r->YMost();
rects[i].right = r->XMost();
i++;
}
params.pDirtyRects = &rects.front();
params.pDirtyRects = rects.data();
chain->Present1(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0, &params);
} else {
mSwapChain->Present(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);

View File

@ -136,6 +136,9 @@ void
MacIOSurfaceTextureSourceOGL::SetCompositor(Compositor* aCompositor)
{
mCompositor = static_cast<CompositorOGL*>(aCompositor);
if (mNextSibling) {
mNextSibling->SetCompositor(aCompositor);
}
}
gl::GLContext*

View File

@ -335,6 +335,9 @@ GLTextureSource::SetCompositor(Compositor* aCompositor)
{
MOZ_ASSERT(aCompositor);
mCompositor = static_cast<CompositorOGL*>(aCompositor);
if (mNextSibling) {
mNextSibling->SetCompositor(aCompositor);
}
}
bool

View File

@ -157,8 +157,6 @@ DriverCrashGuard::GetGuardFile()
void
DriverCrashGuard::ActivateGuard()
{
MOZ_ASSERT(XRE_IsParentProcess());
mGuardActivated = true;
#ifdef MOZ_CRASHREPORTER

View File

@ -451,7 +451,10 @@ DecodePool::Decode(Decoder* aDecoder)
nsresult rv = aDecoder->Decode();
if (NS_SUCCEEDED(rv) && !aDecoder->GetDecodeDone()) {
if (aDecoder->HasProgress()) {
// If this isn't a metadata decode, notify for the progress we've made so
// far. It's important that metadata decode results are delivered
// atomically, so for those decodes we wait until NotifyDecodeComplete.
if (aDecoder->HasProgress() && !aDecoder->IsMetadataDecode()) {
NotifyProgress(aDecoder);
}
// The decoder will ensure that a new worker gets enqueued to continue

View File

@ -37,10 +37,8 @@ Decoder::Decoder(RasterImage* aImage)
, mMetadataDecode(false)
, mSendPartialInvalidations(false)
, mImageIsTransient(false)
, mImageIsLocked(false)
, mFirstFrameDecode(false)
, mInFrame(false)
, mIsAnimated(false)
, mDataDone(false)
, mDecodeDone(false)
, mDataError(false)
@ -237,7 +235,7 @@ Decoder::CompleteDecode()
// If this image wasn't animated and isn't a transient image, mark its frame
// as optimizable. We don't support optimizing animated images and
// optimizing transient images isn't worth it.
if (!mIsAnimated && !mImageIsTransient && mCurrentFrame) {
if (!HasAnimation() && !mImageIsTransient && mCurrentFrame) {
mCurrentFrame->SetOptimizable();
}
}
@ -260,7 +258,12 @@ Decoder::AllocateFrame(uint32_t aFrameNum,
mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize);
if (aFrameNum + 1 == mFrameCount) {
PostFrameStart();
// If we're past the first frame, PostIsAnimated() should've been called.
MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
// Update our state to reflect the new frame
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
mInFrame = true;
}
} else {
PostDataError();
@ -406,19 +409,11 @@ Decoder::PostHasTransparency()
}
void
Decoder::PostFrameStart()
Decoder::PostIsAnimated(int32_t aFirstFrameTimeout)
{
// We shouldn't already be mid-frame
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
// Update our state to reflect the new frame
mInFrame = true;
// If we just became animated, record that fact.
if (mFrameCount > 1) {
mIsAnimated = true;
mProgress |= FLAG_IS_ANIMATED;
}
mProgress |= FLAG_IS_ANIMATED;
mImageMetadata.SetHasAnimation();
mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
}
void
@ -442,7 +437,7 @@ Decoder::PostFrameStop(Opacity aFrameOpacity /* = Opacity::TRANSPARENT */,
// If we're not sending partial invalidations, then we send an invalidation
// here when the first frame is complete.
if (!mSendPartialInvalidations && !mIsAnimated) {
if (!mSendPartialInvalidations && !HasAnimation()) {
mInvalidRect.UnionRect(mInvalidRect,
gfx::IntRect(gfx::IntPoint(0, 0), GetSize()));
}
@ -459,7 +454,7 @@ Decoder::PostInvalidation(const nsIntRect& aRect,
// Record this invalidation, unless we're not sending partial invalidations
// or we're past the first frame.
if (mSendPartialInvalidations && !mIsAnimated) {
if (mSendPartialInvalidations && !HasAnimation()) {
mInvalidRect.UnionRect(mInvalidRect, aRect);
mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect));
}

View File

@ -181,20 +181,6 @@ public:
mImageIsTransient = aIsTransient;
}
/**
* Set whether the image is locked for the lifetime of this decoder. We lock
* the image during our initial decode to ensure that we don't evict any
* surfaces before we realize that the image is animated.
*/
void SetImageIsLocked()
{
MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
mImageIsLocked = true;
}
bool ImageIsLocked() const { return mImageIsLocked; }
/**
* Set whether we should stop decoding after the first frame.
*/
@ -225,7 +211,7 @@ public:
}
// Did we discover that the image we're decoding is animated?
bool HasAnimation() const { return mIsAnimated; }
bool HasAnimation() const { return mImageMetadata.HasAnimation(); }
// Error tracking
bool HasError() const { return HasDataError() || HasDecoderError(); }
@ -344,9 +330,11 @@ protected:
// actual contents of the frame and give a more accurate result.
void PostHasTransparency();
// Called by decoders when they begin a frame. Informs the image, sends
// notifications, and does internal book-keeping.
void PostFrameStart();
// Called by decoders if they determine that the image is animated.
//
// @param aTimeout The time for which the first frame should be shown before
// we advance to the next frame.
void PostIsAnimated(int32_t aFirstFrameTimeout);
// Called by decoders when they end a frame. Informs the image, sends
// notifications, and does internal book-keeping.
@ -451,10 +439,8 @@ private:
bool mMetadataDecode : 1;
bool mSendPartialInvalidations : 1;
bool mImageIsTransient : 1;
bool mImageIsLocked : 1;
bool mFirstFrameDecode : 1;
bool mInFrame : 1;
bool mIsAnimated : 1;
bool mDataDone : 1;
bool mDecodeDone : 1;
bool mDataError : 1;

View File

@ -113,8 +113,7 @@ DecoderFactory::CreateDecoder(DecoderType aType,
int aSampleSize,
const IntSize& aResolution,
bool aIsRedecode,
bool aImageIsTransient,
bool aImageIsLocked)
bool aImageIsTransient)
{
if (aType == DecoderType::UNKNOWN) {
return nullptr;
@ -131,10 +130,7 @@ DecoderFactory::CreateDecoder(DecoderType aType,
decoder->SetResolution(aResolution);
decoder->SetSendPartialInvalidations(!aIsRedecode);
decoder->SetImageIsTransient(aImageIsTransient);
if (aImageIsLocked) {
decoder->SetImageIsLocked();
}
decoder->SetIsFirstFrameDecode();
// Set a target size for downscale-during-decode if applicable.
if (aTargetSize) {
@ -152,6 +148,39 @@ DecoderFactory::CreateDecoder(DecoderType aType,
return decoder.forget();
}
/* static */ already_AddRefed<Decoder>
DecoderFactory::CreateAnimationDecoder(DecoderType aType,
RasterImage* aImage,
SourceBuffer* aSourceBuffer,
uint32_t aFlags,
const IntSize& aResolution)
{
if (aType == DecoderType::UNKNOWN) {
return nullptr;
}
MOZ_ASSERT(aType == DecoderType::GIF || aType == DecoderType::PNG,
"Calling CreateAnimationDecoder for non-animating DecoderType");
nsRefPtr<Decoder> decoder =
GetDecoder(aType, aImage, /* aIsRedecode = */ true);
MOZ_ASSERT(decoder, "Should have a decoder now");
// Initialize the decoder.
decoder->SetMetadataDecode(false);
decoder->SetIterator(aSourceBuffer->Iterator());
decoder->SetFlags(aFlags);
decoder->SetResolution(aResolution);
decoder->SetSendPartialInvalidations(false);
decoder->Init();
if (NS_FAILED(decoder->GetDecoderError())) {
return nullptr;
}
return decoder.forget();
}
/* static */ already_AddRefed<Decoder>
DecoderFactory::CreateMetadataDecoder(DecoderType aType,
RasterImage* aImage,

View File

@ -38,12 +38,13 @@ public:
static DecoderType GetDecoderType(const char* aMimeType);
/**
* Creates and initializes a decoder of type @aType. The decoder will send
* notifications to @aImage.
* Creates and initializes a decoder for non-animated images of type @aType.
* (If the image *is* animated, only the first frame will be decoded.) The
* decoder will send notifications to @aImage.
*
* XXX(seth): @aIsRedecode, @aImageIsTransient, and @aImageIsLocked should
* really be part of @aFlags. This requires changes to the way that decoder
* flags work, though. See bug 1185800.
* XXX(seth): @aIsRedecode and @aImageIsTransient should really be part of
* @aFlags. This requires changes to the way that decoder flags work, though.
* See bug 1185800.
*
* @param aType Which type of decoder to create - JPEG, PNG, etc.
* @param aImage The image will own the decoder and which should receive
@ -62,9 +63,6 @@ public:
* empty rect if none).
* @param aIsRedecode Specify 'true' if this image has been decoded before.
* @param aImageIsTransient Specify 'true' if this image is transient.
* @param aImageIsLocked Specify 'true' if this image is locked for the
* lifetime of this decoder, and should be unlocked
* when the decoder finishes.
*/
static already_AddRefed<Decoder>
CreateDecoder(DecoderType aType,
@ -75,8 +73,28 @@ public:
int aSampleSize,
const gfx::IntSize& aResolution,
bool aIsRedecode,
bool aImageIsTransient,
bool aImageIsLocked);
bool aImageIsTransient);
/**
* Creates and initializes a decoder for animated images of type @aType.
* The decoder will send notifications to @aImage.
*
* @param aType Which type of decoder to create - JPEG, PNG, etc.
* @param aImage The image will own the decoder and which should receive
* notifications as decoding progresses.
* @param aSourceBuffer The SourceBuffer which the decoder will read its data
* from.
* @param aFlags Flags specifying what type of output the decoder should
* produce; see GetDecodeFlags() in RasterImage.h.
* @param aResolution The resolution requested using #-moz-resolution (or an
* empty rect if none).
*/
static already_AddRefed<Decoder>
CreateAnimationDecoder(DecoderType aType,
RasterImage* aImage,
SourceBuffer* aSourceBuffer,
uint32_t aFlags,
const gfx::IntSize& aResolution);
/**
* Creates and initializes a metadata decoder of type @aType. This decoder

View File

@ -291,14 +291,19 @@ FrameAnimator::GetCompositedFrame(uint32_t aFrameNum)
int32_t
FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
{
int32_t rawTimeout = 0;
RawAccessFrameRef frame = GetRawFrame(aFrameNum);
if (!frame) {
if (frame) {
AnimationData data = frame->GetAnimationData();
rawTimeout = data.mRawTimeout;
} else if (aFrameNum == 0) {
rawTimeout = mFirstFrameTimeout;
} else {
NS_WARNING("No frame; called GetTimeoutForFrame too early?");
return 100;
}
AnimationData data = frame->GetAnimationData();
// Ensure a minimal time between updates so we don't throttle the UI thread.
// consider 0 == unspecified and make it fast but not too fast. Unless we
// have a single loop GIF. See bug 890743, bug 125137, bug 139677, and bug
@ -312,11 +317,11 @@ FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
// It seems that there are broken tools out there that set a 0ms or 10ms
// timeout when they really want a "default" one. So munge values in that
// range.
if (data.mRawTimeout >= 0 && data.mRawTimeout <= 10) {
if (rawTimeout >= 0 && rawTimeout <= 10) {
return 100;
}
return data.mRawTimeout;
return rawTimeout;
}
static void

View File

@ -33,6 +33,7 @@ public:
, mLoopRemainingCount(-1)
, mLastCompositedFrameIndex(-1)
, mLoopCount(-1)
, mFirstFrameTimeout(0)
, mAnimationMode(aAnimationMode)
, mDoneDecoding(false)
{ }
@ -148,6 +149,12 @@ public:
void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
int32_t LoopCount() const { return mLoopCount; }
/*
* Set the timeout for the first frame. This is used to allow animation
* scheduling even before a full decode runs for this image.
*/
void SetFirstFrameTimeout(int32_t aTimeout) { mFirstFrameTimeout = aTimeout; }
/**
* Collect an accounting of the memory occupied by the compositing surfaces we
* use during animation playback. All of the actual animation frames are
@ -277,6 +284,9 @@ private: // data
//! The total number of loops for the image.
int32_t mLoopCount;
//! The timeout for the first frame of this image.
int32_t mFirstFrameTimeout;
//! The animation mode of this image. Constants defined in imgIContainer.
uint16_t mAnimationMode;

View File

@ -1,44 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ImageMetadata.h"
#include "RasterImage.h"
#include "nsComponentManagerUtils.h"
#include "nsISupportsPrimitives.h"
#include "nsXPCOMCID.h"
namespace mozilla {
namespace image {
nsresult
ImageMetadata::SetOnImage(RasterImage* aImage)
{
nsresult rv = NS_OK;
if (mHotspotX != -1 && mHotspotY != -1) {
nsCOMPtr<nsISupportsPRUint32> intwrapx =
do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
nsCOMPtr<nsISupportsPRUint32> intwrapy =
do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
intwrapx->SetData(mHotspotX);
intwrapy->SetData(mHotspotY);
aImage->Set("hotspotX", intwrapx);
aImage->Set("hotspotY", intwrapy);
}
aImage->SetLoopCount(mLoopCount);
if (HasSize()) {
MOZ_ASSERT(HasOrientation(), "Should have orientation");
rv = aImage->SetSize(GetWidth(), GetHeight(), GetOrientation());
}
return rv;
}
} // namespace image
} // namespace mozilla

View File

@ -22,23 +22,26 @@ class ImageMetadata
{
public:
ImageMetadata()
: mHotspotX(-1)
, mHotspotY(-1)
, mLoopCount(-1)
: mLoopCount(-1)
, mFirstFrameTimeout(0)
, mHasAnimation(false)
{ }
// Set the metadata this object represents on an image.
nsresult SetOnImage(RasterImage* aImage);
void SetHotspot(uint16_t hotspotx, uint16_t hotspoty)
void SetHotspot(uint16_t aHotspotX, uint16_t aHotspotY)
{
mHotspotX = hotspotx;
mHotspotY = hotspoty;
mHotspot = Some(gfx::IntPoint(aHotspotX, aHotspotY));
}
gfx::IntPoint GetHotspot() const { return *mHotspot; }
bool HasHotspot() const { return mHotspot.isSome(); }
void SetLoopCount(int32_t loopcount)
{
mLoopCount = loopcount;
}
int32_t GetLoopCount() const { return mLoopCount; }
void SetFirstFrameTimeout(int32_t aTimeout) { mFirstFrameTimeout = aTimeout; }
int32_t GetFirstFrameTimeout() const { return mFirstFrameTimeout; }
void SetSize(int32_t width, int32_t height, Orientation orientation)
{
@ -47,25 +50,28 @@ public:
mOrientation.emplace(orientation);
}
}
nsIntSize GetSize() const { return *mSize; }
Orientation GetOrientation() const { return *mOrientation; }
bool HasSize() const { return mSize.isSome(); }
bool HasOrientation() const { return mOrientation.isSome(); }
int32_t GetWidth() const { return mSize->width; }
int32_t GetHeight() const { return mSize->height; }
nsIntSize GetSize() const { return *mSize; }
Orientation GetOrientation() const { return *mOrientation; }
void SetHasAnimation() { mHasAnimation = true; }
bool HasAnimation() const { return mHasAnimation; }
private:
// The hotspot found on cursors, or -1 if none was found.
int32_t mHotspotX;
int32_t mHotspotY;
/// The hotspot found on cursors, if present.
Maybe<gfx::IntPoint> mHotspot;
// The loop count for animated images, or -1 for infinite loop.
/// The loop count for animated images, or -1 for infinite loop.
int32_t mLoopCount;
/// The timeout of an animated image's first frame.
int32_t mFirstFrameTimeout;
Maybe<nsIntSize> mSize;
Maybe<Orientation> mOrientation;
bool mHasAnimation : 1;
};
} // namespace image

View File

@ -24,6 +24,7 @@
#include "nsIConsoleService.h"
#include "nsIInputStream.h"
#include "nsIScriptError.h"
#include "nsISupportsPrimitives.h"
#include "nsPresContext.h"
#include "SourceBuffer.h"
#include "SurfaceCache.h"
@ -478,7 +479,7 @@ RasterImage::LookupFrame(uint32_t aFrameNum,
// We don't have a copy of this frame, and there's no decoder working on
// one. (Or we're sync decoding and the existing decoder hasn't even started
// yet.) Trigger decoding so it'll be available next time.
MOZ_ASSERT(!mAnim, "Animated frames should be locked");
MOZ_ASSERT(!mAnim || GetNumFrames() < 1, "Animated frames should be locked");
Decode(requestedSize, aFlags);
@ -529,7 +530,7 @@ RasterImage::GetRequestedFrameIndex(uint32_t aWhichFrame) const
IntRect
RasterImage::GetFirstFrameRect()
{
if (mAnim) {
if (mAnim && mHasBeenDecoded) {
return mAnim->GetFirstFrameRefreshArea();
}
@ -582,7 +583,10 @@ RasterImage::GetAnimated(bool* aAnimated)
}
// Otherwise, we need to have been decoded to know for sure, since if we were
// decoded at least once mAnim would have been created for animated images
// decoded at least once mAnim would have been created for animated images.
// This is true even though we check for animation during the metadata decode,
// because we may still discover animation only during the full decode for
// corrupt images.
if (!mHasBeenDecoded) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -921,21 +925,9 @@ RasterImage::OnAddedFrame(uint32_t aNewFrameCount,
mFrameCount = aNewFrameCount;
if (aNewFrameCount == 2) {
// We're becoming animated, so initialize animation stuff.
MOZ_ASSERT(!mAnim, "Already have animation state?");
mAnim = MakeUnique<FrameAnimator>(this, mSize, mAnimationMode);
// We don't support discarding animated images (See bug 414259).
// Lock the image and throw away the key.
//
// Note that this is inefficient, since we could get rid of the source
// data too. However, doing this is actually hard, because we're probably
// mid-decode, and thus we're decoding out of the source buffer. Since
// we're going to fix this anyway later, and since we didn't kill the
// source data in the old world either, locking is acceptable for the
// moment.
LockImage();
MOZ_ASSERT(mAnim, "Should already have animation state");
// We may be able to start animating.
if (mPendingAnimation && ShouldAnimate()) {
StartAnimation();
}
@ -947,7 +939,8 @@ RasterImage::OnAddedFrame(uint32_t aNewFrameCount,
}
nsresult
RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation)
RasterImage::SetMetadata(const ImageMetadata& aMetadata,
bool aFromMetadataDecode)
{
MOZ_ASSERT(NS_IsMainThread());
@ -955,26 +948,64 @@ RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation)
return NS_ERROR_FAILURE;
}
// Ensure that we have positive values
// XXX - Why isn't the size unsigned? Should this be changed?
if ((aWidth < 0) || (aHeight < 0)) {
return NS_ERROR_INVALID_ARG;
if (aMetadata.HasSize()) {
IntSize size = aMetadata.GetSize();
if (size.width < 0 || size.height < 0) {
return NS_ERROR_INVALID_ARG;
}
MOZ_ASSERT(aMetadata.HasOrientation());
Orientation orientation = aMetadata.GetOrientation();
// If we already have a size, check the new size against the old one.
if (mHasSize && (size != mSize || orientation != mOrientation)) {
NS_WARNING("Image changed size or orientation on redecode! "
"This should not happen!");
DoError();
return NS_ERROR_UNEXPECTED;
}
// Set the size and flag that we have it.
mSize = size;
mOrientation = orientation;
mHasSize = true;
}
// if we already have a size, check the new size against the old one
if (mHasSize &&
((aWidth != mSize.width) ||
(aHeight != mSize.height) ||
(aOrientation != mOrientation))) {
NS_WARNING("Image changed size on redecode! This should not happen!");
DoError();
return NS_ERROR_UNEXPECTED;
if (mHasSize && aMetadata.HasAnimation() && !mAnim) {
// We're becoming animated, so initialize animation stuff.
mAnim = MakeUnique<FrameAnimator>(this, mSize, mAnimationMode);
// We don't support discarding animated images (See bug 414259).
// Lock the image and throw away the key.
LockImage();
if (!aFromMetadataDecode) {
// The metadata decode reported that this image isn't animated, but we
// discovered that it actually was during the full decode. This is a
// rare failure that only occurs for corrupt images. To recover, we need
// to discard all existing surfaces and redecode.
RecoverFromLossOfFrames(mSize, DECODE_FLAGS_DEFAULT);
}
}
// Set the size and flag that we have it
mSize.SizeTo(aWidth, aHeight);
mOrientation = aOrientation;
mHasSize = true;
if (mAnim) {
mAnim->SetLoopCount(aMetadata.GetLoopCount());
mAnim->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
}
if (aMetadata.HasHotspot()) {
IntPoint hotspot = aMetadata.GetHotspot();
nsCOMPtr<nsISupportsPRUint32> intwrapx =
do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
nsCOMPtr<nsISupportsPRUint32> intwrapy =
do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
intwrapx->SetData(hotspot.x);
intwrapy->SetData(hotspot.y);
Set("hotspotX", intwrapx);
Set("hotspotY", intwrapy);
}
return NS_OK;
}
@ -999,10 +1030,9 @@ RasterImage::StartAnimation()
MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
// If we don't have mAnim yet, then we're not ready to animate. Setting
// mPendingAnimation will cause us to start animating as soon as we have a
// second frame, which causes mAnim to be constructed.
mPendingAnimation = !mAnim;
// If we're not ready to animate, then set mPendingAnimation, which will cause
// us to start animating if and when we do become ready.
mPendingAnimation = !mAnim || GetNumFrames() < 2;
if (mPendingAnimation) {
return NS_OK;
}
@ -1091,19 +1121,6 @@ RasterImage::GetFrameIndex(uint32_t aWhichFrame)
: mAnim->GetCurrentAnimationFrameIndex();
}
void
RasterImage::SetLoopCount(int32_t aLoopCount)
{
if (mError) {
return;
}
// No need to set this if we're not an animation.
if (mAnim) {
mAnim->SetLoopCount(aLoopCount);
}
}
NS_IMETHODIMP_(IntRect)
RasterImage::GetImageSpaceInvalidationRect(const IntRect& aRect)
{
@ -1391,20 +1408,19 @@ RasterImage::Decode(const IntSize& aSize, uint32_t aFlags)
Maybe<IntSize> targetSize = mSize != aSize ? Some(aSize) : Nothing();
bool imageIsLocked = false;
if (!mHasBeenDecoded) {
// Lock the image while we're decoding, so that it doesn't get evicted from
// the SurfaceCache before we have a chance to realize that it's animated.
// The corresponding unlock happens in FinalizeDecoder.
LockImage();
imageIsLocked = true;
}
// Create a decoder.
nsRefPtr<Decoder> decoder =
DecoderFactory::CreateDecoder(mDecoderType, this, mSourceBuffer, targetSize,
aFlags, mRequestedSampleSize, mRequestedResolution,
mHasBeenDecoded, mTransient, imageIsLocked);
nsRefPtr<Decoder> decoder;
if (mAnim) {
decoder = DecoderFactory::CreateAnimationDecoder(mDecoderType, this,
mSourceBuffer, aFlags,
mRequestedResolution);
} else {
decoder = DecoderFactory::CreateDecoder(mDecoderType, this, mSourceBuffer,
targetSize, aFlags,
mRequestedSampleSize,
mRequestedResolution,
mHasBeenDecoded, mTransient);
}
// Make sure DecoderFactory was able to create a decoder successfully.
if (!decoder) {
@ -1483,6 +1499,11 @@ RasterImage::RecoverFromLossOfFrames(const IntSize& aSize, uint32_t aFlags)
// Discard all existing frames, since they're probably all now invalid.
SurfaceCache::RemoveImage(ImageKey(this));
// Relock the image if it's supposed to be locked.
if (mLockCount > 0) {
SurfaceCache::LockImage(ImageKey(this));
}
// Animated images require some special handling, because we normally require
// that they never be discarded.
if (mAnim) {
@ -1948,7 +1969,8 @@ RasterImage::FinalizeDecoder(Decoder* aDecoder)
}
// Record all the metadata the decoder gathered about this image.
nsresult rv = aDecoder->GetImageMetadata().SetOnImage(this);
nsresult rv = SetMetadata(aDecoder->GetImageMetadata(),
aDecoder->IsMetadataDecode());
if (NS_FAILED(rv)) {
aDecoder->PostResizeError();
}
@ -1959,17 +1981,8 @@ RasterImage::FinalizeDecoder(Decoder* aDecoder)
if (aDecoder->GetDecodeTotallyDone() && !mError) {
// Flag that we've been decoded before.
mHasBeenDecoded = true;
if (aDecoder->HasAnimation()) {
if (mAnim) {
mAnim->SetDoneDecoding(true);
} else {
// The OnAddedFrame event that will create mAnim is still in the event
// queue. Wait for it.
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &RasterImage::MarkAnimationDecoded);
NS_DispatchToMainThread(runnable);
}
if (mAnim) {
mAnim->SetDoneDecoding(true);
}
}
@ -2017,11 +2030,6 @@ RasterImage::FinalizeDecoder(Decoder* aDecoder)
}
}
if (aDecoder->ImageIsLocked()) {
// Unlock the image, balancing the LockImage call we made in CreateDecoder.
UnlockImage();
}
// If we were a metadata decode and a full decode was requested, do it.
if (done && wasMetadata && mWantFullDecode) {
mWantFullDecode = false;
@ -2029,17 +2037,6 @@ RasterImage::FinalizeDecoder(Decoder* aDecoder)
}
}
void
RasterImage::MarkAnimationDecoded()
{
MOZ_ASSERT(mAnim, "Should have an animation now");
if (!mAnim) {
return;
}
mAnim->SetDoneDecoding(true);
}
void
RasterImage::ReportDecoderError(Decoder* aDecoder)
{

View File

@ -132,6 +132,7 @@ namespace image {
class Decoder;
class FrameAnimator;
class ImageMetadata;
class SourceBuffer;
/**
@ -188,18 +189,6 @@ public:
void OnAddedFrame(uint32_t aNewFrameCount, const nsIntRect& aNewRefreshArea);
/** Sets the size and inherent orientation of the container. This should only
* be called by the decoder. This function may be called multiple times, but
* will throw an error if subsequent calls do not match the first.
*/
nsresult SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation);
/**
* Number of times to loop the image.
* @note -1 means forever.
*/
void SetLoopCount(int32_t aLoopCount);
/**
* Sends the provided progress notifications to ProgressTracker.
*
@ -222,8 +211,7 @@ public:
*/
void FinalizeDecoder(Decoder* aDecoder);
// Helper methods for FinalizeDecoder.
void MarkAnimationDecoded();
// Helper method for FinalizeDecoder.
void ReportDecoderError(Decoder* aDecoder);
@ -339,6 +327,19 @@ private:
*/
NS_IMETHOD DecodeMetadata(uint32_t aFlags);
/**
* Sets the size, inherent orientation, animation metadata, and other
* information about the image gathered during decoding.
*
* This function may be called multiple times, but will throw an error if
* subsequent calls do not match the first.
*
* @param aMetadata The metadata to set on this image.
* @param aFromMetadataDecode True if this metadata came from a metadata
* decode; false if it came from a full decode.
*/
nsresult SetMetadata(const ImageMetadata& aMetadata, bool aFromMetadataDecode);
/**
* In catastrophic circumstances like a GPU driver crash, we may lose our
* frames even if they're locked. RecoverFromLossOfFrames discards all

View File

@ -225,6 +225,10 @@ NS_IMETHODIMP
nsIconChannel::AsyncOpen(nsIStreamListener* aListener,
nsISupports* ctxt)
{
MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
mLoadInfo->GetInitialSecurityCheckDone(),
"security flags in loadInfo but asyncOpen2() not called");
nsCOMPtr<nsIInputStream> inStream;
nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -239,6 +239,10 @@ NS_IMETHODIMP
nsIconChannel::AsyncOpen(nsIStreamListener* aListener,
nsISupports* ctxt)
{
MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
mLoadInfo->GetInitialSecurityCheckDone(),
"security flags in loadInfo but asyncOpen2() not called");
nsCOMPtr<nsIInputStream> inStream;
nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
if (NS_FAILED(rv)) {

View File

@ -857,6 +857,11 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
}
mGIFStruct.delay_time = GETINT16(q + 1) * 10;
if (mGIFStruct.delay_time > 0) {
PostIsAnimated(mGIFStruct.delay_time);
}
GETN(1, gif_consume_block);
break;
@ -921,11 +926,20 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
break;
case gif_image_header: {
if (mGIFStruct.images_decoded > 0 && IsFirstFrameDecode()) {
// We're about to get a second frame, but we only want the first. Stop
// decoding now.
mGIFStruct.state = gif_done;
break;
if (mGIFStruct.images_decoded == 1) {
if (!HasAnimation()) {
// We should've already called PostIsAnimated(); this must be a
// corrupt animated image with a first frame timeout of zero. Signal
// that we're animated now, before the first-frame decode early exit
// below, so that RasterImage can detect that this happened.
PostIsAnimated(/* aFirstFrameTimeout = */ 0);
}
if (IsFirstFrameDecode()) {
// We're about to get a second frame, but we only want the first. Stop
// decoding now.
mGIFStruct.state = gif_done;
break;
}
}
// Get image offsets, with respect to the screen origin

View File

@ -378,8 +378,8 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
}
if (!HasSize() && mContainedDecoder->HasSize()) {
PostSize(mContainedDecoder->GetImageMetadata().GetWidth(),
mContainedDecoder->GetImageMetadata().GetHeight());
nsIntSize size = mContainedDecoder->GetSize();
PostSize(size.width, size.height);
}
mPos += aCount;
@ -479,8 +479,8 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
return;
}
PostSize(mContainedDecoder->GetImageMetadata().GetWidth(),
mContainedDecoder->GetImageMetadata().GetHeight());
nsIntSize size = mContainedDecoder->GetSize();
PostSize(size.width, size.height);
// We have the size. If we're doing a metadata decode, we're done.
if (IsMetadataDecode()) {

View File

@ -56,32 +56,33 @@ nsPNGDecoder::AnimFrameInfo::AnimFrameInfo()
{ }
#ifdef PNG_APNG_SUPPORTED
int32_t GetNextFrameDelay(png_structp aPNG, png_infop aInfo)
{
// Delay, in seconds, is delayNum / delayDen.
png_uint_16 delayNum = png_get_next_frame_delay_num(aPNG, aInfo);
png_uint_16 delayDen = png_get_next_frame_delay_den(aPNG, aInfo);
if (delayNum == 0) {
return 0; // SetFrameTimeout() will set to a minimum.
}
if (delayDen == 0) {
delayDen = 100; // So says the APNG spec.
}
// Need to cast delay_num to float to have a proper division and
// the result to int to avoid a compiler warning.
return static_cast<int32_t>(static_cast<double>(delayNum) * 1000 / delayDen);
}
nsPNGDecoder::AnimFrameInfo::AnimFrameInfo(png_structp aPNG, png_infop aInfo)
: mDispose(DisposalMethod::KEEP)
, mBlend(BlendMethod::OVER)
, mTimeout(0)
{
png_uint_16 delay_num, delay_den;
// delay, in seconds is delay_num/delay_den
png_byte dispose_op;
png_byte blend_op;
delay_num = png_get_next_frame_delay_num(aPNG, aInfo);
delay_den = png_get_next_frame_delay_den(aPNG, aInfo);
dispose_op = png_get_next_frame_dispose_op(aPNG, aInfo);
blend_op = png_get_next_frame_blend_op(aPNG, aInfo);
if (delay_num == 0) {
mTimeout = 0; // SetFrameTimeout() will set to a minimum
} else {
if (delay_den == 0) {
delay_den = 100; // so says the APNG spec
}
// Need to cast delay_num to float to have a proper division and
// the result to int to avoid compiler warning
mTimeout = static_cast<int32_t>(static_cast<double>(delay_num) *
1000 / delay_den);
}
png_byte dispose_op = png_get_next_frame_dispose_op(aPNG, aInfo);
png_byte blend_op = png_get_next_frame_blend_op(aPNG, aInfo);
if (dispose_op == PNG_DISPOSE_OP_PREVIOUS) {
mDispose = DisposalMethod::RESTORE_PREVIOUS;
@ -96,6 +97,8 @@ nsPNGDecoder::AnimFrameInfo::AnimFrameInfo(png_structp aPNG, png_infop aInfo)
} else {
mBlend = BlendMethod::OVER;
}
mTimeout = GetNextFrameDelay(aPNG, aInfo);
}
#endif
@ -595,18 +598,25 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
png_longjmp(decoder->mPNG, 1); // invalid number of channels
}
#ifdef PNG_APNG_SUPPORTED
bool isAnimated = png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL);
if (isAnimated) {
decoder->PostIsAnimated(GetNextFrameDelay(png_ptr, info_ptr));
}
#endif
if (decoder->IsMetadataDecode()) {
decoder->CheckForTransparency(decoder->format,
IntRect(0, 0, width, height));
// We have the size and transparency information we're looking for, so we
// don't need to decode any further.
// We have the metadata we're looking for, so we don't need to decode any
// further.
decoder->mSuccessfulEarlyFinish = true;
png_longjmp(decoder->mPNG, 1);
}
#ifdef PNG_APNG_SUPPORTED
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
if (isAnimated) {
png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback,
nullptr);
}

View File

@ -59,7 +59,6 @@ UNIFIED_SOURCES += [
'Image.cpp',
'ImageCacheKey.cpp',
'ImageFactory.cpp',
'ImageMetadata.cpp',
'ImageOps.cpp',
'ImageWrapper.cpp',
'imgFrame.cpp',

View File

@ -15,6 +15,7 @@
#include "nsIProperties.h"
#include "nsNetUtil.h"
#include "mozilla/nsRefPtr.h"
#include "nsStreamUtils.h"
#include "nsString.h"
namespace mozilla {
@ -71,6 +72,15 @@ LoadFile(const char* aRelativePath)
rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), file);
ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr);
// Ensure the resulting input stream is buffered.
if (!NS_InputStreamIsBuffered(inputStream)) {
nsCOMPtr<nsIInputStream> bufStream;
rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
inputStream, 1024);
ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr);
inputStream = bufStream;
}
return inputStream.forget();
}
@ -144,13 +154,14 @@ ImageTestCase GreenICOTestCase()
ImageTestCase GreenFirstFrameAnimatedGIFTestCase()
{
return ImageTestCase("first-frame-green.gif", "image/gif", IntSize(100, 100));
return ImageTestCase("first-frame-green.gif", "image/gif", IntSize(100, 100),
TEST_CASE_IS_ANIMATED);
}
ImageTestCase GreenFirstFrameAnimatedPNGTestCase()
{
return ImageTestCase("first-frame-green.png", "image/png", IntSize(100, 100),
TEST_CASE_IS_TRANSPARENT);
TEST_CASE_IS_TRANSPARENT | TEST_CASE_IS_ANIMATED);
}
ImageTestCase CorruptTestCase()
@ -198,4 +209,13 @@ ImageTestCase RLE8BMPTestCase()
TEST_CASE_IS_TRANSPARENT);
}
ImageTestCase NoFrameDelayGIFTestCase()
{
// This is an invalid (or at least, questionably valid) GIF that's animated
// even though it specifies a frame delay of zero. It's animated, but it's not
// marked TEST_CASE_IS_ANIMATED because the metadata decoder can't detect that
// it's animated.
return ImageTestCase("no-frame-delay.gif", "image/gif", IntSize(100, 100));
}
} // namespace mozilla

View File

@ -21,8 +21,9 @@ enum TestCaseFlags
{
TEST_CASE_DEFAULT_FLAGS = 0,
TEST_CASE_IS_FUZZY = 1 << 0,
TEST_CASE_IS_TRANSPARENT = 1 << 1,
TEST_CASE_HAS_ERROR = 1 << 2
TEST_CASE_HAS_ERROR = 1 << 1,
TEST_CASE_IS_TRANSPARENT = 1 << 2,
TEST_CASE_IS_ANIMATED = 1 << 3,
};
struct ImageTestCase
@ -97,6 +98,7 @@ ImageTestCase CorruptTestCase();
ImageTestCase TransparentPNGTestCase();
ImageTestCase TransparentGIFTestCase();
ImageTestCase FirstFramePaddingGIFTestCase();
ImageTestCase NoFrameDelayGIFTestCase();
ImageTestCase TransparentBMPWhenBMPAlphaEnabledTestCase();
ImageTestCase RLE4BMPTestCase();

View File

@ -10,6 +10,7 @@
#include "decoders/nsBMPDecoder.h"
#include "imgIContainer.h"
#include "imgITools.h"
#include "ImageFactory.h"
#include "mozilla/gfx/2D.h"
#include "nsComponentManagerUtils.h"
#include "nsCOMPtr.h"
@ -37,26 +38,22 @@ TEST(ImageMetadata, ImageModuleAvailable)
EXPECT_TRUE(imgTools != nullptr);
}
enum class BMPAlpha
{
DISABLED,
ENABLED
};
static void
CheckMetadata(const ImageTestCase& aTestCase, bool aEnableBMPAlpha = false)
CheckMetadata(const ImageTestCase& aTestCase,
BMPAlpha aBMPAlpha = BMPAlpha::DISABLED)
{
nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
ASSERT_TRUE(inputStream != nullptr);
// Prepare the input stream.
nsresult rv;
if (!NS_InputStreamIsBuffered(inputStream)) {
nsCOMPtr<nsIInputStream> bufStream;
rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
inputStream, 1024);
if (NS_SUCCEEDED(rv)) {
inputStream = bufStream;
}
}
// Figure out how much data we have.
uint64_t length;
rv = inputStream->Available(&length);
nsresult rv = inputStream->Available(&length);
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Write the data into a SourceBuffer.
@ -73,7 +70,7 @@ CheckMetadata(const ImageTestCase& aTestCase, bool aEnableBMPAlpha = false)
DecoderFactory::CreateAnonymousMetadataDecoder(decoderType, sourceBuffer);
ASSERT_TRUE(decoder != nullptr);
if (aEnableBMPAlpha) {
if (aBMPAlpha == BMPAlpha::ENABLED) {
static_cast<nsBMPDecoder*>(decoder.get())->SetUseAlphaData(true);
}
@ -84,7 +81,8 @@ CheckMetadata(const ImageTestCase& aTestCase, bool aEnableBMPAlpha = false)
// (which would indicate that it decoded past the header of the image).
Progress metadataProgress = decoder->TakeProgress();
EXPECT_TRUE(0 == (metadataProgress & ~(FLAG_SIZE_AVAILABLE |
FLAG_HAS_TRANSPARENCY)));
FLAG_HAS_TRANSPARENCY |
FLAG_IS_ANIMATED)));
// If the test case is corrupt, assert what we can and return early.
if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) {
@ -102,18 +100,21 @@ CheckMetadata(const ImageTestCase& aTestCase, bool aEnableBMPAlpha = false)
EXPECT_EQ(aTestCase.mSize.width, metadataSize.width);
EXPECT_EQ(aTestCase.mSize.height, metadataSize.height);
bool expectTransparency = aEnableBMPAlpha
bool expectTransparency = aBMPAlpha == BMPAlpha::ENABLED
? true
: bool(aTestCase.mFlags & TEST_CASE_IS_TRANSPARENT);
EXPECT_EQ(expectTransparency, bool(metadataProgress & FLAG_HAS_TRANSPARENCY));
EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED),
bool(metadataProgress & FLAG_IS_ANIMATED));
// Create a full decoder, so we can compare the result.
decoder =
DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer,
imgIContainer::DECODE_FLAGS_DEFAULT);
ASSERT_TRUE(decoder != nullptr);
if (aEnableBMPAlpha) {
if (aBMPAlpha == BMPAlpha::ENABLED) {
static_cast<nsBMPDecoder*>(decoder.get())->SetUseAlphaData(true);
}
@ -164,17 +165,90 @@ TEST(ImageMetadata, FirstFramePaddingGIF)
TEST(ImageMetadata, TransparentBMPWithBMPAlphaOff)
{
CheckMetadata(TransparentBMPWhenBMPAlphaEnabledTestCase(),
/* aEnableBMPAlpha = */ false);
CheckMetadata(TransparentBMPWhenBMPAlphaEnabledTestCase(), BMPAlpha::ENABLED);
}
TEST(ImageMetadata, TransparentBMPWithBMPAlphaOn)
{
CheckMetadata(TransparentBMPWhenBMPAlphaEnabledTestCase(),
/* aEnableBMPAlpha = */ true);
CheckMetadata(TransparentBMPWhenBMPAlphaEnabledTestCase(), BMPAlpha::ENABLED);
}
TEST(ImageMetadata, RLE4BMP) { CheckMetadata(RLE4BMPTestCase()); }
TEST(ImageMetadata, RLE8BMP) { CheckMetadata(RLE8BMPTestCase()); }
TEST(ImageMetadata, Corrupt) { CheckMetadata(CorruptTestCase()); }
TEST(ImageMetadata, NoFrameDelayGIF)
{
CheckMetadata(NoFrameDelayGIFTestCase());
}
TEST(ImageMetadata, NoFrameDelayGIFFullDecode)
{
ImageTestCase testCase = NoFrameDelayGIFTestCase();
// The previous test (NoFrameDelayGIF) verifies that we *don't* detect that
// this test case is animated, because it has a zero frame delay for the first
// frame. This test verifies that when we do a full decode, we detect the
// animation at that point and successfully decode all the frames.
// Create an image.
nsRefPtr<Image> image =
ImageFactory::CreateAnonymousImage(nsAutoCString(testCase.mMimeType));
ASSERT_TRUE(!image->HasError());
nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
ASSERT_TRUE(inputStream != nullptr);
// Figure out how much data we have.
uint64_t length;
nsresult rv = inputStream->Available(&length);
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Write the data into the image.
rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
static_cast<uint32_t>(length));
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Let the image know we've sent all the data.
rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
ASSERT_TRUE(NS_SUCCEEDED(rv));
nsRefPtr<ProgressTracker> tracker = image->GetProgressTracker();
tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
// Use GetFrame() to force a sync decode of the image.
nsRefPtr<SourceSurface> surface =
image->GetFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE);
// Ensure that the image's metadata meets our expectations.
IntSize imageSize(0, 0);
rv = image->GetWidth(&imageSize.width);
EXPECT_TRUE(NS_SUCCEEDED(rv));
rv = image->GetHeight(&imageSize.height);
EXPECT_TRUE(NS_SUCCEEDED(rv));
EXPECT_EQ(testCase.mSize.width, imageSize.width);
EXPECT_EQ(testCase.mSize.height, imageSize.height);
Progress imageProgress = tracker->GetProgress();
EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
// Ensure that we decoded both frames of the image.
LookupResult firstFrameLookupResult =
SurfaceCache::Lookup(ImageKey(image.get()),
RasterSurfaceKey(imageSize,
imgIContainer::DECODE_FLAGS_DEFAULT,
/* aFrameNum = */ 0));
EXPECT_EQ(MatchType::EXACT, firstFrameLookupResult.Type());
LookupResult secondFrameLookupResult =
SurfaceCache::Lookup(ImageKey(image.get()),
RasterSurfaceKey(imageSize,
imgIContainer::DECODE_FLAGS_DEFAULT,
/* aFrameNum = */ 1));
EXPECT_EQ(MatchType::EXACT, secondFrameLookupResult.Type());
}

View File

@ -24,6 +24,7 @@ TEST_HARNESS_FILES.gtest += [
'green.ico',
'green.jpg',
'green.png',
'no-frame-delay.gif',
'rle4.bmp',
'rle8.bmp',
'transparent.bmp',

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

View File

@ -361,23 +361,6 @@ js::obj_toString(JSContext* cx, unsigned argc, Value* vp)
return true;
}
/* ES5 15.2.4.3. */
static bool
obj_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
{
JS_CHECK_RECURSION(cx, return false);
CallArgs args = CallArgsFromVp(argc, vp);
/* Step 1. */
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
/* Steps 2-4. */
RootedId id(cx, NameToId(cx->names().toString));
return obj->callMethod(cx, id, 0, nullptr, args.rval());
}
bool
js::obj_valueOf(JSContext* cx, unsigned argc, Value* vp)
@ -999,7 +982,7 @@ static const JSFunctionSpec object_methods[] = {
JS_FN(js_toSource_str, obj_toSource, 0,0),
#endif
JS_FN(js_toString_str, obj_toString, 0,0),
JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0,JSPROP_DEFINE_LATE),
JS_FN(js_valueOf_str, obj_valueOf, 0,0),
#if JS_HAS_OBJ_WATCHPOINT
JS_FN(js_watch_str, obj_watch, 2,0),

View File

@ -51,6 +51,15 @@ function ObjectIsExtensible(obj) {
return IsObject(obj) && std_Reflect_isExtensible(obj);
}
/* ES2015 19.1.3.5 Object.prototype.toLocaleString */
function Object_toLocaleString() {
// Step 1.
var O = this;
// Step 2.
return O.toString();
}
function ObjectDefineSetter(name, setter) {
var object;
if (this === null || this === undefined)

View File

@ -1922,7 +1922,7 @@ TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, Handl
uint32_t index;
if (IdIsIndex(id, &index)) {
if (!receiver.isObject() || obj != &receiver.toObject())
return SetPropertyByDefining(cx, obj, id, v, receiver, false, result);
return SetPropertyByDefining(cx, obj, id, v, receiver, result);
if (index >= uint32_t(typedObj->length())) {
JS_ReportErrorNumber(cx, GetErrorMessage,
@ -1948,7 +1948,7 @@ TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, Handl
break;
if (!receiver.isObject() || obj != &receiver.toObject())
return SetPropertyByDefining(cx, obj, id, v, receiver, false, result);
return SetPropertyByDefining(cx, obj, id, v, receiver, result);
size_t offset = descr->fieldOffset(fieldIndex);
Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));

File diff suppressed because it is too large Load Diff

View File

@ -5705,7 +5705,9 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling)
case TOK_COMMA:
// No value.
exprNode = null();
tokenStream.addModifierException(TokenStream::NoneIsOperand);
tokenStream.addModifierException((tt == TOK_EOL || tt == TOK_EOF)
? TokenStream::NoneIsOperandYieldEOL
: TokenStream::NoneIsOperand);
break;
case TOK_MUL:
kind = PNK_YIELD_STAR;
@ -5768,7 +5770,9 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling)
case TOK_COMMA:
// No value.
exprNode = null();
tokenStream.addModifierException(TokenStream::NoneIsOperand);
tokenStream.addModifierException((tt == TOK_EOL || tt == TOK_EOF)
? TokenStream::NoneIsOperandYieldEOL
: TokenStream::NoneIsOperand);
break;
default:
exprNode = assignExpr(inHandling, YieldIsKeyword);

View File

@ -1650,7 +1650,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
// occurs and then the token is re-gotten (or peeked, etc.), we can assert
// that both gets have used the same modifiers.
tp->modifier = modifier;
tp->modifierExceptions = NoException;
tp->modifierException = NoException;
#endif
MOZ_ASSERT(IsTokenSane(tp));
*ttp = tp->type;

View File

@ -117,6 +117,29 @@ struct Token
// ^ TemplateTail context
TemplateTail,
};
enum ModifierException
{
NoException,
// If an yield expression operand is omitted and yield expression is
// followed by non-EOL, the next token is already gotten with Operand,
// but we expect operator (None).
NoneIsOperand,
// If an yield expression operand is omitted and yield expression is
// followed by EOL, the next token is already gotten with Operand, and
// we expect Operand in next statement, but MatchOrInsertSemicolon
// after expression statement expects operator (None).
NoneIsOperandYieldEOL,
// If a semicolon is inserted automatically, the next token is already
// gotten with None, but we expect Operand.
OperandIsNone,
// If name of method definition is `get` or `set`, the next token is
// already gotten with KeywordIsName, but we expect None.
NoneIsKeywordIsName,
};
friend class TokenStream;
public:
@ -136,7 +159,7 @@ struct Token
} u;
#ifdef DEBUG
Modifier modifier; // Modifier used to get this token
uint8_t modifierExceptions; // Bitwise OR of modifier exceptions
ModifierException modifierException; // Exception for this modifier
#endif
// This constructor is necessary only for MSVC 2013 and how it compiles the
@ -411,37 +434,47 @@ class MOZ_STACK_CLASS TokenStream
static MOZ_CONSTEXPR_VAR Modifier KeywordIsName = Token::KeywordIsName;
static MOZ_CONSTEXPR_VAR Modifier TemplateTail = Token::TemplateTail;
enum ModifierException
{
NoException = 0x00,
// If a semicolon is inserted automatically, the next token is already
// gotten with None, but we expect Operand.
NoneIsOperand = 0x01,
// If an yield expression operand is omitted, the next token is already
// gotten with Operand, but we expect operator (None).
OperandIsNone = 0x02,
// If name of method definition is `get` or `set`, the next token is
// already gotten with KeywordIsName, but we expect None.
NoneIsKeywordIsName = 0x04,
};
typedef Token::ModifierException ModifierException;
static MOZ_CONSTEXPR_VAR ModifierException NoException = Token::NoException;
static MOZ_CONSTEXPR_VAR ModifierException NoneIsOperand = Token::NoneIsOperand;
static MOZ_CONSTEXPR_VAR ModifierException NoneIsOperandYieldEOL = Token::NoneIsOperandYieldEOL;
static MOZ_CONSTEXPR_VAR ModifierException OperandIsNone = Token::OperandIsNone;
static MOZ_CONSTEXPR_VAR ModifierException NoneIsKeywordIsName = Token::NoneIsKeywordIsName;
void addModifierException(ModifierException modifierException) {
#ifdef DEBUG
const Token& next = nextToken();
if (next.modifierException == NoneIsOperand ||
next.modifierException == NoneIsOperandYieldEOL)
{
// Token after yield expression without operand already has
// NoneIsOperand or NoneIsOperandYieldEOL exception.
MOZ_ASSERT(modifierException == OperandIsNone);
if (next.modifierException == NoneIsOperand)
MOZ_ASSERT(next.type != TOK_DIV && next.type != TOK_REGEXP,
"next token requires contextual specifier to be parsed unambiguously");
else
MOZ_ASSERT(next.type != TOK_DIV,
"next token requires contextual specifier to be parsed unambiguously");
// Do not update modifierException.
return;
}
MOZ_ASSERT(next.modifierException == NoException);
switch (modifierException) {
case NoneIsOperand:
MOZ_ASSERT(next.modifier == Operand);
MOZ_ASSERT(next.type != TOK_DIV && next.type != TOK_REGEXP,
"next token requires contextual specifier to be parsed unambiguously");
break;
case NoneIsOperandYieldEOL:
MOZ_ASSERT(next.modifier == Operand);
MOZ_ASSERT(next.type != TOK_DIV,
"next token requires contextual specifier to be parsed unambiguously");
break;
case OperandIsNone:
// Non-Operand token after yield/continue/break already has
// NoneIsOperand exception.
MOZ_ASSERT(next.modifier == None ||
((next.modifierExceptions & NoneIsOperand) && next.modifier == Operand));
MOZ_ASSERT(next.modifier == None);
MOZ_ASSERT(next.type != TOK_DIV && next.type != TOK_REGEXP,
"next token requires contextual specifier to be parsed unambiguously");
break;
@ -452,7 +485,7 @@ class MOZ_STACK_CLASS TokenStream
default:
MOZ_CRASH("unexpected modifier exception");
}
tokens[(cursor + 1) & ntokensMask].modifierExceptions |= modifierException;
tokens[(cursor + 1) & ntokensMask].modifierException = modifierException;
#endif
}
@ -473,19 +506,21 @@ class MOZ_STACK_CLASS TokenStream
if (modifier == lookaheadToken.modifier)
return;
if (lookaheadToken.modifierExceptions & OperandIsNone) {
if (lookaheadToken.modifierException == OperandIsNone) {
// getToken(Operand) permissibly following getToken().
if (modifier == Operand && lookaheadToken.modifier == None)
return;
}
if (lookaheadToken.modifierExceptions & NoneIsOperand) {
if (lookaheadToken.modifierException == NoneIsOperand ||
lookaheadToken.modifierException == NoneIsOperandYieldEOL)
{
// getToken() permissibly following getToken(Operand).
if (modifier == None && lookaheadToken.modifier == Operand)
return;
}
if (lookaheadToken.modifierExceptions & NoneIsKeywordIsName) {
if (lookaheadToken.modifierException == NoneIsKeywordIsName) {
// getToken() permissibly following getToken(KeywordIsName).
if (modifier == None && lookaheadToken.modifier == KeywordIsName)
return;

View File

@ -0,0 +1,10 @@
var n = 0;
this.__proto__ = new Proxy({}, {
has: function () {
if (++n === 2)
return false;
a = 0;
}
});
a = 0;
assertEq(a, 0);

View File

@ -0,0 +1,9 @@
// |jit-test| error: m is not defined
this.__proto__ = Proxy.create({
has:function(){
try {
aa0 = Function(undefined);
} catch (aa) {}
}
});
m();

View File

@ -0,0 +1,11 @@
var obj;
for (var i = 0; i < 100; i++)
obj = {a: 7, b: 13, c: 42, d: 0};
Object.defineProperty(obj, "x", {
get: function () { return 3; }
});
obj.__ob__ = 17;
Object.defineProperty(obj, "c", {value: 8, writable: true});
assertEq(obj.__ob__, 17);

View File

@ -0,0 +1,7 @@
// |jit-test| --no-ggc; allow-unhandlable-oom; --no-threads
load(libdir + 'oomTest.js');
oomTest(function () {
eval(`var wm = new WeakMap();
wm.set({}, 'FOO').get(false);`);
});

View File

@ -475,7 +475,8 @@ function rpow_number(i) {
var x = Math.pow(i, 3.14159);
if (uceFault_pow_number(i) || uceFault_pow_number(i))
assertEq(x, Math.pow(99, 3.14159));
assertRecoveredOnBailout(x, true);
// POW recovery temporarily disabled. See bug 1188586.
assertRecoveredOnBailout(x, false);
return i;
}

View File

@ -6,8 +6,14 @@ assertNoWarning(() => Function("yield"), SyntaxError,
"yield followed by EOF is fine");
assertNoWarning(() => Function("yield;"), SyntaxError,
"yield followed by semicolon is fine");
assertNoWarning(() => Function("yield\n print('ok');"), SyntaxError,
assertNoWarning(() => Function("yield\n"), SyntaxError,
"yield followed by newline is fine");
assertNoWarning(() => Function("yield\n print('ok');"), SyntaxError,
"yield followed by newline and statement is fine");
assertNoWarning(() => Function("yield\n /x/;"), SyntaxError,
"yield followed by newline and regexp is fine");
assertThrowsInstanceOf(() => Function("yield\n /"), SyntaxError,
"yield followed by newline and slash is fine");
assertNoWarning(() => eval("(function () { yield; })"), SyntaxError,
"yield followed by semicolon in eval code is fine");

View File

@ -5949,7 +5949,8 @@ class MPow
}
bool writeRecoverData(CompactBufferWriter& writer) const override;
bool canRecoverOnBailout() const override {
return true;
// Temporarily disable recovery to relieve fuzzer pressure. See bug 1188586.
return false;
}
ALLOW_CLONE(MPow)

View File

@ -1021,11 +1021,10 @@ ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t len
return false;
if (!hole && !v.isNullOrUndefined()) {
if (Locale) {
JSObject* robj = ToObject(cx, v);
if (!robj)
RootedValue fun(cx);
if (!GetProperty(cx, v, cx->names().toLocaleString, &fun))
return false;
RootedId id(cx, NameToId(cx->names().toLocaleString));
if (!robj->callMethod(cx, id, 0, nullptr, &v))
if (!Invoke(cx, v, fun, 0, nullptr, &v))
return false;
}
if (!ValueToStringBuffer(cx, v, sb))

View File

@ -2586,8 +2586,10 @@ js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
} else {
// This is either a straight-up data property or (rarely) a
// property with a JSGetterOp/JSSetterOp. The latter must be
// reported to the caller as a plain data property, so don't
// populate desc.getter/setter, and mask away the SHARED bit.
// reported to the caller as a plain data property, so clear
// desc.getter/setter, and mask away the SHARED bit.
desc.setGetter(nullptr);
desc.setSetter(nullptr);
desc.attributesRef() &= ~JSPROP_SHARED;
if (IsImplicitDenseOrTypedArrayElement(shape)) {

View File

@ -1004,7 +1004,8 @@ GetObjectClassName(JSContext* cx, HandleObject obj);
*/
/*
* If obj a WindowProxy, return its current inner Window. Otherwise return obj.
* If obj is a WindowProxy, return its current inner Window. Otherwise return
* obj. This function can't fail and never returns nullptr.
*
* GetInnerObject is called when we need a scope chain; you never want a
* WindowProxy on a scope chain.
@ -1027,6 +1028,7 @@ GetInnerObject(JSObject* obj)
/*
* If obj is a Window object, return the WindowProxy. Otherwise return obj.
* This function can't fail; it never sets an exception or returns nullptr.
*
* This must be called before passing an object to script, if the object might
* be a Window. (But usually those cases involve scope objects, and for those,

View File

@ -355,14 +355,15 @@ SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
{
ObjectValueMap* map = mapObj->getMap();
if (!map) {
map = cx->new_<ObjectValueMap>(cx, mapObj.get());
if (!map)
AutoInitGCManagedObject<ObjectValueMap> newMap(
cx->make_unique<ObjectValueMap>(cx, mapObj.get()));
if (!newMap)
return false;
if (!map->init()) {
js_delete(map);
if (!newMap->init()) {
JS_ReportOutOfMemory(cx);
return false;
}
map = newMap.release();
mapObj->setPrivate(map);
}

View File

@ -88,17 +88,6 @@ js::assertEnteredPolicy(JSContext* cx, JSObject* proxy, jsid id,
}
#endif
#define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall) \
JS_BEGIN_MACRO \
RootedObject proto(cx); \
if (!GetPrototype(cx, proxy, &proto)) \
return false; \
if (!proto) \
return true; \
assertSameCompartment(cx, proxy, proto); \
return protoCall; \
JS_END_MACRO \
bool
Proxy::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
MutableHandle<PropertyDescriptor> desc)
@ -109,13 +98,12 @@ Proxy::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
if (!policy.allowed())
return policy.returnValue();
if (!handler->hasPrototype())
return handler->getPropertyDescriptor(cx, proxy, id, desc);
if (!handler->getOwnPropertyDescriptor(cx, proxy, id, desc))
return false;
if (desc.object())
return true;
INVOKE_ON_PROTOTYPE(cx, handler, proxy, GetPropertyDescriptor(cx, proto, id, desc));
// Special case. See the comment on BaseProxyHandler::mHasPrototype.
if (handler->hasPrototype())
return handler->BaseProxyHandler::getPropertyDescriptor(cx, proxy, id, desc);
return handler->getPropertyDescriptor(cx, proxy, id, desc);
}
bool
@ -241,16 +229,23 @@ Proxy::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
if (!policy.allowed())
return policy.returnValue();
if (!handler->hasPrototype())
return handler->has(cx, proxy, id, bp);
if (!handler->hasOwn(cx, proxy, id, bp))
return false;
if (*bp)
return true;
bool Bp;
INVOKE_ON_PROTOTYPE(cx, handler, proxy,
JS_HasPropertyById(cx, proto, id, &Bp) &&
((*bp = Bp) || true));
if (handler->hasPrototype()) {
if (!handler->hasOwn(cx, proxy, id, bp))
return false;
if (*bp)
return true;
RootedObject proto(cx);
if (!GetPrototype(cx, proxy, &proto))
return false;
if (!proto)
return true;
return HasProperty(cx, proto, id, bp);
}
return handler->has(cx, proxy, id, bp);
}
bool
@ -265,8 +260,18 @@ Proxy::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
return handler->hasOwn(cx, proxy, id, bp);
}
static Value
OuterizeValue(JSContext* cx, HandleValue v)
{
if (v.isObject()) {
RootedObject obj(cx, &v.toObject());
return ObjectValue(*GetOuterObject(cx, obj));
}
return v;
}
bool
Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id,
Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver_, HandleId id,
MutableHandleValue vp)
{
JS_CHECK_RECURSION(cx, return false);
@ -275,16 +280,26 @@ Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
if (!policy.allowed())
return policy.returnValue();
bool own;
if (!handler->hasPrototype()) {
own = true;
} else {
// Outerize the receiver. Proxy handlers shouldn't have to know about
// the Window/WindowProxy distinction.
RootedObject receiver(cx, GetOuterObject(cx, receiver_));
if (handler->hasPrototype()) {
bool own;
if (!handler->hasOwn(cx, proxy, id, &own))
return false;
if (!own) {
RootedObject proto(cx);
if (!GetPrototype(cx, proxy, &proto))
return false;
if (!proto)
return true;
return GetProperty(cx, proto, receiver, id, vp);
}
}
if (own)
return handler->get(cx, proxy, receiver, id, vp);
INVOKE_ON_PROTOTYPE(cx, handler, proxy, GetProperty(cx, proto, receiver, id, vp));
return handler->get(cx, proxy, receiver, id, vp);
}
bool
@ -307,7 +322,7 @@ Proxy::callProp(JSContext* cx, HandleObject proxy, HandleObject receiver, Handle
}
bool
Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver,
Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver_,
ObjectOpResult& result)
{
JS_CHECK_RECURSION(cx, return false);
@ -319,6 +334,10 @@ Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, Handle
return result.succeed();
}
// Outerize the receiver. Proxy handlers shouldn't have to know about
// the Window/WindowProxy distinction.
RootedValue receiver(cx, OuterizeValue(cx, receiver_));
// Special case. See the comment on BaseProxyHandler::mHasPrototype.
if (handler->hasPrototype())
return handler->BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
@ -343,33 +362,35 @@ Proxy::enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp)
JS_CHECK_RECURSION(cx, return false);
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
objp.set(nullptr); // default result if we refuse to perform this action
if (!handler->hasPrototype()) {
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
BaseProxyHandler::ENUMERATE, true);
// If the policy denies access but wants us to return true, we need
// to hand a valid (empty) iterator object to the caller.
if (!policy.allowed()) {
return policy.returnValue() &&
NewEmptyPropertyIterator(cx, 0, objp);
}
return handler->enumerate(cx, proxy, objp);
if (handler->hasPrototype()) {
AutoIdVector props(cx);
if (!Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props))
return false;
RootedObject proto(cx);
if (!GetPrototype(cx, proxy, &proto))
return false;
if (!proto)
return EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
assertSameCompartment(cx, proxy, proto);
AutoIdVector protoProps(cx);
return GetPropertyKeys(cx, proto, 0, &protoProps) &&
AppendUnique(cx, props, protoProps) &&
EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
}
AutoIdVector props(cx);
if (!Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props))
return false;
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
BaseProxyHandler::ENUMERATE, true);
RootedObject proto(cx);
if (!GetPrototype(cx, proxy, &proto))
return false;
if (!proto)
return EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
assertSameCompartment(cx, proxy, proto);
AutoIdVector protoProps(cx);
return GetPropertyKeys(cx, proto, 0, &protoProps) &&
AppendUnique(cx, props, protoProps) &&
EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
// If the policy denies access but wants us to return true, we need
// to hand a valid (empty) iterator object to the caller.
if (!policy.allowed()) {
return policy.returnValue() &&
NewEmptyPropertyIterator(cx, 0, objp);
}
return handler->enumerate(cx, proxy, objp);
}
bool

View File

@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(String.prototype, "toLocaleString", {
get() {
// Congratulations! You probably fixed primitive-this getters.
// Change "object" to "string".
assertEq(typeof this, "object");
return function() { return typeof this; };
}
})
assertEq(["test"].toLocaleString(), "string");
if (typeof reportCompare === "function")
reportCompare(true, true);

Some files were not shown because too many files have changed in this diff Show More