mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge inbound to m-c. a=merge
This commit is contained in:
commit
3af2c3a70e
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
|
@ -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>
|
@ -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>
|
@ -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 + ".");
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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 */
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"),
|
||||
|
@ -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:
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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.)
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -34,6 +34,9 @@ SplitAt(const char* aDelims,
|
||||
const nsACString& aInput,
|
||||
nsTArray<nsCString>& aOutTokens);
|
||||
|
||||
nsCString
|
||||
ToBase64(const nsTArray<uint8_t>& aBytes);
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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; \
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
[DEFAULT]
|
||||
tags = mcb
|
||||
support-files =
|
||||
file_bug803225_test_mailto.html
|
||||
file_frameNavigation.html
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -37,6 +37,7 @@ EXPORTS.mozilla.gfx += [
|
||||
'ScaleFactor.h',
|
||||
'ScaleFactors2D.h',
|
||||
'SourceSurfaceCairo.h',
|
||||
'StackArray.h',
|
||||
'Tools.h',
|
||||
'Types.h',
|
||||
'UserData.h',
|
||||
|
@ -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);
|
||||
|
@ -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(¶ms);
|
||||
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, ¶ms);
|
||||
} else {
|
||||
mSwapChain->Present(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
|
||||
|
@ -136,6 +136,9 @@ void
|
||||
MacIOSurfaceTextureSourceOGL::SetCompositor(Compositor* aCompositor)
|
||||
{
|
||||
mCompositor = static_cast<CompositorOGL*>(aCompositor);
|
||||
if (mNextSibling) {
|
||||
mNextSibling->SetCompositor(aCompositor);
|
||||
}
|
||||
}
|
||||
|
||||
gl::GLContext*
|
||||
|
@ -335,6 +335,9 @@ GLTextureSource::SetCompositor(Compositor* aCompositor)
|
||||
{
|
||||
MOZ_ASSERT(aCompositor);
|
||||
mCompositor = static_cast<CompositorOGL*>(aCompositor);
|
||||
if (mNextSibling) {
|
||||
mNextSibling->SetCompositor(aCompositor);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -157,8 +157,6 @@ DriverCrashGuard::GetGuardFile()
|
||||
void
|
||||
DriverCrashGuard::ActivateGuard()
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
mGuardActivated = true;
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -59,7 +59,6 @@ UNIFIED_SOURCES += [
|
||||
'Image.cpp',
|
||||
'ImageCacheKey.cpp',
|
||||
'ImageFactory.cpp',
|
||||
'ImageMetadata.cpp',
|
||||
'ImageOps.cpp',
|
||||
'ImageWrapper.cpp',
|
||||
'imgFrame.cpp',
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ TEST_HARNESS_FILES.gtest += [
|
||||
'green.ico',
|
||||
'green.jpg',
|
||||
'green.png',
|
||||
'no-frame-delay.gif',
|
||||
'rle4.bmp',
|
||||
'rle8.bmp',
|
||||
'transparent.bmp',
|
||||
|
BIN
image/test/gtest/no-frame-delay.gif
Normal file
BIN
image/test/gtest/no-frame-delay.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 317 B |
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
10
js/src/jit-test/tests/basic/bug1172503-2.js
Normal file
10
js/src/jit-test/tests/basic/bug1172503-2.js
Normal 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);
|
9
js/src/jit-test/tests/basic/bug1172503.js
Normal file
9
js/src/jit-test/tests/basic/bug1172503.js
Normal 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();
|
11
js/src/jit-test/tests/basic/bug1189744.js
Normal file
11
js/src/jit-test/tests/basic/bug1189744.js
Normal 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);
|
7
js/src/jit-test/tests/gc/oomInWeakMap.js
Normal file
7
js/src/jit-test/tests/gc/oomInWeakMap.js
Normal 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);`);
|
||||
});
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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)) {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
16
js/src/tests/ecma_6/Array/toLocaleString.js
Normal file
16
js/src/tests/ecma_6/Array/toLocaleString.js
Normal 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
Loading…
Reference in New Issue
Block a user