--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -2848,21 +2848,22 @@ nsIURL.store = new Map();
function parseQueryString(aQueryString) {
// Make sure there's at least one param available.
// Be careful here, params don't necessarily need to have values, so
// no need to verify the existence of a "=".
if (!aQueryString) {
return;
}
// Turn the params string into an array containing { name: value } tuples.
- let paramsArray = aQueryString.replace(/^[?&]/, "").split("&").map(e =>
- let (param = e.split("=")) {
+ let paramsArray = aQueryString.replace(/^[?&]/, "").split("&").map(e => {
+ let param = e.split("=");
+ return {
name: param[0] ? NetworkHelper.convertToUnicode(unescape(param[0])) : "",
value: param[1] ? NetworkHelper.convertToUnicode(unescape(param[1])) : ""
- });
+ }});
return paramsArray;
}
/**
* Parse text representation of multiple HTTP headers.
*
* @param string aText
* Text of headers
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -482,17 +482,16 @@ class Automation(object):
ldLibraryPath = ldLibraryPath + ":" + env[envVar]
env[envVar] = ldLibraryPath
elif self.IS_WIN32:
env["PATH"] = env["PATH"] + ";" + str(ldLibraryPath)
dmdLibrary = "dmd.dll"
preloadEnvVar = "MOZ_REPLACE_MALLOC_LIB"
if dmdPath and dmdLibrary and preloadEnvVar:
- env['DMD'] = '1'
env[preloadEnvVar] = os.path.join(dmdPath, dmdLibrary)
if crashreporter and not debugger:
env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
env['MOZ_CRASHREPORTER'] = '1'
else:
env['MOZ_CRASHREPORTER_DISABLE'] = '1'
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -384,17 +384,16 @@ def environment(xrePath, env=None, crash
preloadEnvVar = "MOZ_REPLACE_MALLOC_LIB"
if envVar:
envValue = ((env.get(envVar), str(ldLibraryPath))
if mozinfo.isWin
else (ldLibraryPath, dmdPath, env.get(envVar)))
env[envVar] = os.path.pathsep.join([path for path in envValue if path])
if dmdPath and dmdLibrary and preloadEnvVar:
- env['DMD'] = '1'
env[preloadEnvVar] = os.path.join(dmdPath, dmdLibrary)
# crashreporter
env['GNOME_DISABLE_CRASH_DIALOG'] = '1'
env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1'
if crashreporter and not debugger:
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -55,17 +55,16 @@ class RemoteAutomation(Automation):
# Set up what we need for the remote environment
def environment(self, env=None, xrePath=None, crashreporter=True, debugger=False, dmdPath=None, lsanPath=None):
# Because we are running remote, we don't want to mimic the local env
# so no copying of os.environ
if env is None:
env = {}
if dmdPath:
- env['DMD'] = '1'
env['MOZ_REPLACE_MALLOC_LIB'] = os.path.join(dmdPath, 'libdmd.so')
# Except for the mochitest results table hiding option, which isn't
# passed to runtestsremote.py as an actual option, but through the
# MOZ_HIDE_RESULTS_TABLE environment variable.
if 'MOZ_HIDE_RESULTS_TABLE' in os.environ:
env['MOZ_HIDE_RESULTS_TABLE'] = os.environ['MOZ_HIDE_RESULTS_TABLE']
--- a/docshell/shistory/src/nsSHEntryShared.cpp
+++ b/docshell/shistory/src/nsSHEntryShared.cpp
@@ -29,17 +29,17 @@ uint64_t gSHEntrySharedID = 0;
#define CONTENT_VIEWER_TIMEOUT_SECONDS "browser.sessionhistory.contentViewerTimeout"
// Default this to time out unused content viewers after 30 minutes
#define CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT (30*60)
typedef nsExpirationTracker<nsSHEntryShared, 3> HistoryTrackerBase;
class HistoryTracker MOZ_FINAL : public HistoryTrackerBase {
public:
- HistoryTracker(uint32_t aTimeout)
+ explicit HistoryTracker(uint32_t aTimeout)
: HistoryTrackerBase(1000 * aTimeout / 2)
{
}
protected:
virtual void NotifyExpired(nsSHEntryShared *aObj) {
RemoveObject(aObj);
aObj->Expire();
--- a/dom/base/ImportManager.cpp
+++ b/dom/base/ImportManager.cpp
@@ -621,16 +621,19 @@ ImportLoader::OnStartRequest(nsIRequest*
DocumentFlavorHTML);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
// The imported document must know which master document it belongs to.
mDocument = do_QueryInterface(importDoc);
nsCOMPtr<nsIDocument> master = mImportParent->MasterDocument();
mDocument->SetMasterDocument(master);
+ // We want to inherit the sandbox flags from the master document.
+ mDocument->SetSandboxFlags(master->GetSandboxFlags());
+
// We have to connect the blank document we created with the channel we opened,
// and create its own LoadGroup for it.
nsCOMPtr<nsIStreamListener> listener;
nsCOMPtr<nsILoadGroup> loadGroup;
channel->GetLoadGroup(getter_AddRefs(loadGroup));
nsCOMPtr<nsILoadGroup> newLoadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
newLoadGroup->SetLoadGroup(loadGroup);
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -59,16 +59,17 @@ EXPORTS += [
'nsContentPolicyUtils.h',
'nsContentSink.h',
'nsContentTypeParser.h',
'nsContentUtils.h',
'nsCopySupport.h',
'nsCrossSiteListenerProxy.h',
'nsDeprecatedOperationList.h',
'nsDocElementCreatedNotificationRunner.h',
+ 'nsDocumentWarningList.h',
'nsDOMAttributeMap.h',
'nsDOMCID.h',
'nsDOMClassInfoClasses.h',
'nsDOMClassInfoID.h',
'nsDOMJSUtils.h',
'nsDOMNavigationTiming.h',
'nsDOMString.h',
'nsElementFrameLoaderOwner.h',
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -425,18 +425,17 @@ NS_INTERFACE_MAP_BEGIN(nsDOMClassInfo)
else
NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIClassInfo)
NS_INTERFACE_MAP_END
static const JSClass sDOMConstructorProtoClass = {
- "DOM Constructor.prototype", 0,
- nullptr, nullptr, JS_PropertyStub, JS_StrictPropertyStub
+ "DOM Constructor.prototype", 0
};
static const char *
CutPrefix(const char *aName) {
static const char prefix_nsIDOM[] = "nsIDOM";
static const char prefix_nsI[] = "nsI";
@@ -999,19 +998,19 @@ nsDOMClassInfo::SetProperty(nsIXPConnect
NS_IMETHODIMP
nsDOMClassInfo::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, bool *_retval)
{
return NS_OK;
}
NS_IMETHODIMP
-nsDOMClassInfo::NewEnumerate(nsIXPConnectWrappedNative *wrapper,
- JSContext *cx, JSObject *obj, uint32_t enum_op,
- jsval *statep, jsid *idp, bool *_retval)
+nsDOMClassInfo::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+ JSObject *obj, JS::AutoIdVector &properties,
+ bool *_retval)
{
NS_WARNING("nsDOMClassInfo::NewEnumerate Don't call me!");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
nsDOMClassInfo::Resolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
@@ -2046,18 +2045,17 @@ nsWindowSH::NameStructEnabled(JSContext*
}
#ifdef RELEASE_BUILD
#define USE_CONTROLLERS_SHIM
#endif
#ifdef USE_CONTROLLERS_SHIM
static const JSClass ControllersShimClass = {
- "XULControllers", 0,
- nullptr, nullptr, JS_PropertyStub, JS_StrictPropertyStub
+ "XULControllers", 0
};
#endif
// static
nsresult
nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc)
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3560,17 +3560,22 @@ nsDocument::SetBaseURI(nsIURI* aURI)
}
// Check if CSP allows this base-uri
nsCOMPtr<nsIContentSecurityPolicy> csp;
nsresult rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
NS_ENSURE_SUCCESS(rv, rv);
if (csp) {
bool permitsBaseURI = false;
- rv = csp->PermitsBaseURI(aURI, &permitsBaseURI);
+
+ // base-uri is only enforced if explicitly defined in the
+ // policy - do *not* consult default-src, see:
+ // http://www.w3.org/TR/CSP2/#directive-default-src
+ rv = csp->Permits(aURI, nsIContentSecurityPolicy::BASE_URI_DIRECTIVE,
+ true, &permitsBaseURI);
NS_ENSURE_SUCCESS(rv, rv);
if (!permitsBaseURI) {
return NS_OK;
}
}
if (aURI) {
mDocumentBaseURI = NS_TryToMakeImmutable(aURI);
@@ -10170,44 +10175,79 @@ nsDocument::FindImageMap(const nsAString
return map->AsElement();
}
}
return nullptr;
}
#define DEPRECATED_OPERATION(_op) #_op "Warning",
-static const char* kWarnings[] = {
+static const char* kDeprecationWarnings[] = {
#include "nsDeprecatedOperationList.h"
nullptr
};
#undef DEPRECATED_OPERATION
+#define DOCUMENT_WARNING(_op) #_op "Warning",
+static const char* kDocumentWarnings[] = {
+#include "nsDocumentWarningList.h"
+ nullptr
+};
+#undef DOCUMENT_WARNING
+
bool
nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation)
{
static_assert(eDeprecatedOperationCount <= 64,
"Too many deprecated operations");
- return mWarnedAbout & (1ull << aOperation);
+ return mDeprecationWarnedAbout & (1ull << aOperation);
}
void
nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
bool asError /* = false */)
{
if (HasWarnedAbout(aOperation)) {
return;
}
- mWarnedAbout |= (1ull << aOperation);
+ mDeprecationWarnedAbout |= (1ull << aOperation);
uint32_t flags = asError ? nsIScriptError::errorFlag
: nsIScriptError::warningFlag;
nsContentUtils::ReportToConsole(flags,
NS_LITERAL_CSTRING("DOM Core"), this,
nsContentUtils::eDOM_PROPERTIES,
- kWarnings[aOperation]);
+ kDeprecationWarnings[aOperation]);
+}
+
+bool
+nsIDocument::HasWarnedAbout(DocumentWarnings aWarning)
+{
+ static_assert(eDocumentWarningCount <= 64,
+ "Too many document warnings");
+ return mDocWarningWarnedAbout & (1ull << aWarning);
+}
+
+void
+nsIDocument::WarnOnceAbout(DocumentWarnings aWarning,
+ bool asError /* = false */,
+ const char16_t **aParams /* = nullptr */,
+ uint32_t aParamsLength /* = 0 */)
+{
+ if (HasWarnedAbout(aWarning)) {
+ return;
+ }
+ mDocWarningWarnedAbout |= (1ull << aWarning);
+ uint32_t flags = asError ? nsIScriptError::errorFlag
+ : nsIScriptError::warningFlag;
+ nsContentUtils::ReportToConsole(flags,
+ NS_LITERAL_CSTRING("DOM Core"), this,
+ nsContentUtils::eDOM_PROPERTIES,
+ kDocumentWarnings[aWarning],
+ aParams,
+ aParamsLength);
}
nsresult
nsDocument::AddImage(imgIRequest* aImage)
{
NS_ENSURE_ARG_POINTER(aImage);
// See if the image is already in the hashtable. If it is, get the old count.
new file mode 100644
--- /dev/null
+++ b/dom/base/nsDocumentWarningList.h
@@ -0,0 +1,12 @@
+
+/* 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/. */
+// IWYU pragma: private, include "nsIDocument.h"
+
+/*
+ * This file contains the list of document DOM operations warnings. It is
+ * designed to be used as input to the C preprocessor *only*.
+ */
+
+DOCUMENT_WARNING(WillChangeBudget)
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -905,17 +905,18 @@ nsFrameLoader::ShowRemoteFrame(const nsI
mRemoteBrowserInitialized = true;
}
} else {
nsIntRect dimensions;
NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false);
// Don't show remote iframe if we are waiting for the completion of reflow.
if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
- mRemoteBrowser->UpdateDimensions(dimensions, size);
+ nsIntPoint chromeDisp = aFrame->GetChromeDisplacement();
+ mRemoteBrowser->UpdateDimensions(dimensions, size, chromeDisp);
}
}
return true;
}
void
nsFrameLoader::Hide()
@@ -1937,17 +1938,18 @@ nsFrameLoader::GetWindowDimensions(nsInt
NS_IMETHODIMP
nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame *aIFrame)
{
if (mRemoteFrame) {
if (mRemoteBrowser) {
nsIntSize size = aIFrame->GetSubdocumentSize();
nsIntRect dimensions;
NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE);
- mRemoteBrowser->UpdateDimensions(dimensions, size);
+ nsIntPoint chromeDisp = aIFrame->GetChromeDisplacement();
+ mRemoteBrowser->UpdateDimensions(dimensions, size, chromeDisp);
}
return NS_OK;
}
return UpdateBaseWindowPositionAndSize(aIFrame);
}
nsresult
nsFrameLoader::UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame)
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2098,16 +2098,28 @@ public:
enum DeprecatedOperations {
#include "nsDeprecatedOperationList.h"
eDeprecatedOperationCount
};
#undef DEPRECATED_OPERATION
bool HasWarnedAbout(DeprecatedOperations aOperation);
void WarnOnceAbout(DeprecatedOperations aOperation, bool asError = false);
+#define DOCUMENT_WARNING(_op) e##_op,
+ enum DocumentWarnings {
+#include "nsDocumentWarningList.h"
+ eDocumentWarningCount
+ };
+#undef DOCUMENT_WARNING
+ bool HasWarnedAbout(DocumentWarnings aWarning);
+ void WarnOnceAbout(DocumentWarnings aWarning,
+ bool asError = false,
+ const char16_t **aParams = nullptr,
+ uint32_t aParamsLength = 0);
+
virtual void PostVisibilityUpdateEvent() = 0;
bool IsSyntheticDocument() const { return mIsSyntheticDocument; }
void SetNeedLayoutFlush() {
mNeedLayoutFlush = true;
if (mDisplayDocument) {
mDisplayDocument->SetNeedLayoutFlush();
@@ -2454,17 +2466,18 @@ public:
}
// FontFaceSource
mozilla::dom::FontFaceSet* GetFonts(mozilla::ErrorResult& aRv);
bool DidFireDOMContentLoaded() const { return mDidFireDOMContentLoaded; }
private:
- uint64_t mWarnedAbout;
+ uint64_t mDeprecationWarnedAbout;
+ uint64_t mDocWarningWarnedAbout;
SelectorCache mSelectorCache;
protected:
~nsIDocument();
nsPropertyTable* GetExtraPropertyTable(uint16_t aCategory);
// Never ever call this. Only call GetWindow!
virtual nsPIDOMWindow *GetWindowInternal() const = 0;
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -433,16 +433,18 @@ ParseTypeAttribute(const nsAString& aTyp
return true;
}
static bool
CSPAllowsInlineScript(nsIScriptElement *aElement, nsIDocument *aDocument)
{
nsCOMPtr<nsIContentSecurityPolicy> csp;
+ // Note: For imports NodePrincipal and the principal of the master are
+ // the same.
nsresult rv = aDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
NS_ENSURE_SUCCESS(rv, false);
if (!csp) {
// no CSP --> allow
return true;
}
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1193,18 +1193,18 @@ ResolvePrototypeOrConstructor(JSContext*
if (!protoOrIface) {
return false;
}
cacheOnHolder = true;
desc.object().set(wrapper);
desc.setAttributes(attrs);
- desc.setGetter(JS_PropertyStub);
- desc.setSetter(JS_StrictPropertyStub);
+ desc.setGetter(nullptr);
+ desc.setSetter(nullptr);
desc.value().set(JS::ObjectValue(*protoOrIface));
}
return JS_WrapPropertyDescriptor(cx, desc);
}
#ifdef DEBUG
static void
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -581,17 +581,16 @@ DOMInterfaces = {
},
'IDBMutableFile': {
'nativeType': 'mozilla::dom::indexedDB::IDBMutableFile',
},
'IDBObjectStore': {
'nativeType': 'mozilla::dom::indexedDB::IDBObjectStore',
- 'implicitJSContext': [ 'createIndex' ],
'binaryNames': {
'mozGetAll': 'getAll'
}
},
'IDBOpenDBRequest': {
'nativeType': 'mozilla::dom::indexedDB::IDBOpenDBRequest',
'headerFile': 'IDBRequest.h'
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -430,18 +430,18 @@ class CGDOMJSClass(CGThing):
return fill(
"""
static const DOMJSClass Class = {
{ "${name}",
${flags},
${addProperty}, /* addProperty */
nullptr, /* delProperty */
- JS_PropertyStub, /* getProperty */
- JS_StrictPropertyStub, /* setProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
${enumerate}, /* enumerate */
${resolve}, /* resolve */
nullptr, /* convert */
${finalize}, /* finalize */
${call}, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
${trace}, /* trace */
@@ -646,18 +646,18 @@ class CGPrototypeJSClass(CGThing):
return fill(
"""
static const DOMIfaceAndProtoJSClass PrototypeClass = {
{
"${name}Prototype",
JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
nullptr, /* addProperty */
nullptr, /* delProperty */
- JS_PropertyStub, /* getProperty */
- JS_StrictPropertyStub, /* setProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
nullptr, /* enumerate */
nullptr, /* resolve */
nullptr, /* convert */
nullptr, /* finalize */
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr, /* trace */
@@ -742,18 +742,18 @@ class CGInterfaceObjectJSClass(CGThing):
return fill(
"""
static const DOMIfaceAndProtoJSClass InterfaceObjectClass = {
{
"Function",
JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
nullptr, /* addProperty */
nullptr, /* delProperty */
- JS_PropertyStub, /* getProperty */
- JS_StrictPropertyStub, /* setProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
nullptr, /* enumerate */
nullptr, /* resolve */
nullptr, /* convert */
nullptr, /* finalize */
${ctorname}, /* call */
${hasInstance}, /* hasInstance */
${ctorname}, /* construct */
nullptr, /* trace */
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -690,17 +690,17 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Ca
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPattern, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPattern, Release)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext)
class CanvasDrawObserver
{
public:
- CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext);
+ explicit CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext);
// Only enumerate draw calls that could affect the heuristic
enum DrawCallType {
PutImageData,
GetImageData,
DrawImage
};
--- a/dom/canvas/WebGLBindableName.h
+++ b/dom/canvas/WebGLBindableName.h
@@ -49,19 +49,19 @@ protected:
/** Represents a GL name that can be bound to a target.
*/
template<typename T>
class WebGLBindableName
: public WebGLBindable<T>
{
public:
- WebGLBindableName(GLuint name)
+ explicit WebGLBindableName(GLuint aName)
: WebGLBindable<T>()
- , mGLName(name)
+ , mGLName(aName)
{ }
GLuint GLName() const { return mGLName; }
protected:
const GLuint mGLName;
};
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -3318,17 +3318,18 @@ WebGLContext::CompileShader(WebGLShader*
compiler = ShConstructCompiler(shader->ShaderType(),
SH_WEBGL_SPEC,
targetShaderSourceLanguage,
&resources);
int compileOptions = SH_VARIABLES |
SH_ENFORCE_PACKING_RESTRICTIONS |
SH_INIT_VARYINGS_WITHOUT_STATIC_USE |
- SH_OBJECT_CODE;
+ SH_OBJECT_CODE |
+ SH_LIMIT_CALL_STACK_DEPTH;
if (resources.MaxExpressionComplexity > 0) {
compileOptions |= SH_LIMIT_EXPRESSION_COMPLEXITY;
}
#ifndef XP_MACOSX
// We want to do this everywhere, but to do this on Mac, we need
// to do it only on Mac OSX > 10.6 as this causes the shader
--- a/dom/datastore/DataStoreDB.cpp
+++ b/dom/datastore/DataStoreDB.cpp
@@ -242,45 +242,45 @@ DataStoreDB::UpgradeSchema(nsIDOMEvent*
IDBDatabase* database = nullptr;
nsresult rv = UNWRAP_OBJECT(IDBDatabase, &result.toObject(), database);
if (NS_FAILED(rv)) {
NS_WARNING("Didn't get the object we expected!");
return rv;
}
{
- RootedDictionary<IDBObjectStoreParameters> params(cx);
+ IDBObjectStoreParameters params;
params.Init(NS_LITERAL_STRING("{ \"autoIncrement\": true }"));
nsRefPtr<IDBObjectStore> store =
- database->CreateObjectStore(cx, NS_LITERAL_STRING(DATASTOREDB_NAME),
+ database->CreateObjectStore(NS_LITERAL_STRING(DATASTOREDB_NAME),
params, error);
if (NS_WARN_IF(error.Failed())) {
return error.ErrorCode();
}
}
nsRefPtr<IDBObjectStore> store;
{
- RootedDictionary<IDBObjectStoreParameters> params(cx);
+ IDBObjectStoreParameters params;
params.Init(NS_LITERAL_STRING("{ \"autoIncrement\": true, \"keyPath\": \"internalRevisionId\" }"));
store =
- database->CreateObjectStore(cx, NS_LITERAL_STRING(DATASTOREDB_REVISION),
+ database->CreateObjectStore(NS_LITERAL_STRING(DATASTOREDB_REVISION),
params, error);
if (NS_WARN_IF(error.Failed())) {
return error.ErrorCode();
}
}
{
- RootedDictionary<IDBIndexParameters> params(cx);
+ IDBIndexParameters params;
params.Init(NS_LITERAL_STRING("{ \"unique\": true }"));
nsRefPtr<IDBIndex> index =
- store->CreateIndex(cx, NS_LITERAL_STRING(DATASTOREDB_REVISION_INDEX),
+ store->CreateIndex(NS_LITERAL_STRING(DATASTOREDB_REVISION_INDEX),
NS_LITERAL_STRING("revisionId"), params, error);
if (NS_WARN_IF(error.Failed())) {
return error.ErrorCode();
}
}
return NS_OK;
}
--- a/dom/geolocation/nsGeolocationSettings.h
+++ b/dom/geolocation/nsGeolocationSettings.h
@@ -52,17 +52,17 @@ enum GeolocationFuzzMethod {
#endif
/**
* Simple class for holding the geolocation settings values.
*/
class GeolocationSetting MOZ_FINAL {
public:
- GeolocationSetting(const nsString& aOrigin) :
+ explicit GeolocationSetting(const nsString& aOrigin) :
mFuzzMethod(GEO_ALA_TYPE_DEFAULT),
#ifdef MOZ_APPROX_LOCATION
mDistance(0),
#endif
mLatitude(0.0),
mLongitude(0.0),
mOrigin(aOrigin) {}
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -1628,17 +1628,22 @@ HTMLFormElement::GetActionURL(nsIURI** a
NS_ENSURE_SUCCESS(rv, rv);
// Check if CSP allows this form-action
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
NS_ENSURE_SUCCESS(rv, rv);
if (csp) {
bool permitsFormAction = true;
- rv = csp->PermitsFormAction(actionURL, &permitsFormAction);
+
+ // form-action is only enforced if explicitly defined in the
+ // policy - do *not* consult default-src, see:
+ // http://www.w3.org/TR/CSP2/#directive-default-src
+ rv = csp->Permits(actionURL, nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE,
+ true, &permitsFormAction);
NS_ENSURE_SUCCESS(rv, rv);
if (!permitsFormAction) {
rv = NS_ERROR_CSP_FORM_ACTION_VIOLATION;
}
}
//
// Assign to the output
--- a/dom/html/TimeRanges.cpp
+++ b/dom/html/TimeRanges.cpp
@@ -93,49 +93,49 @@ TimeRanges::GetEndTime()
{
if (mRanges.IsEmpty()) {
return -1.0;
}
return mRanges[mRanges.Length() - 1].mEnd;
}
void
-TimeRanges::Normalize()
+TimeRanges::Normalize(double aError)
{
if (mRanges.Length() >= 2) {
nsAutoTArray<TimeRange,4> normalized;
mRanges.Sort(CompareTimeRanges());
// This merges the intervals.
TimeRange current(mRanges[0]);
for (uint32_t i = 1; i < mRanges.Length(); i++) {
if (current.mStart <= mRanges[i].mStart &&
current.mEnd >= mRanges[i].mEnd) {
continue;
}
- if (current.mEnd >= mRanges[i].mStart) {
+ if (current.mEnd + aError >= mRanges[i].mStart) {
current.mEnd = mRanges[i].mEnd;
} else {
normalized.AppendElement(current);
current = mRanges[i];
}
}
normalized.AppendElement(current);
mRanges = normalized;
}
}
void
-TimeRanges::Union(const TimeRanges* aOtherRanges)
+TimeRanges::Union(const TimeRanges* aOtherRanges, double aError)
{
mRanges.AppendElements(aOtherRanges->mRanges);
- Normalize();
+ Normalize(aError);
}
void
TimeRanges::Intersection(const TimeRanges* aOtherRanges)
{
nsAutoTArray<TimeRange,4> intersection;
const nsTArray<TimeRange>& otherRanges = aOtherRanges->mRanges;
--- a/dom/html/TimeRanges.h
+++ b/dom/html/TimeRanges.h
@@ -37,20 +37,20 @@ public:
// Returns the start time of the first range, or -1 if no ranges exist.
double GetStartTime();
// Returns the end time of the last range, or -1 if no ranges exist.
double GetEndTime();
// See http://www.whatwg.org/html/#normalized-timeranges-object
- void Normalize();
+ void Normalize(double aError = 0.0);
// Mutate this TimeRange to be the union of this and aOtherRanges.
- void Union(const TimeRanges* aOtherRanges);
+ void Union(const TimeRanges* aOtherRanges, double aError);
// Mutate this TimeRange to be the intersection of this and aOtherRanges.
void Intersection(const TimeRanges* aOtherRanges);
JSObject* WrapObject(JSContext* aCx);
uint32_t Length() const
{
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -30,17 +30,17 @@ namespace mozilla {
static const char kRemoteBrowserPending[] = "remote-browser-pending";
static const char kInprocessBrowserShown[] = "inprocess-browser-shown";
class nsBrowserElement::BrowserShownObserver : public nsIObserver
, public nsSupportsWeakReference
{
public:
- BrowserShownObserver(nsBrowserElement* aBrowserElement);
+ explicit BrowserShownObserver(nsBrowserElement* aBrowserElement);
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
void AddObserver();
void RemoveObserver();
private:
virtual ~BrowserShownObserver();
// Weak reference to the browser element. nsBrowserElement has a
new file mode 100644
--- /dev/null
+++ b/dom/html/test/imports/file_CSP_sandbox.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<head>
+</head>
+<body>
+ <link rel="import" href="file_CSP_sandbox_import.html" id="import"></link>
+</body>
+
new file mode 100644
--- /dev/null
+++ b/dom/html/test/imports/file_CSP_sandbox_import.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+</head>
+<body>
+<script>
+ var scriptExecuted = true;
+</script>
+</body>
+
--- a/dom/html/test/imports/mochitest.ini
+++ b/dom/html/test/imports/mochitest.ini
@@ -33,23 +33,25 @@ support-files =
file_cycle_4_E.html
file_encoding.html
file_simple_import.html
file_blocking_DOMContentLoaded_A.html
file_blocking_DOMContentLoaded_B.html
file_blocking_DOMContentLoaded_C.html
file_blocking_DOMContentLoaded_D.html
file_element_upgrade.html
-
+ file_CSP_sandbox.html
+ file_CSP_sandbox_import.html
[test_cycle_1.html]
skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
[test_cycle_2.html]
skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
[test_cycle_3.html]
skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
[test_cycle_4.html]
skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
[test_blocking_DOMContentLoaded.html]
skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
[test_encoding.html]
[test_defaultView.html]
[test_element_upgrade.html]
+[test_CSP_sandbox.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/imports/test_CSP_sandbox.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1106713
+-->
+<head>
+ <title>Test for Bug 1106713</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1106713">Mozilla Bug 1106713</a>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function go() {
+ var ifr = document.getElementById('iframe1').contentWindow;
+ ok(!ifr.scriptExecuted, "script is not allowed to run");
+ SimpleTest.finish();
+ }
+
+ </script>
+ <iframe src='file_CSP_sandbox.html' sandbox="allow-same-origin" onload="go()" id="iframe1"></iframe>
+</body>
+</html>
+
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -14070,18 +14070,18 @@ CreateIndexOp::DoDatabaseWork(Transactio
return NS_OK;
}
const JSClass CreateIndexOp::ThreadLocalJSRuntime::kGlobalClass = {
"IndexedDBTransactionThreadGlobal",
JSCLASS_GLOBAL_FLAGS,
/* addProperty */ nullptr,
/* delProperty */ nullptr,
- /* getProperty */ JS_PropertyStub,
- /* setProperty */ JS_StrictPropertyStub,
+ /* getProperty */ nullptr,
+ /* setProperty */ nullptr,
/* enumerate */ nullptr,
/* resolve */ nullptr,
/* convert */ nullptr,
/* finalize */ nullptr,
/* call */ nullptr,
/* hasInstance */ nullptr,
/* construct */ nullptr,
/* trace */ JS_GlobalObjectTraceHook
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -443,17 +443,16 @@ IDBDatabase::GetOwnerDocument() const
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
return doc.forget();
}
return nullptr;
}
already_AddRefed<IDBObjectStore>
IDBDatabase::CreateObjectStore(
- JSContext* aCx,
const nsAString& aName,
const IDBObjectStoreParameters& aOptionalParameters,
ErrorResult& aRv)
{
AssertIsOnOwningThread();
IDBTransaction* transaction = IDBTransaction::GetCurrent();
@@ -461,17 +460,17 @@ IDBDatabase::CreateObjectStore(
transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
return nullptr;
}
MOZ_ASSERT(transaction->IsOpen());
KeyPath keyPath(0);
- if (NS_FAILED(KeyPath::Parse(aCx, aOptionalParameters.mKeyPath, &keyPath))) {
+ if (NS_FAILED(KeyPath::Parse(aOptionalParameters.mKeyPath, &keyPath))) {
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return nullptr;
}
nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
for (uint32_t count = objectStores.Length(), index = 0;
index < count;
index++) {
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -198,18 +198,17 @@ public:
nsPIDOMWindow*
GetParentObject() const;
already_AddRefed<DOMStringList>
ObjectStoreNames() const;
already_AddRefed<IDBObjectStore>
- CreateObjectStore(JSContext* aCx,
- const nsAString& aName,
+ CreateObjectStore(const nsAString& aName,
const IDBObjectStoreParameters& aOptionalParameters,
ErrorResult& aRv);
void
DeleteObjectStore(const nsAString& name, ErrorResult& aRv);
already_AddRefed<IDBTransaction>
Transaction(const nsAString& aStoreName,
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -837,21 +837,17 @@ ClearStructuredCloneBuffer(JSAutoStructu
aBuffer.clear();
}
}
} // anonymous namespace
const JSClass IDBObjectStore::sDummyPropJSClass = {
"IDBObjectStore Dummy",
- 0 /* flags */,
- nullptr /* addProperty */,
- nullptr /* delProperty */,
- JS_PropertyStub /* getProperty */,
- JS_StrictPropertyStub /* setProperty */
+ 0 /* flags */
};
IDBObjectStore::IDBObjectStore(IDBTransaction* aTransaction,
const ObjectStoreSpec* aSpec)
: mTransaction(aTransaction)
, mCachedKeyPath(JSVAL_VOID)
, mSpec(aSpec)
, mId(aSpec->metadata().id())
@@ -1645,57 +1641,54 @@ IDBObjectStore::Delete(JSContext* aCx,
IDB_PROFILER_STRING(Transaction()->Database()),
IDB_PROFILER_STRING(Transaction()),
IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange));
return request.forget();
}
already_AddRefed<IDBIndex>
-IDBObjectStore::CreateIndex(JSContext* aCx,
- const nsAString& aName,
+IDBObjectStore::CreateIndex(const nsAString& aName,
const nsAString& aKeyPath,
const IDBIndexParameters& aOptionalParameters,
ErrorResult& aRv)
{
AssertIsOnOwningThread();
KeyPath keyPath(0);
- if (NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath)) ||
+ if (NS_FAILED(KeyPath::Parse(aKeyPath, &keyPath)) ||
!keyPath.IsValid()) {
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return nullptr;
}
- return CreateIndexInternal(aCx, aName, keyPath, aOptionalParameters, aRv);
+ return CreateIndexInternal(aName, keyPath, aOptionalParameters, aRv);
}
already_AddRefed<IDBIndex>
-IDBObjectStore::CreateIndex(JSContext* aCx,
- const nsAString& aName,
+IDBObjectStore::CreateIndex(const nsAString& aName,
const Sequence<nsString >& aKeyPath,
const IDBIndexParameters& aOptionalParameters,
ErrorResult& aRv)
{
AssertIsOnOwningThread();
KeyPath keyPath(0);
if (aKeyPath.IsEmpty() ||
- NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath)) ||
+ NS_FAILED(KeyPath::Parse(aKeyPath, &keyPath)) ||
!keyPath.IsValid()) {
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return nullptr;
}
- return CreateIndexInternal(aCx, aName, keyPath, aOptionalParameters, aRv);
+ return CreateIndexInternal(aName, keyPath, aOptionalParameters, aRv);
}
already_AddRefed<IDBIndex>
IDBObjectStore::CreateIndexInternal(
- JSContext* aCx,
const nsAString& aName,
const KeyPath& aKeyPath,
const IDBIndexParameters& aOptionalParameters,
ErrorResult& aRv)
{
AssertIsOnOwningThread();
IDBTransaction* transaction = IDBTransaction::GetCurrent();
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -179,25 +179,23 @@ public:
already_AddRefed<IDBRequest>
Get(JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult& aRv);
already_AddRefed<IDBRequest>
Clear(ErrorResult& aRv);
already_AddRefed<IDBIndex>
- CreateIndex(JSContext* aCx,
- const nsAString& aName,
+ CreateIndex(const nsAString& aName,
const nsAString& aKeyPath,
const IDBIndexParameters& aOptionalParameters,
ErrorResult& aRv);
already_AddRefed<IDBIndex>
- CreateIndex(JSContext* aCx,
- const nsAString& aName,
+ CreateIndex(const nsAString& aName,
const Sequence<nsString>& aKeyPath,
const IDBIndexParameters& aOptionalParameters,
ErrorResult& aRv);
already_AddRefed<IDBIndex>
Index(const nsAString& aName, ErrorResult &aRv);
void
@@ -293,18 +291,17 @@ private:
already_AddRefed<IDBRequest>
GetAllInternal(bool aKeysOnly,
JSContext* aCx,
JS::Handle<JS::Value> aKey,
const Optional<uint32_t>& aLimit,
ErrorResult& aRv);
already_AddRefed<IDBIndex>
- CreateIndexInternal(JSContext* aCx,
- const nsAString& aName,
+ CreateIndexInternal(const nsAString& aName,
const KeyPath& aKeyPath,
const IDBIndexParameters& aOptionalParameters,
ErrorResult& aRv);
already_AddRefed<IDBRequest>
OpenCursorInternal(bool aKeysOnly,
JSContext* aCx,
JS::Handle<JS::Value> aRange,
--- a/dom/indexedDB/KeyPath.cpp
+++ b/dom/indexedDB/KeyPath.cpp
@@ -9,16 +9,17 @@
#include "Key.h"
#include "ReportInternalError.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsJSUtils.h"
#include "xpcpublic.h"
#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/IDBObjectStoreBinding.h"
namespace mozilla {
namespace dom {
namespace indexedDB {
namespace {
inline
@@ -26,39 +27,30 @@ bool
IgnoreWhitespace(char16_t c)
{
return false;
}
typedef nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> KeyPathTokenizer;
bool
-IsValidKeyPathString(JSContext* aCx, const nsAString& aKeyPath)
+IsValidKeyPathString(const nsAString& aKeyPath)
{
NS_ASSERTION(!aKeyPath.IsVoid(), "What?");
KeyPathTokenizer tokenizer(aKeyPath, '.');
while (tokenizer.hasMoreTokens()) {
nsString token(tokenizer.nextToken());
if (!token.Length()) {
return false;
}
- JS::Rooted<JS::Value> stringVal(aCx);
- if (!xpc::StringToJsval(aCx, token, &stringVal)) {
- return false;
- }
-
- NS_ASSERTION(stringVal.toString(), "This should never happen");
- JS::Rooted<JSString*> str(aCx, stringVal.toString());
-
- bool isIdentifier = false;
- if (!JS_IsIdentifier(aCx, str, &isIdentifier) || !isIdentifier) {
+ if (!JS_IsIdentifier(token.get(), token.Length())) {
return false;
}
}
// If the very last character was a '.', the tokenizer won't give us an empty
// token, but the keyPath is still invalid.
if (!aKeyPath.IsEmpty() &&
aKeyPath.CharAt(aKeyPath.Length() - 1) == '.') {
@@ -78,17 +70,17 @@ GetJSValFromKeyPathString(JSContext* aCx
const JS::Value& aValue,
const nsAString& aKeyPathString,
JS::Value* aKeyJSVal,
KeyExtractionOptions aOptions,
KeyPath::ExtractOrCreateKeyCallback aCallback,
void* aClosure)
{
NS_ASSERTION(aCx, "Null pointer!");
- NS_ASSERTION(IsValidKeyPathString(aCx, aKeyPathString),
+ NS_ASSERTION(IsValidKeyPathString(aKeyPathString),
"This will explode!");
NS_ASSERTION(!(aCallback || aClosure) || aOptions == CreateProperties,
"This is not allowed!");
NS_ASSERTION(aOptions != CreateProperties || aCallback,
"If properties are created, there must be a callback!");
nsresult rv = NS_OK;
*aKeyJSVal = aValue;
@@ -224,118 +216,83 @@ GetJSValFromKeyPathString(JSContext* aCx
NS_ENSURE_SUCCESS(rv, rv);
return rv;
}
} // anonymous namespace
// static
nsresult
-KeyPath::Parse(JSContext* aCx, const nsAString& aString, KeyPath* aKeyPath)
+KeyPath::Parse(const nsAString& aString, KeyPath* aKeyPath)
{
KeyPath keyPath(0);
keyPath.SetType(STRING);
- if (!keyPath.AppendStringWithValidation(aCx, aString)) {
+ if (!keyPath.AppendStringWithValidation(aString)) {
return NS_ERROR_FAILURE;
}
*aKeyPath = keyPath;
return NS_OK;
}
//static
nsresult
-KeyPath::Parse(JSContext* aCx, const mozilla::dom::Sequence<nsString>& aStrings,
- KeyPath* aKeyPath)
+KeyPath::Parse(const Sequence<nsString>& aStrings, KeyPath* aKeyPath)
{
KeyPath keyPath(0);
keyPath.SetType(ARRAY);
for (uint32_t i = 0; i < aStrings.Length(); ++i) {
- if (!keyPath.AppendStringWithValidation(aCx, aStrings[i])) {
+ if (!keyPath.AppendStringWithValidation(aStrings[i])) {
return NS_ERROR_FAILURE;
}
}
*aKeyPath = keyPath;
return NS_OK;
}
// static
nsresult
-KeyPath::Parse(JSContext* aCx, const JS::Value& aValue_, KeyPath* aKeyPath)
+KeyPath::Parse(const Nullable<OwningStringOrStringSequence>& aValue, KeyPath* aKeyPath)
{
- JS::Rooted<JS::Value> aValue(aCx, aValue_);
KeyPath keyPath(0);
aKeyPath->SetType(NONEXISTENT);
- // See if this is a JS array.
- if (JS_IsArrayObject(aCx, aValue)) {
-
- JS::Rooted<JSObject*> obj(aCx, aValue.toObjectOrNull());
-
- uint32_t length;
- if (!JS_GetArrayLength(aCx, obj, &length)) {
- return NS_ERROR_FAILURE;
- }
-
- if (!length) {
- return NS_ERROR_FAILURE;
- }
-
- keyPath.SetType(ARRAY);
-
- for (uint32_t index = 0; index < length; index++) {
- JS::Rooted<JS::Value> val(aCx);
- JSString* jsstr;
- nsAutoJSString str;
- if (!JS_GetElement(aCx, obj, index, &val) ||
- !(jsstr = JS::ToString(aCx, val)) ||
- !str.init(aCx, jsstr)) {
- return NS_ERROR_FAILURE;
- }
-
- if (!keyPath.AppendStringWithValidation(aCx, str)) {
- return NS_ERROR_FAILURE;
- }
- }
- }
- // Otherwise convert it to a string.
- else if (!aValue.isNull() && !aValue.isUndefined()) {
- JSString* jsstr;
- nsAutoJSString str;
- if (!(jsstr = JS::ToString(aCx, aValue)) ||
- !str.init(aCx, jsstr)) {
- return NS_ERROR_FAILURE;
- }
-
- keyPath.SetType(STRING);
-
- if (!keyPath.AppendStringWithValidation(aCx, str)) {
- return NS_ERROR_FAILURE;
- }
+ if (aValue.IsNull()) {
+ *aKeyPath = keyPath;
+ return NS_OK;
}
- *aKeyPath = keyPath;
- return NS_OK;
+ if (aValue.Value().IsString()) {
+ return Parse(aValue.Value().GetAsString(), aKeyPath);
+ }
+
+ MOZ_ASSERT(aValue.Value().IsStringSequence());
+
+ const Sequence<nsString>& seq = aValue.Value().GetAsStringSequence();
+ if (seq.Length() == 0) {
+ return NS_ERROR_FAILURE;
+ }
+ return Parse(seq, aKeyPath);
}
void
KeyPath::SetType(KeyPathType aType)
{
mType = aType;
mStrings.Clear();
}
bool
-KeyPath::AppendStringWithValidation(JSContext* aCx, const nsAString& aString)
+KeyPath::AppendStringWithValidation(const nsAString& aString)
{
- if (!IsValidKeyPathString(aCx, aString)) {
+ if (!IsValidKeyPathString(aString)) {
return false;
}
if (IsString()) {
NS_ASSERTION(mStrings.Length() == 0, "Too many strings!");
mStrings.AppendElement(aString);
return true;
}
--- a/dom/indexedDB/KeyPath.h
+++ b/dom/indexedDB/KeyPath.h
@@ -3,19 +3,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_indexeddb_keypath_h__
#define mozilla_dom_indexeddb_keypath_h__
#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/Nullable.h"
namespace mozilla {
namespace dom {
+
+class OwningStringOrStringSequence;
+
namespace indexedDB {
class IndexMetadata;
class Key;
class ObjectStoreMetadata;
class KeyPath
{
@@ -34,18 +38,17 @@ public:
NONEXISTENT,
STRING,
ARRAY,
ENDGUARD
};
void SetType(KeyPathType aType);
- // This does not set exceptions.
- bool AppendStringWithValidation(JSContext* aCx, const nsAString& aString);
+ bool AppendStringWithValidation(const nsAString& aString);
explicit KeyPath(int aDummy)
: mType(NONEXISTENT)
{
MOZ_COUNT_CTOR(KeyPath);
}
KeyPath(const KeyPath& aOther)
@@ -55,23 +58,23 @@ public:
}
~KeyPath()
{
MOZ_COUNT_DTOR(KeyPath);
}
static nsresult
- Parse(JSContext* aCx, const nsAString& aString, KeyPath* aKeyPath);
+ Parse(const nsAString& aString, KeyPath* aKeyPath);
static nsresult
- Parse(JSContext* aCx, const Sequence<nsString>& aStrings, KeyPath* aKeyPath);
+ Parse(const Sequence<nsString>& aStrings, KeyPath* aKeyPath);
static nsresult
- Parse(JSContext* aCx, const JS::Value& aValue, KeyPath* aKeyPath);
+ Parse(const Nullable<OwningStringOrStringSequence>& aValue, KeyPath* aKeyPath);
nsresult
ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey) const;
nsresult
ExtractKeyAsJSVal(JSContext* aCx, const JS::Value& aValue,
JS::Value* aOutVal) const;
index c41c53ee7058b5a62e8cf1ee971c0da5851392e8..c19a2ef5d69c27a29ca7104e7313222960815abc
GIT binary patch
literal 54089
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
Vhj!XIG_ij;4$;uizQFz8{{wUV_^bc`
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -13,19 +13,45 @@ interface nsIURI;
/**
* nsIContentSecurityPolicy
* Describes an XPCOM component used to model and enforce CSPs. Instances of
* this class may have multiple policies within them, but there should only be
* one of these per document/principal.
*/
-[scriptable, uuid(dc86b261-5e41-4cab-ace3-a0278f5a7ec7)]
+typedef unsigned short CSPDirective;
+
+[scriptable, uuid(69b7663e-117a-4a3b-81bd-d86420b7c79e)]
interface nsIContentSecurityPolicy : nsISerializable
{
+ /**
+ * Directives supported by Content Security Policy. These are enums for
+ * the CSPDirective type.
+ * The NO_DIRECTIVE entry is used for checking default permissions and
+ * returning failure when asking CSP which directive to check.
+ *
+ * NOTE: When implementing a new directive, you will need to add it here but also
+ * add it to the CSPStrDirectives array in nsCSPUtils.h.
+ */
+ const unsigned short NO_DIRECTIVE = 0;
+ const unsigned short DEFAULT_SRC_DIRECTIVE = 1;
+ const unsigned short SCRIPT_SRC_DIRECTIVE = 2;
+ const unsigned short OBJECT_SRC_DIRECTIVE = 3;
+ const unsigned short STYLE_SRC_DIRECTIVE = 4;
+ const unsigned short IMG_SRC_DIRECTIVE = 5;
+ const unsigned short MEDIA_SRC_DIRECTIVE = 6;
+ const unsigned short FRAME_SRC_DIRECTIVE = 7;
+ const unsigned short FONT_SRC_DIRECTIVE = 8;
+ const unsigned short CONNECT_SRC_DIRECTIVE = 9;
+ const unsigned short REPORT_URI_DIRECTIVE = 10;
+ const unsigned short FRAME_ANCESTORS_DIRECTIVE = 11;
+ const unsigned short REFLECTED_XSS_DIRECTIVE = 12;
+ const unsigned short BASE_URI_DIRECTIVE = 13;
+ const unsigned short FORM_ACTION_DIRECTIVE = 14;
/**
* Accessor method for a read-only string version of the policy at a given
* index.
*/
AString getPolicy(in unsigned long index);
/**
@@ -191,34 +217,39 @@ interface nsIContentSecurityPolicy : nsI
* containing the protected resource
* @return
* true if the frame's ancestors are all allowed by policy (except for
* report-only policies, which will send reports and then return true
* here when violated).
*/
boolean permitsAncestry(in nsIDocShell docShell);
- /**
- * Whether this policy allows setting the document's base URI to
- * a given value.
- *
- * @return
- * Whether or not the provided URI is allowed to be used as the
- * document's base URI. (block the setting if false).
- */
- boolean permitsBaseURI(in nsIURI aURI);
/**
- * Whether this policy allows submitting HTML forms to a given URI.
+ * Checks if a specific directive permits loading of a URI.
+ *
+ * NOTE: Calls to this may trigger violation reports when queried, so the
+ * return value should not be cached.
*
+ * @param aURI
+ * The URI about to be loaded or used.
+ * @param aDir
+ * The CSPDirective to query (see above constants *_DIRECTIVE).
+ * @param aSpecific
+ * If "true" and the directive is specified to fall back to "default-src"
+ * when it's not explicitly provided, directivePermits will NOT try
+ * default-src when the specific directive is not used. Setting this to
+ * "false" allows CSP to fall back to default-src. This function
+ * behaves the same for both values of canUseDefault when querying
+ * directives that don't fall-back.
* @return
- * Whether or not the provided URI is allowed to be used as a
- * form's action URI.
+ * Whether or not the provided URI is allowed by CSP under the given
+ * directive. (block the pending operation if false).
*/
- boolean permitsFormAction(in nsIURI aURI);
+ boolean permits(in nsIURI aURI, in CSPDirective aDir, in boolean aSpecific);
/**
* Delegate method called by the service when sub-elements of the protected
* document are being loaded. Given a bit of information about the request,
* decides whether or not the policy is satisfied.
*
* Calls to this may trigger violation reports when queried, so
* this value should not be cached.
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -435,17 +435,18 @@ child:
TextureFactoryIdentifier textureFactoryIdentifier,
uint64_t layersId,
nullable PRenderFrame renderFrame);
LoadURL(nsCString uri);
CacheFileDescriptor(nsString path, FileDescriptor fd);
- UpdateDimensions(nsIntRect rect, nsIntSize size, ScreenOrientation orientation) compress;
+ UpdateDimensions(nsIntRect rect, nsIntSize size, ScreenOrientation orientation,
+ nsIntPoint chromeDisp) compress;
UpdateFrame(FrameMetrics frame);
// The following methods correspond to functions on the GeckoContentController
// interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation
// in that file for these functions.
AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration);
HandleDoubleTap(CSSPoint point, ScrollableLayerGuid aGuid);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -753,17 +753,17 @@ private:
};
class TabChild::DelayedDeleteRunnable MOZ_FINAL
: public nsRunnable
{
nsRefPtr<TabChild> mTabChild;
public:
- DelayedDeleteRunnable(TabChild* aTabChild)
+ explicit DelayedDeleteRunnable(TabChild* aTabChild)
: mTabChild(aTabChild)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aTabChild);
}
private:
~DelayedDeleteRunnable()
@@ -2005,23 +2005,25 @@ TabChild::RecvShow(const nsIntSize& aSiz
#endif
bool res = InitTabChildGlobal();
ApplyShowInfo(aInfo);
return res;
}
bool
-TabChild::RecvUpdateDimensions(const nsIntRect& rect, const nsIntSize& size, const ScreenOrientation& orientation)
+TabChild::RecvUpdateDimensions(const nsIntRect& rect, const nsIntSize& size,
+ const ScreenOrientation& orientation, const nsIntPoint& chromeDisp)
{
if (!mRemoteFrame) {
return true;
}
mOuterRect = rect;
+ mChromeDisp = chromeDisp;
bool initialSizing = !HasValidInnerSize()
&& (size.width != 0 && size.height != 0);
if (initialSizing) {
mHasValidInnerSize = true;
}
mOrientation = orientation;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -319,17 +319,18 @@ public:
virtual bool RecvShow(const nsIntSize& aSize,
const ShowInfo& aInfo,
const ScrollingBehavior& aScrolling,
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
const uint64_t& aLayersId,
PRenderFrameChild* aRenderFrame) MOZ_OVERRIDE;
virtual bool RecvUpdateDimensions(const nsIntRect& rect,
const nsIntSize& size,
- const ScreenOrientation& orientation) MOZ_OVERRIDE;
+ const ScreenOrientation& orientation,
+ const nsIntPoint& chromeDisp) MOZ_OVERRIDE;
virtual bool RecvUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
virtual bool RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
const uint32_t& aScrollGeneration) MOZ_OVERRIDE;
virtual bool RecvHandleDoubleTap(const CSSPoint& aPoint,
const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
virtual bool RecvHandleSingleTap(const CSSPoint& aPoint,
const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
virtual bool RecvHandleLongTap(const CSSPoint& aPoint,
@@ -490,16 +491,18 @@ public:
/**
* Native widget remoting protocol for use with windowed plugins with e10s.
*/
PPluginWidgetChild* AllocPPluginWidgetChild() MOZ_OVERRIDE;
bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) MOZ_OVERRIDE;
already_AddRefed<nsIWidget> CreatePluginWidget(nsIWidget* aParent);
+ nsIntPoint GetChromeDisplacement() { return mChromeDisp; };
+
protected:
virtual ~TabChild();
virtual PRenderFrameChild* AllocPRenderFrameChild() MOZ_OVERRIDE;
virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) MOZ_OVERRIDE;
virtual bool RecvDestroy() MOZ_OVERRIDE;
virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) MOZ_OVERRIDE;
virtual bool RecvSetIsDocShellActive(const bool& aIsActive) MOZ_OVERRIDE;
@@ -648,16 +651,18 @@ private:
bool mTouchEndCancelled;
bool mEndTouchIsClick;
bool mIgnoreKeyPressEvent;
nsRefPtr<ActiveElementManager> mActiveElementManager;
bool mHasValidInnerSize;
bool mDestroyed;
+ // Position of tab, relative to parent widget (typically the window)
+ nsIntPoint mChromeDisp;
TabId mUniqueId;
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
};
}
}
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -613,33 +613,34 @@ TabParent::Show(const nsIntSize& size)
bool isPrivate = mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
info = ShowInfo(name, allowFullscreen, isPrivate);
}
unused << SendShow(size, info, scrolling, textureFactoryIdentifier, layersId, renderFrame);
}
void
-TabParent::UpdateDimensions(const nsIntRect& rect, const nsIntSize& size)
+TabParent::UpdateDimensions(const nsIntRect& rect, const nsIntSize& size,
+ const nsIntPoint& aChromeDisp)
{
if (mIsDestroyed) {
return;
}
hal::ScreenConfiguration config;
hal::GetCurrentScreenConfiguration(&config);
ScreenOrientation orientation = config.orientation();
if (!mUpdatedDimensions || mOrientation != orientation ||
mDimensions != size || !mRect.IsEqualEdges(rect)) {
mUpdatedDimensions = true;
mRect = rect;
mDimensions = size;
mOrientation = orientation;
- unused << SendUpdateDimensions(mRect, mDimensions, mOrientation);
+ unused << SendUpdateDimensions(mRect, mDimensions, mOrientation, aChromeDisp);
}
}
void
TabParent::UpdateFrame(const FrameMetrics& aFrameMetrics)
{
if (!mIsDestroyed) {
unused << SendUpdateFrame(aFrameMetrics);
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -220,17 +220,18 @@ public:
AllocPColorPickerParent(const nsString& aTitle, const nsString& aInitialColor) MOZ_OVERRIDE;
virtual bool DeallocPColorPickerParent(PColorPickerParent* aColorPicker) MOZ_OVERRIDE;
void LoadURL(nsIURI* aURI);
// XXX/cjones: it's not clear what we gain by hiding these
// message-sending functions under a layer of indirection and
// eating the return values
void Show(const nsIntSize& size);
- void UpdateDimensions(const nsIntRect& rect, const nsIntSize& size);
+ void UpdateDimensions(const nsIntRect& rect, const nsIntSize& size,
+ const nsIntPoint& chromeDisp);
void UpdateFrame(const layers::FrameMetrics& aFrameMetrics);
void UIResolutionChanged();
void AcknowledgeScrollUpdate(const ViewID& aScrollId, const uint32_t& aScrollGeneration);
void HandleDoubleTap(const CSSPoint& aPoint,
int32_t aModifiers,
const ScrollableLayerGuid& aGuid);
void HandleSingleTap(const CSSPoint& aPoint,
int32_t aModifiers,
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -205,9 +205,11 @@ KeyNameAppsWarning=KeyboardEvent.key val
KeyNameFastFwdWarning=KeyboardEvent.key value "FastFwd" is obsolete and will be renamed to "MediaFastForward". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Zoom" and "ZoomToggle".
KeyNameZoomWarning=KeyboardEvent.key value "Zoom" is obsolete and will be renamed to "ZoomToggle". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
# LOCALIZATION NOTE: Do not translate "KeyboardEvent.key" and "Dead".
KeyNameDeadKeysWarning=KeyboardEvent.key values starting with "Dead" are obsolete and will be merged into just "Dead". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
ImportXULIntoContentWarning=Importing XUL nodes into a content document is deprecated. This functionality may be removed soon.
XMLDocumentLoadPrincipalMismatch=Use of document.load forbidden on Documents that come from other Windows. Only the Window in which a Document was created is allowed to call .load on that Document. Preferably, use XMLHttpRequest instead.
# LOCALIZATION NOTE: Do not translate "IndexedDB".
-IndexedDBTransactionAbortNavigation=An IndexedDB transaction that was not yet complete has been aborted due to page navigation.
\ No newline at end of file
+IndexedDBTransactionAbortNavigation=An IndexedDB transaction that was not yet complete has been aborted due to page navigation.
+# LOCALIZATION NOTE (WillChangeBudgetWarning): Do not translate Will-change, %1$S,%2$S,%3$S are numbers.
+WillChangeBudgetWarning=Will-change memory consumption is too high. Surface area covers %1$S pixels, budget is the document surface area multiplied by %2$S (%3$S pixels). All occurences of will-change in the document are ignored when over budget.
--- a/dom/media/MediaDataDecodedListener.h
+++ b/dom/media/MediaDataDecodedListener.h
@@ -25,47 +25,16 @@ public:
: mMonitor("MediaDataDecodedListener")
, mTaskQueue(aTaskQueue)
, mTarget(aTarget)
{
MOZ_ASSERT(aTarget);
MOZ_ASSERT(aTaskQueue);
}
- virtual void OnAudioDecoded(AudioData* aSample) MOZ_OVERRIDE {
- MonitorAutoLock lock(mMonitor);
- if (!mTarget || !mTaskQueue) {
- // We've been shutdown, abort.
- return;
- }
- RefPtr<nsIRunnable> task(new DeliverAudioTask(aSample, mTarget));
- mTaskQueue->Dispatch(task);
- }
-
- virtual void OnVideoDecoded(VideoData* aSample) MOZ_OVERRIDE {
- MonitorAutoLock lock(mMonitor);
- if (!mTarget || !mTaskQueue) {
- // We've been shutdown, abort.
- return;
- }
- RefPtr<nsIRunnable> task(new DeliverVideoTask(aSample, mTarget));
- mTaskQueue->Dispatch(task);
- }
-
- virtual void OnNotDecoded(MediaData::Type aType,
- MediaDecoderReader::NotDecodedReason aReason) MOZ_OVERRIDE {
- MonitorAutoLock lock(mMonitor);
- if (!mTarget || !mTaskQueue) {
- // We've been shutdown, abort.
- return;
- }
- RefPtr<nsIRunnable> task(new DeliverNotDecodedTask(aType, aReason, mTarget));
- mTaskQueue->Dispatch(task);
- }
-
void BreakCycles() {
MonitorAutoLock lock(mMonitor);
mTarget = nullptr;
mTaskQueue = nullptr;
}
virtual void OnSeekCompleted(nsresult aResult) MOZ_OVERRIDE {
MonitorAutoLock lock(mMonitor);
@@ -77,90 +46,16 @@ public:
&Target::OnSeekCompleted,
aResult));
if (NS_FAILED(mTaskQueue->Dispatch(task))) {
NS_WARNING("Failed to dispatch OnSeekCompleted task");
}
}
private:
-
- class DeliverAudioTask : public nsRunnable {
- public:
- DeliverAudioTask(AudioData* aSample, Target* aTarget)
- : mSample(aSample)
- , mTarget(aTarget)
- {
- MOZ_COUNT_CTOR(DeliverAudioTask);
- }
- protected:
- ~DeliverAudioTask()
- {
- MOZ_COUNT_DTOR(DeliverAudioTask);
- }
- public:
- NS_METHOD Run() {
- mTarget->OnAudioDecoded(mSample);
- return NS_OK;
- }
- private:
- nsRefPtr<AudioData> mSample;
- RefPtr<Target> mTarget;
- };
-
- class DeliverVideoTask : public nsRunnable {
- public:
- DeliverVideoTask(VideoData* aSample, Target* aTarget)
- : mSample(aSample)
- , mTarget(aTarget)
- {
- MOZ_COUNT_CTOR(DeliverVideoTask);
- }
- protected:
- ~DeliverVideoTask()
- {
- MOZ_COUNT_DTOR(DeliverVideoTask);
- }
- public:
- NS_METHOD Run() {
- mTarget->OnVideoDecoded(mSample);
- return NS_OK;
- }
- private:
- nsRefPtr<VideoData> mSample;
- RefPtr<Target> mTarget;
- };
-
- class DeliverNotDecodedTask : public nsRunnable {
- public:
- DeliverNotDecodedTask(MediaData::Type aType,
- MediaDecoderReader::NotDecodedReason aReason,
- Target* aTarget)
- : mType(aType)
- , mReason(aReason)
- , mTarget(aTarget)
- {
- MOZ_COUNT_CTOR(DeliverNotDecodedTask);
- }
- protected:
- ~DeliverNotDecodedTask()
- {
- MOZ_COUNT_DTOR(DeliverNotDecodedTask);
- }
- public:
- NS_METHOD Run() {
- mTarget->OnNotDecoded(mType, mReason);
- return NS_OK;
- }
- private:
- MediaData::Type mType;
- MediaDecoderReader::NotDecodedReason mReason;
- RefPtr<Target> mTarget;
- };
-
Monitor mMonitor;
RefPtr<MediaTaskQueue> mTaskQueue;
RefPtr<Target> mTarget;
};
} /* namespace mozilla */
#endif // MediaDataDecodedListener_h_
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -68,16 +68,17 @@ public:
size_t mSize;
};
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
: mAudioCompactor(mAudioQueue)
, mDecoder(aDecoder)
, mIgnoreAudioOutputFormat(false)
, mStartTime(-1)
+ , mHitAudioDecodeError(false)
, mTaskQueueIsBorrowed(false)
, mAudioDiscontinuity(false)
, mVideoDiscontinuity(false)
, mShutdown(false)
{
MOZ_COUNT_CTOR(MediaDecoderReader);
EnsureMediaPromiseLog();
}
@@ -183,79 +184,88 @@ public:
mReader->RequestVideoData(skip, mTimeThreshold);
return NS_OK;
}
private:
nsRefPtr<MediaDecoderReader> mReader;
int64_t mTimeThreshold;
};
-void
+nsRefPtr<MediaDecoderReader::VideoDataPromise>
MediaDecoderReader::RequestVideoData(bool aSkipToNextKeyframe,
int64_t aTimeThreshold)
{
+ nsRefPtr<VideoDataPromise> p = mBaseVideoPromise.Ensure(__func__);
bool skip = aSkipToNextKeyframe;
while (VideoQueue().GetSize() == 0 &&
!VideoQueue().IsFinished()) {
if (!DecodeVideoFrame(skip, aTimeThreshold)) {
VideoQueue().Finish();
} else if (skip) {
// We still need to decode more data in order to skip to the next
// keyframe. Post another task to the decode task queue to decode
// again. We don't just decode straight in a loop here, as that
// would hog the decode task queue.
RefPtr<nsIRunnable> task(new RequestVideoWithSkipTask(this, aTimeThreshold));
mTaskQueue->Dispatch(task);
- return;
+ return p;
}
}
if (VideoQueue().GetSize() > 0) {
nsRefPtr<VideoData> v = VideoQueue().PopFront();
if (v && mVideoDiscontinuity) {
v->mDiscontinuity = true;
mVideoDiscontinuity = false;
}
- GetCallback()->OnVideoDecoded(v);
+ mBaseVideoPromise.Resolve(v, __func__);
} else if (VideoQueue().IsFinished()) {
- GetCallback()->OnNotDecoded(MediaData::VIDEO_DATA, END_OF_STREAM);
+ mBaseVideoPromise.Reject(END_OF_STREAM, __func__);
+ } else {
+ MOZ_ASSERT(false, "Dropping this promise on the floor");
}
+
+ return p;
}
-void
+nsRefPtr<MediaDecoderReader::AudioDataPromise>
MediaDecoderReader::RequestAudioData()
{
+ nsRefPtr<AudioDataPromise> p = mBaseAudioPromise.Ensure(__func__);
while (AudioQueue().GetSize() == 0 &&
!AudioQueue().IsFinished()) {
if (!DecodeAudioData()) {
AudioQueue().Finish();
break;
}
// AudioQueue size is still zero, post a task to try again. Don't spin
// waiting in this while loop since it somehow prevents audio EOS from
// coming in gstreamer 1.x when there is still video buffer waiting to be
// consumed. (|mVideoSinkBufferCount| > 0)
if (AudioQueue().GetSize() == 0 && mTaskQueue) {
RefPtr<nsIRunnable> task(NS_NewRunnableMethod(
this, &MediaDecoderReader::RequestAudioData));
mTaskQueue->Dispatch(task.forget());
- return;
+ return p;
}
}
if (AudioQueue().GetSize() > 0) {
nsRefPtr<AudioData> a = AudioQueue().PopFront();
if (mAudioDiscontinuity) {
a->mDiscontinuity = true;
mAudioDiscontinuity = false;
}
- GetCallback()->OnAudioDecoded(a);
- return;
+ mBaseAudioPromise.Resolve(a, __func__);
} else if (AudioQueue().IsFinished()) {
- GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, END_OF_STREAM);
- return;
+ mBaseAudioPromise.Reject(mHitAudioDecodeError ? DECODE_ERROR : END_OF_STREAM, __func__);
+ mHitAudioDecodeError = false;
+ } else {
+ MOZ_ASSERT(false, "Dropping this promise on the floor");
}
+
+ return p;
}
void
MediaDecoderReader::SetCallback(RequestSampleCallback* aCallback)
{
mSampleDecodedCallback = aCallback;
}
@@ -283,16 +293,20 @@ MediaDecoderReader::BreakCycles()
mTaskQueue = nullptr;
}
nsRefPtr<ShutdownPromise>
MediaDecoderReader::Shutdown()
{
MOZ_ASSERT(OnDecodeThread());
mShutdown = true;
+
+ mBaseAudioPromise.RejectIfExists(END_OF_STREAM, __func__);
+ mBaseVideoPromise.RejectIfExists(END_OF_STREAM, __func__);
+
ReleaseMediaResources();
nsRefPtr<ShutdownPromise> p;
// Spin down the task queue if necessary. We wait until BreakCycles to null
// out mTaskQueue, since otherwise any remaining tasks could crash when they
// invoke GetTaskQueue()->IsCurrentThreadIn().
if (mTaskQueue && !mTaskQueueIsBorrowed) {
// If we own our task queue, shutdown ends when the task queue is done.
@@ -302,71 +316,9 @@ MediaDecoderReader::Shutdown()
// asynchronously).
p = new ShutdownPromise(__func__);
p->Resolve(true, __func__);
}
return p;
}
-AudioDecodeRendezvous::AudioDecodeRendezvous()
- : mMonitor("AudioDecodeRendezvous")
- , mHaveResult(false)
-{
-}
-
-AudioDecodeRendezvous::~AudioDecodeRendezvous()
-{
-}
-
-void
-AudioDecodeRendezvous::OnAudioDecoded(AudioData* aSample)
-{
- MonitorAutoLock mon(mMonitor);
- mSample = aSample;
- mStatus = NS_OK;
- mHaveResult = true;
- mon.NotifyAll();
-}
-
-void
-AudioDecodeRendezvous::OnNotDecoded(MediaData::Type aType,
- MediaDecoderReader::NotDecodedReason aReason)
-{
- MOZ_ASSERT(aType == MediaData::AUDIO_DATA);
- MonitorAutoLock mon(mMonitor);
- mSample = nullptr;
- mStatus = aReason == MediaDecoderReader::DECODE_ERROR ? NS_ERROR_FAILURE : NS_OK;
- mHaveResult = true;
- mon.NotifyAll();
-}
-
-void
-AudioDecodeRendezvous::Reset()
-{
- MonitorAutoLock mon(mMonitor);
- mHaveResult = false;
- mStatus = NS_OK;
- mSample = nullptr;
-}
-
-nsresult
-AudioDecodeRendezvous::Await(nsRefPtr<AudioData>& aSample)
-{
- MonitorAutoLock mon(mMonitor);
- while (!mHaveResult) {
- mon.Wait();
- }
- mHaveResult = false;
- aSample = mSample;
- return mStatus;
-}
-
-void
-AudioDecodeRendezvous::Cancel()
-{
- MonitorAutoLock mon(mMonitor);
- mStatus = NS_ERROR_ABORT;
- mHaveResult = true;
- mon.NotifyAll();
-}
-
} // namespace mozilla
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -4,16 +4,17 @@
* 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/. */
#if !defined(MediaDecoderReader_h_)
#define MediaDecoderReader_h_
#include "AbstractMediaDecoder.h"
#include "MediaInfo.h"
#include "MediaData.h"
+#include "MediaPromise.h"
#include "MediaQueue.h"
#include "AudioCompactor.h"
namespace mozilla {
namespace dom {
class TimeRanges;
}
@@ -32,16 +33,19 @@ class MediaDecoderReader {
public:
enum NotDecodedReason {
END_OF_STREAM,
DECODE_ERROR,
WAITING_FOR_DATA,
CANCELED
};
+ typedef MediaPromise<nsRefPtr<AudioData>, NotDecodedReason> AudioDataPromise;
+ typedef MediaPromise<nsRefPtr<VideoData>, NotDecodedReason> VideoDataPromise;
+
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReader)
explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder);
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
// on failure.
virtual nsresult Init(MediaDecoderReader* aCloneDonor) = 0;
@@ -88,32 +92,32 @@ public:
// outstanding Request*Data() calls after this is called. Calls to
// Request*Data() made after this should be processed as usual.
// Normally this call preceedes a Seek() call, or shutdown.
// The first samples of every stream produced after a ResetDecode() call
// *must* be marked as "discontinuities". If it's not, seeking work won't
// properly!
virtual nsresult ResetDecode();
- // Requests the Reader to call OnAudioDecoded() on aCallback with one
- // audio sample. The decode should be performed asynchronously, and
- // the callback can be performed on any thread. Don't hold the decoder
+ // Requests one audio sample from the reader.
+ //
+ // The decode should be performed asynchronously, and the promise should
+ // be resolved when it is complete. Don't hold the decoder
// monitor while calling this, as the implementation may try to wait
// on something that needs the monitor and deadlock.
- virtual void RequestAudioData();
+ virtual nsRefPtr<AudioDataPromise> RequestAudioData();
- // Requests the Reader to call OnVideoDecoded() on aCallback with one
- // video sample. The decode should be performed asynchronously, and
- // the callback can be performed on any thread. Don't hold the decoder
- // monitor while calling this, as the implementation may try to wait
- // on something that needs the monitor and deadlock.
+ // Requests one video sample from the reader.
+ //
+ // Don't hold the decoder monitor while calling this, as the implementation
+ // may try to wait on something that needs the monitor and deadlock.
// If aSkipToKeyframe is true, the decode should skip ahead to the
// the next keyframe at or after aTimeThreshold microseconds.
- virtual void RequestVideoData(bool aSkipToNextKeyframe,
- int64_t aTimeThreshold);
+ virtual nsRefPtr<VideoDataPromise>
+ RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold);
virtual bool HasAudio() = 0;
virtual bool HasVideo() = 0;
// A function that is called before ReadMetadata() call.
virtual void PreReadMetadata() {};
// Read header data for all bitstreams in the file. Fills aInfo with
@@ -270,20 +274,31 @@ protected:
// what we support.
bool mIgnoreAudioOutputFormat;
// The start time of the media, in microseconds. This is the presentation
// time of the first frame decoded from the media. This is initialized to -1,
// and then set to a value >= by MediaDecoderStateMachine::SetStartTime(),
// after which point it never changes.
int64_t mStartTime;
-private:
+
+ // This is a quick-and-dirty way for DecodeAudioData implementations to
+ // communicate the presence of a decoding error to RequestAudioData. We should
+ // replace this with a promise-y mechanism as we make this stuff properly
+ // async.
+ bool mHitAudioDecodeError;
+private:
nsRefPtr<RequestSampleCallback> mSampleDecodedCallback;
+ // Promises used only for the base-class (sync->async adapter) implementation
+ // of Request{Audio,Video}Data.
+ MediaPromiseHolder<AudioDataPromise> mBaseAudioPromise;
+ MediaPromiseHolder<VideoDataPromise> mBaseVideoPromise;
+
nsRefPtr<MediaTaskQueue> mTaskQueue;
bool mTaskQueueIsBorrowed;
// Flags whether a the next audio/video sample comes after a "gap" or
// "discontinuity" in the stream. For example after a seek.
bool mAudioDiscontinuity;
bool mVideoDiscontinuity;
bool mShutdown;
@@ -292,64 +307,20 @@ private:
// Interface that callers to MediaDecoderReader::Request{Audio,Video}Data()
// must implement to receive the requested samples asynchronously.
// This object is refcounted, and cycles must be broken by calling
// BreakCycles() during shutdown.
class RequestSampleCallback {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RequestSampleCallback)
- // Receives the result of a RequestAudioData() call.
- virtual void OnAudioDecoded(AudioData* aSample) = 0;
-
- // Receives the result of a RequestVideoData() call.
- virtual void OnVideoDecoded(VideoData* aSample) = 0;
-
- // Called when a RequestAudioData() or RequestVideoData() call can't be
- // fulfiled. The reason is passed as aReason.
- virtual void OnNotDecoded(MediaData::Type aType,
- MediaDecoderReader::NotDecodedReason aReason) = 0;
-
virtual void OnSeekCompleted(nsresult aResult) = 0;
// Called during shutdown to break any reference cycles.
virtual void BreakCycles() = 0;
protected:
virtual ~RequestSampleCallback() {}
};
-// A RequestSampleCallback implementation that can be passed to the
-// MediaDecoderReader to block the thread requesting an audio sample until
-// the audio decode is complete. This is used to adapt the asynchronous
-// model of the MediaDecoderReader to a synchronous model.
-class AudioDecodeRendezvous : public RequestSampleCallback {
-public:
- AudioDecodeRendezvous();
- ~AudioDecodeRendezvous();
-
- // RequestSampleCallback implementation. Called when decode is complete.
- // Note: aSample is null at end of stream.
- virtual void OnAudioDecoded(AudioData* aSample) MOZ_OVERRIDE;
- virtual void OnVideoDecoded(VideoData* aSample) MOZ_OVERRIDE {}
- virtual void OnNotDecoded(MediaData::Type aType,
- MediaDecoderReader::NotDecodedReason aReason) MOZ_OVERRIDE;
- virtual void OnSeekCompleted(nsresult aResult) MOZ_OVERRIDE {};
- virtual void BreakCycles() MOZ_OVERRIDE {};
- void Reset();
-
- // Returns failure on error, or NS_OK.
- // If *aSample is null, EOS has been reached.
- nsresult Await(nsRefPtr<AudioData>& aSample);
-
- // Interrupts a call to Wait().
- void Cancel();
-
-private:
- Monitor mMonitor;
- nsresult mStatus;
- nsRefPtr<AudioData> mSample;
- bool mHaveResult;
-};
-
} // namespace mozilla
#endif
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -618,17 +618,20 @@ MediaDecoderStateMachine::DecodeVideo()
currentTime = mState == DECODER_STATE_SEEKING ? 0 : GetMediaTime();
// Time the video decode, so that if it's slow, we can increase our low
// audio threshold to reduce the chance of an audio underrun while we're
// waiting for a video decode to complete.
mVideoDecodeStartTime = TimeStamp::Now();
}
- mReader->RequestVideoData(skipToNextKeyFrame, currentTime);
+ mReader->RequestVideoData(skipToNextKeyFrame, currentTime)
+ ->Then(DecodeTaskQueue(), __func__, this,
+ &MediaDecoderStateMachine::OnVideoDecoded,
+ &MediaDecoderStateMachine::OnVideoNotDecoded);
}
bool
MediaDecoderStateMachine::NeedToDecodeAudio()
{
AssertCurrentThreadInMonitor();
SAMPLE_LOG("NeedToDecodeAudio() isDec=%d decToTar=%d minPrl=%d seek=%d enufAud=%d",
IsAudioDecoding(), mDecodeToSeekTarget, mMinimizePreroll,
@@ -661,17 +664,19 @@ MediaDecoderStateMachine::DecodeAudio()
// We don't want to consider skipping to the next keyframe if we've
// only just started up the decode loop, so wait until we've decoded
// some audio data before enabling the keyframe skip logic on audio.
if (mIsAudioPrerolling &&
GetDecodedAudioDuration() >= mAudioPrerollUsecs * mPlaybackRate) {
mIsAudioPrerolling = false;
}
}
- mReader->RequestAudioData();
+ mReader->RequestAudioData()->Then(DecodeTaskQueue(), __func__, this,
+ &MediaDecoderStateMachine::OnAudioDecoded,
+ &MediaDecoderStateMachine::OnAudioNotDecoded);
}
bool
MediaDecoderStateMachine::IsAudioSeekComplete()
{
AssertCurrentThreadInMonitor();
SAMPLE_LOG("IsAudioSeekComplete() curTarVal=%d mAudDis=%d aqFin=%d aqSz=%d",
mCurrentSeekTarget.IsValid(), mDropAudioUntilNextDiscontinuity, AudioQueue().IsFinished(), AudioQueue().GetSize());
@@ -910,16 +915,17 @@ MediaDecoderStateMachine::MaybeFinishDec
if (NS_FAILED(FinishDecodeFirstFrame())) {
DecodeError();
}
}
void
MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
{
+ MOZ_ASSERT(OnDecodeThread());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
nsRefPtr<VideoData> video(aVideoSample);
mVideoRequestPending = false;
SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d",
(video ? video->mTime : -1),
(video ? video->GetEndTime() : -1),
(video ? video->mDiscontinuity : 0));
@@ -2123,22 +2129,27 @@ MediaDecoderStateMachine::DecodeFirstFra
NS_ENSURE_SUCCESS(res, res);
} else if (mDecodingFrozenAtStateMetadata) {
SetStartTime(mStartTime);
nsresult res = FinishDecodeFirstFrame();
NS_ENSURE_SUCCESS(res, res);
} else {
if (HasAudio()) {
ReentrantMonitorAutoExit unlock(mDecoder->GetReentrantMonitor());
- mReader->RequestAudioData();
+ mReader->RequestAudioData()->Then(DecodeTaskQueue(), __func__, this,
+ &MediaDecoderStateMachine::OnAudioDecoded,
+ &MediaDecoderStateMachine::OnAudioNotDecoded);
}
if (HasVideo()) {
mVideoDecodeStartTime = TimeStamp::Now();
ReentrantMonitorAutoExit unlock(mDecoder->GetReentrantMonitor());
- mReader->RequestVideoData(false, 0);
+ mReader->RequestVideoData(false, 0)
+ ->Then(DecodeTaskQueue(), __func__, this,
+ &MediaDecoderStateMachine::OnVideoDecoded,
+ &MediaDecoderStateMachine::OnVideoNotDecoded);
}
}
return NS_OK;
}
nsresult
MediaDecoderStateMachine::FinishDecodeFirstFrame()
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -371,16 +371,27 @@ public:
// the state machine is free to return to prerolling normally. Note
// "prerolling" in this context refers to when we decode and buffer decoded
// samples in advance of when they're needed for playback.
void SetMinimizePrerollUntilPlaybackStarts();
void OnAudioDecoded(AudioData* aSample);
void OnVideoDecoded(VideoData* aSample);
void OnNotDecoded(MediaData::Type aType, MediaDecoderReader::NotDecodedReason aReason);
+ void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
+ {
+ MOZ_ASSERT(OnDecodeThread());
+ OnNotDecoded(MediaData::AUDIO_DATA, aReason);
+ }
+ void OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
+ {
+ MOZ_ASSERT(OnDecodeThread());
+ OnNotDecoded(MediaData::VIDEO_DATA, aReason);
+ }
+
void OnSeekCompleted(nsresult aResult);
private:
void AcquireMonitorAndInvokeDecodeError();
protected:
virtual ~MediaDecoderStateMachine();
--- a/dom/media/MediaDevices.h
+++ b/dom/media/MediaDevices.h
@@ -19,17 +19,18 @@ struct MediaStreamConstraints;
#define MOZILLA_DOM_MEDIADEVICES_IMPLEMENTATION_IID \
{ 0x2f784d8a, 0x7485, 0x4280, \
{ 0x9a, 0x36, 0x74, 0xa4, 0xd6, 0x71, 0xa6, 0xc8 } }
class MediaDevices MOZ_FINAL : public DOMEventTargetHelper
{
public:
- MediaDevices(nsPIDOMWindow* aWindow) : DOMEventTargetHelper(aWindow) {}
+ explicit MediaDevices(nsPIDOMWindow* aWindow) :
+ DOMEventTargetHelper(aWindow) {}
NS_DECL_ISUPPORTS_INHERITED
NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_MEDIADEVICES_IMPLEMENTATION_IID)
JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
already_AddRefed<Promise>
GetUserMedia(const MediaStreamConstraints& aConstraints, ErrorResult &aRv);
--- a/dom/media/MediaPromise.h
+++ b/dom/media/MediaPromise.h
@@ -21,17 +21,16 @@
#ifdef _MSC_VER
#define __func__ __FUNCTION__
#endif
class nsIEventTarget;
namespace mozilla {
extern PRLogModuleInfo* gMediaPromiseLog;
-void EnsureMediaPromiseLog();
#define PROMISE_LOG(x, ...) \
MOZ_ASSERT(gMediaPromiseLog); \
PR_LOG(gMediaPromiseLog, PR_LOG_DEBUG, (x, ##__VA_ARGS__))
class MediaTaskQueue;
namespace detail {
@@ -53,17 +52,17 @@ template<typename T> class MediaPromiseH
template<typename ResolveValueT, typename RejectValueT>
class MediaPromise
{
public:
typedef ResolveValueT ResolveValueType;
typedef RejectValueT RejectValueType;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPromise)
- MediaPromise(const char* aCreationSite)
+ explicit MediaPromise(const char* aCreationSite)
: mCreationSite(aCreationSite)
, mMutex("MediaPromise Mutex")
{
MOZ_COUNT_CTOR(MediaPromise);
PROMISE_LOG("%s creating MediaPromise (%p)", mCreationSite, this);
}
protected:
@@ -134,17 +133,17 @@ protected:
return NS_OK;
}
private:
ThenValueBase* mThenValue;
RejectValueType mRejectValue;
};
- ThenValueBase(const char* aCallSite) : mCallSite(aCallSite)
+ explicit ThenValueBase(const char* aCallSite) : mCallSite(aCallSite)
{
MOZ_COUNT_CTOR(ThenValueBase);
}
virtual void Dispatch(MediaPromise *aPromise) = 0;
protected:
// This may only be deleted by {Resolve,Reject}Runnable::Run.
@@ -256,18 +255,19 @@ public:
DispatchAll();
}
protected:
bool IsPending() { return mResolveValue.isNothing() && mRejectValue.isNothing(); }
void DispatchAll()
{
mMutex.AssertCurrentThreadOwns();
- for (size_t i = 0; i < mThenValues.Length(); ++i)
+ for (size_t i = 0; i < mThenValues.Length(); ++i) {
mThenValues[i]->Dispatch(this);
+ }
mThenValues.Clear();
for (size_t i = 0; i < mChainedPromises.Length(); ++i) {
ForwardTo(mChainedPromises[i]);
}
mChainedPromises.Clear();
}
--- a/dom/media/MediaStreamError.h
+++ b/dom/media/MediaStreamError.h
@@ -39,19 +39,19 @@ protected:
nsString mMessage;
const nsString mConstraintName;
};
class MediaMgrError MOZ_FINAL : public nsISupports,
public BaseMediaMgrError
{
public:
- MediaMgrError(const nsAString& aName,
- const nsAString& aMessage = EmptyString(),
- const nsAString& aConstraintName = EmptyString())
+ explicit MediaMgrError(const nsAString& aName,
+ const nsAString& aMessage = EmptyString(),
+ const nsAString& aConstraintName = EmptyString())
: BaseMediaMgrError(aName, aMessage, aConstraintName) {}
NS_DECL_THREADSAFE_ISUPPORTS
private:
~MediaMgrError() {}
};
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -1206,17 +1206,17 @@ public:
}
/**
* Returns graph sample rate in Hz.
*/
TrackRate GraphRate() const { return mSampleRate; }
protected:
- MediaStreamGraph(TrackRate aSampleRate)
+ explicit MediaStreamGraph(TrackRate aSampleRate)
: mNextGraphUpdateIndex(1)
, mSampleRate(aSampleRate)
{
MOZ_COUNT_CTOR(MediaStreamGraph);
}
virtual ~MediaStreamGraph()
{
MOZ_COUNT_DTOR(MediaStreamGraph);
--- a/dom/media/MediaTaskQueue.h
+++ b/dom/media/MediaTaskQueue.h
@@ -87,17 +87,18 @@ private:
// Monitor that protects the queue and mIsRunning;
Monitor mQueueMonitor;
struct TaskQueueEntry {
RefPtr<nsIRunnable> mRunnable;
bool mForceDispatch;
- TaskQueueEntry(TemporaryRef<nsIRunnable> aRunnable, bool aForceDispatch = false)
+ explicit TaskQueueEntry(TemporaryRef<nsIRunnable> aRunnable,
+ bool aForceDispatch = false)
: mRunnable(aRunnable), mForceDispatch(aForceDispatch) {}
};
// Queue of tasks to run.
std::queue<TaskQueueEntry> mTasks;
// The thread currently running the task queue. We store a reference
// to this so that IsCurrentThreadIn() can tell if the current thread
--- a/dom/media/eme/CDMCaps.cpp
+++ b/dom/media/eme/CDMCaps.cpp
@@ -3,16 +3,17 @@
/* 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 "CDMCaps.h"
#include "gmp-decryption.h"
#include "EMELog.h"
#include "nsThreadUtils.h"
+#include "SamplesWaitingForKey.h"
namespace mozilla {
CDMCaps::CDMCaps()
: mMonitor("CDMCaps")
, mCaps(0)
{
}
@@ -98,22 +99,17 @@ CDMCaps::AutoLock::SetKeyUsable(const Ce
return false;
}
mData.mUsableKeyIds.AppendElement(key);
auto& waiters = mData.mWaitForKeys;
size_t i = 0;
while (i < waiters.Length()) {
auto& w = waiters[i];
if (w.mKeyId == aKeyId) {
- if (waiters[i].mTarget) {
- EME_LOG("SetKeyUsable() notified waiter.");
- w.mTarget->Dispatch(w.mContinuation, NS_DISPATCH_NORMAL);
- } else {
- w.mContinuation->Run();
- }
+ w.mListener->NotifyUsable(aKeyId);
waiters.RemoveElementAt(i);
} else {
i++;
}
}
return true;
}
@@ -133,39 +129,23 @@ CDMCaps::AutoLock::SetKeyUnusable(const
keys.RemoveElementAt(i);
break;
}
}
return true;
}
void
-CDMCaps::AutoLock::CallWhenKeyUsable(const CencKeyId& aKey,
- nsIRunnable* aContinuation,
- nsIThread* aTarget)
+CDMCaps::AutoLock::NotifyWhenKeyIdUsable(const CencKeyId& aKey,
+ SamplesWaitingForKey* aListener)
{
mData.mMonitor.AssertCurrentThreadOwns();
MOZ_ASSERT(!IsKeyUsable(aKey));
- MOZ_ASSERT(aContinuation);
- mData.mWaitForKeys.AppendElement(WaitForKeys(aKey, aContinuation, aTarget));
-}
-
-void
-CDMCaps::AutoLock::DropKeysForSession(const nsAString& aSessionId)
-{
- mData.mMonitor.AssertCurrentThreadOwns();
- auto& keys = mData.mUsableKeyIds;
- size_t i = 0;
- while (i < keys.Length()) {
- if (keys[i].mSessionId == aSessionId) {
- keys.RemoveElementAt(i);
- } else {
- i++;
- }
- }
+ MOZ_ASSERT(aListener);
+ mData.mWaitForKeys.AppendElement(WaitForKeys(aKey, aListener));
}
bool
CDMCaps::AutoLock::AreCapsKnown()
{
mData.mMonitor.AssertCurrentThreadOwns();
return mData.mCaps != 0;
}
--- a/dom/media/eme/CDMCaps.h
+++ b/dom/media/eme/CDMCaps.h
@@ -8,21 +8,20 @@
#define CDMCaps_h_
#include "nsString.h"
#include "nsAutoPtr.h"
#include "mozilla/Monitor.h"
#include "nsIThread.h"
#include "nsTArray.h"
#include "mozilla/Attributes.h"
+#include "SamplesWaitingForKey.h"
namespace mozilla {
-typedef nsTArray<uint8_t> CencKeyId;
-
// CDM capabilities; what keys a CDMProxy can use, and whether it can decrypt, or
// decrypt-and-decode on a per stream basis. Must be locked to access state.
class CDMCaps {
public:
CDMCaps();
~CDMCaps();
// Locks the CDMCaps. It must be locked to access its shared state.
@@ -41,60 +40,52 @@ public:
// Returns true if setting this key usable results in the usable keys
// changing for this session, i.e. the key was not previously marked usable.
bool SetKeyUsable(const CencKeyId& aKeyId, const nsString& aSessionId);
// Returns true if setting this key unusable results in the usable keys
// changing for this session, i.e. the key was previously marked usable.
bool SetKeyUnusable(const CencKeyId& aKeyId, const nsString& aSessionId);
- void DropKeysForSession(const nsAString& aSessionId);
void GetUsableKeysForSession(const nsAString& aSessionId,
nsTArray<CencKeyId>& aOutKeyIds);
// Sets the capabilities of the CDM. aCaps is the logical OR of the
// GMP_EME_CAP_* flags from gmp-decryption.h.
void SetCaps(uint64_t aCaps);
bool CanDecryptAndDecodeAudio();
bool CanDecryptAndDecodeVideo();
bool CanDecryptAudio();
bool CanDecryptVideo();
void CallOnMainThreadWhenCapsAvailable(nsIRunnable* aContinuation);
- // Calls aContinuation on aTarget thread when key become usable.
- // Pass aTarget=nullptr and runnable will be called on the GMP thread
- // when key becomes usable.
- void CallWhenKeyUsable(const CencKeyId& aKey,
- nsIRunnable* aContinuation,
- nsIThread* aTarget = nullptr);
-
+ // Notifies the SamplesWaitingForKey when key become usable.
+ void NotifyWhenKeyIdUsable(const CencKeyId& aKey,
+ SamplesWaitingForKey* aSamplesWaiting);
private:
// Not taking a strong ref, since this should be allocated on the stack.
CDMCaps& mData;
};
private:
void Lock();
void Unlock();
bool HasCap(uint64_t);
struct WaitForKeys {
WaitForKeys(const CencKeyId& aKeyId,
- nsIRunnable* aContinuation,
- nsIThread* aTarget)
+ SamplesWaitingForKey* aListener)
: mKeyId(aKeyId)
- , mContinuation(aContinuation)
- , mTarget(aTarget)
+ , mListener(aListener)
{}
CencKeyId mKeyId;
- nsRefPtr<nsIRunnable> mContinuation;
- nsCOMPtr<nsIThread> mTarget;
+ nsRefPtr<SamplesWaitingForKey> mListener;
};
Monitor mMonitor;
struct UsableKey {
UsableKey(const CencKeyId& aId,
const nsString& aSessionId)
: mId(aId)
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -266,20 +266,16 @@ CDMProxy::gmp_UpdateSession(nsAutoPtr<Up
void
CDMProxy::CloseSession(const nsAString& aSessionId,
PromiseId aPromiseId)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
- {
- CDMCaps::AutoLock caps(Capabilites());
- caps.DropKeysForSession(aSessionId);
- }
nsAutoPtr<SessionOpData> data(new SessionOpData());
data->mPromiseId = aPromiseId;
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
nsRefPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_CloseSession, data));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
@@ -296,20 +292,16 @@ CDMProxy::gmp_CloseSession(nsAutoPtr<Ses
void
CDMProxy::RemoveSession(const nsAString& aSessionId,
PromiseId aPromiseId)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
- {
- CDMCaps::AutoLock caps(Capabilites());
- caps.DropKeysForSession(aSessionId);
- }
nsAutoPtr<SessionOpData> data(new SessionOpData());
data->mPromiseId = aPromiseId;
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
nsRefPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_RemoveSession, data));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
@@ -326,28 +318,30 @@ CDMProxy::gmp_RemoveSession(nsAutoPtr<Se
void
CDMProxy::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
mKeys.Clear();
// Note: This may end up being the last owning reference to the CDMProxy.
nsRefPtr<nsIRunnable> task(NS_NewRunnableMethod(this, &CDMProxy::gmp_Shutdown));
- mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+ if (mGMPThread) {
+ mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+ }
}
void
CDMProxy::gmp_Shutdown()
{
MOZ_ASSERT(IsOnGMPThread());
// Abort any pending decrypt jobs, to awaken any clients waiting on a job.
for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
DecryptJob* job = mDecryptionJobs[i];
- job->mClient->Decrypted(NS_ERROR_ABORT, nullptr);
+ job->mClient->Decrypted(GMPAbortedErr, nullptr);
}
mDecryptionJobs.Clear();
if (mCDM) {
mCDM->Close();
mCDM = nullptr;
}
}
@@ -445,17 +439,23 @@ CDMProxy::OnExpirationChange(const nsASt
MOZ_ASSERT(NS_IsMainThread());
NS_WARNING("CDMProxy::OnExpirationChange() not implemented");
}
void
CDMProxy::OnSessionClosed(const nsAString& aSessionId)
{
MOZ_ASSERT(NS_IsMainThread());
- NS_WARNING("CDMProxy::OnSessionClosed() not implemented");
+ if (mKeys.IsNull()) {
+ return;
+ }
+ nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->OnClosed();
+ }
}
static void
LogToConsole(const nsAString& aMsg)
{
nsCOMPtr<nsIConsoleService> console(
do_GetService("@mozilla.org/consoleservice;1"));
if (!console) {
@@ -517,17 +517,17 @@ CDMProxy::Decrypt(mp4_demuxer::MP4Sample
void
CDMProxy::gmp_Decrypt(nsAutoPtr<DecryptJob> aJob)
{
MOZ_ASSERT(IsOnGMPThread());
MOZ_ASSERT(aJob->mClient);
MOZ_ASSERT(aJob->mSample);
if (!mCDM) {
- aJob->mClient->Decrypted(NS_ERROR_FAILURE, nullptr);
+ aJob->mClient->Decrypted(GMPAbortedErr, nullptr);
return;
}
aJob->mId = ++mDecryptionJobCount;
nsTArray<uint8_t> data;
data.AppendElements(aJob->mSample->data, aJob->mSample->size);
mCDM->Decrypt(aJob->mId, aJob->mSample->crypto, data);
mDecryptionJobs.AppendElement(aJob.forget());
@@ -544,29 +544,36 @@ CDMProxy::gmp_Decrypted(uint32_t aId,
if (job->mId == aId) {
if (aDecryptedData.Length() != job->mSample->size) {
NS_WARNING("CDM returned incorrect number of decrypted bytes");
}
if (GMP_SUCCEEDED(aResult)) {
PodCopy(job->mSample->data,
aDecryptedData.Elements(),
std::min<size_t>(aDecryptedData.Length(), job->mSample->size));
- job->mClient->Decrypted(NS_OK, job->mSample.forget());
+ job->mClient->Decrypted(GMPNoErr, job->mSample.forget());
+ } else if (aResult == GMPNoKeyErr) {
+ NS_WARNING("CDM returned GMPNoKeyErr");
+ // We still have the encrypted sample, so we can re-enqueue it to be
+ // decrypted again once the key is usable again.
+ job->mClient->Decrypted(GMPNoKeyErr, job->mSample.forget());
} else {
- job->mClient->Decrypted(NS_ERROR_FAILURE, nullptr);
+ nsAutoCString str("CDM returned decode failure GMPErr=");
+ str.AppendInt(aResult);
+ NS_WARNING(str.get());
+ job->mClient->Decrypted(aResult, nullptr);
}
mDecryptionJobs.RemoveElementAt(i);
return;
- } else {
- NS_WARNING("GMPDecryptorChild returned incorrect job ID");
}
}
+ NS_WARNING("GMPDecryptorChild returned incorrect job ID");
}
void
CDMProxy::gmp_Terminated()
{
MOZ_ASSERT(IsOnGMPThread());
- EME_LOG("CDM terminated");
+ NS_WARNING("CDM terminated");
gmp_Shutdown();
}
} // namespace mozilla
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -22,17 +22,17 @@ class CDMCallbackProxy;
namespace dom {
class MediaKeySession;
}
class DecryptionClient {
public:
virtual ~DecryptionClient() {}
- virtual void Decrypted(nsresult aResult,
+ virtual void Decrypted(GMPErr aResult,
mp4_demuxer::MP4Sample* aSample) = 0;
};
// Proxies calls GMP/CDM, and proxies calls back.
// Note: Promises are passed in via a PromiseId, so that the ID can be
// passed via IPC to the CDM, which can then signal when to reject or
// resolve the promise using its PromiseId.
class CDMProxy {
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -199,17 +199,16 @@ MediaKeySession::Close(ErrorResult& aRv)
void
MediaKeySession::OnClosed()
{
if (IsClosed()) {
return;
}
mIsClosed = true;
- // TODO: reset usableKeyIds
mKeys->OnSessionClosed(this);
mKeys = nullptr;
mClosed->MaybeResolve(JS::UndefinedHandleValue);
}
bool
MediaKeySession::IsClosed() const
{
@@ -286,23 +285,15 @@ MediaKeySession::DispatchKeyError(uint32
}
void
MediaKeySession::DispatchKeysChange()
{
if (IsClosed()) {
return;
}
- DebugOnly<nsresult> rv =
- nsContentUtils::DispatchTrustedEvent(mKeys->GetOwnerDoc(),
- this,
- NS_LITERAL_STRING("keyschange"),
- false,
- false);
-#ifdef DEBUG
- if (NS_FAILED(rv)) {
- NS_WARNING("Failed to dispatch keyschange event");
- }
-#endif
+ nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
+ new AsyncEventDispatcher(this, NS_LITERAL_STRING("keyschange"), false);
+ asyncDispatcher->PostDOMEvent();
}
} // namespace dom
} // namespace mozilla
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -459,16 +459,10 @@ CopyArrayBufferViewOrArrayBufferData(con
bufferview.ComputeLengthAndData();
aOutData.AppendElements(bufferview.Data(), bufferview.Length());
} else {
return false;
}
return true;
}
-nsIDocument*
-MediaKeys::GetOwnerDoc() const
-{
- return mElement ? mElement->OwnerDoc() : nullptr;
-}
-
} // namespace dom
} // namespace mozilla
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -106,20 +106,16 @@ public:
// Returns true if this MediaKeys has been bound to a media element.
bool IsBoundToMediaElement() const;
// Return NS_OK if the principals are the same as when the MediaKeys
// was created, failure otherwise.
nsresult CheckPrincipals();
- // Returns a pointer to the bound media element's owner doc.
- // If we're not bound, this returns null.
- nsIDocument* GetOwnerDoc() const;
-
private:
bool IsInPrivateBrowsing();
// Removes promise from mPromises, and returns it.
already_AddRefed<Promise> RetrievePromise(PromiseId aId);
// Owning ref to proxy. The proxy has a weak reference back to the MediaKeys,
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -116,18 +116,18 @@ public:
}
private:
RefPtr<MediaResource> mResource;
};
MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
: MediaDecoderReader(aDecoder)
- , mAudio("MP4 audio decoder data", Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
- , mVideo("MP4 video decoder data", Preferences::GetUint("media.mp4-video-decode-ahead", 2))
+ , mAudio(MediaData::AUDIO_DATA, Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
+ , mVideo(MediaData::VIDEO_DATA, Preferences::GetUint("media.mp4-video-decode-ahead", 2))
, mLastReportedNumDecodedFrames(0)
, mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
, mDemuxerInitialized(false)
, mIsEncrypted(false)
, mIndexReady(false)
, mIndexMonitor("MP4 index")
{
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
@@ -149,26 +149,31 @@ MP4Reader::Shutdown()
mAudio.mDecoder->Shutdown();
mAudio.mDecoder = nullptr;
}
if (mAudio.mTaskQueue) {
mAudio.mTaskQueue->BeginShutdown();
mAudio.mTaskQueue->AwaitShutdownAndIdle();
mAudio.mTaskQueue = nullptr;
}
+ mAudio.mPromise.SetMonitor(nullptr);
+ MOZ_ASSERT(mAudio.mPromise.IsEmpty());
+
if (mVideo.mDecoder) {
Flush(kVideo);
mVideo.mDecoder->Shutdown();
mVideo.mDecoder = nullptr;
}
if (mVideo.mTaskQueue) {
mVideo.mTaskQueue->BeginShutdown();
mVideo.mTaskQueue->AwaitShutdownAndIdle();
mVideo.mTaskQueue = nullptr;
}
+ mVideo.mPromise.SetMonitor(nullptr);
+ MOZ_ASSERT(mVideo.mPromise.IsEmpty());
// Dispose of the queued sample before shutting down the demuxer
mQueuedVideoSample = nullptr;
if (mPlatform) {
mPlatform->Shutdown();
mPlatform = nullptr;
}
@@ -487,61 +492,70 @@ MP4Reader::HasVideo()
{
return mVideo.mActive;
}
MP4Reader::DecoderData&
MP4Reader::GetDecoderData(TrackType aTrack)
{
MOZ_ASSERT(aTrack == kAudio || aTrack == kVideo);
- return (aTrack == kAudio) ? mAudio : mVideo;
+ if (aTrack == kAudio) {
+ return mAudio;
+ }
+ return mVideo;
}
-void
+nsRefPtr<MediaDecoderReader::VideoDataPromise>
MP4Reader::RequestVideoData(bool aSkipToNextKeyframe,
int64_t aTimeThreshold)
{
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
VLOG("RequestVideoData skip=%d time=%lld", aSkipToNextKeyframe, aTimeThreshold);
// Record number of frames decoded and parsed. Automatically update the
// stats counters using the AutoNotifyDecoded stack-based class.
uint32_t parsed = 0, decoded = 0;
AbstractMediaDecoder::AutoNotifyDecoded autoNotify(mDecoder, parsed, decoded);
MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder);
+ bool eos = false;
if (aSkipToNextKeyframe) {
- if (!SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed) ||
- NS_FAILED(mVideo.mDecoder->Flush())) {
+ eos = !SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed);
+ if (!eos && NS_FAILED(mVideo.mDecoder->Flush())) {
NS_WARNING("Failed to skip/flush video when skipping-to-next-keyframe.");
}
}
- auto& decoder = GetDecoderData(kVideo);
- MonitorAutoLock lock(decoder.mMonitor);
- decoder.mOutputRequested = true;
- ScheduleUpdate(kVideo);
+ MonitorAutoLock lock(mVideo.mMonitor);
+ nsRefPtr<VideoDataPromise> p = mVideo.mPromise.Ensure(__func__);
+ if (eos) {
+ mVideo.mPromise.Reject(END_OF_STREAM, __func__);
+ } else {
+ ScheduleUpdate(kVideo);
+ }
// Report the number of "decoded" frames as the difference in the
// mNumSamplesOutput field since the last time we were called.
uint64_t delta = mVideo.mNumSamplesOutput - mLastReportedNumDecodedFrames;
decoded = static_cast<uint32_t>(delta);
mLastReportedNumDecodedFrames = mVideo.mNumSamplesOutput;
+
+ return p;
}
-void
+nsRefPtr<MediaDecoderReader::AudioDataPromise>
MP4Reader::RequestAudioData()
{
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
VLOG("RequestAudioData");
- auto& decoder = GetDecoderData(kAudio);
- MonitorAutoLock lock(decoder.mMonitor);
- decoder.mOutputRequested = true;
+ MonitorAutoLock lock(mAudio.mMonitor);
+ nsRefPtr<AudioDataPromise> p = mAudio.mPromise.Ensure(__func__);
ScheduleUpdate(kAudio);
+ return p;
}
void
MP4Reader::ScheduleUpdate(TrackType aTrack)
{
auto& decoder = GetDecoderData(aTrack);
decoder.mMonitor.AssertCurrentThreadOwns();
if (decoder.mUpdateScheduled) {
@@ -561,115 +575,101 @@ MP4Reader::NeedInput(DecoderData& aDecod
// We try to keep a few more compressed samples input than decoded samples
// have been output, provided the state machine has requested we send it a
// decoded sample. To account for H.264 streams which may require a longer
// run of input than we input, decoders fire an "input exhausted" callback,
// which overrides our "few more samples" threshold.
return
!aDecoder.mError &&
!aDecoder.mDemuxEOS &&
- aDecoder.mOutputRequested &&
+ aDecoder.HasPromise() &&
aDecoder.mOutput.IsEmpty() &&
(aDecoder.mInputExhausted ||
aDecoder.mNumSamplesInput - aDecoder.mNumSamplesOutput < aDecoder.mDecodeAhead);
}
void
MP4Reader::Update(TrackType aTrack)
{
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
bool needInput = false;
bool needOutput = false;
- bool eos = false;
auto& decoder = GetDecoderData(aTrack);
- nsRefPtr<MediaData> output;
{
MonitorAutoLock lock(decoder.mMonitor);
decoder.mUpdateScheduled = false;
if (NeedInput(decoder)) {
needInput = true;
decoder.mInputExhausted = false;
decoder.mNumSamplesInput++;
}
- needOutput = decoder.mOutputRequested;
- if (needOutput && !decoder.mOutput.IsEmpty()) {
- output = decoder.mOutput[0];
- decoder.mOutput.RemoveElementAt(0);
+ if (decoder.HasPromise()) {
+ needOutput = true;
+ if (!decoder.mOutput.IsEmpty()) {
+ nsRefPtr<MediaData> output = decoder.mOutput[0];
+ decoder.mOutput.RemoveElementAt(0);
+ ReturnOutput(output, aTrack);
+ } else if (decoder.mDrainComplete) {
+ decoder.RejectPromise(END_OF_STREAM, __func__);
+ }
}
- eos = decoder.mDrainComplete;
}
- VLOG("Update(%s) ni=%d no=%d iex=%d or=%d fl=%d",
+
+ VLOG("Update(%s) ni=%d no=%d iex=%d fl=%d",
TrackTypeToStr(aTrack),
needInput,
needOutput,
decoder.mInputExhausted,
- decoder.mOutputRequested,
decoder.mIsFlushing);
if (needInput) {
MP4Sample* sample = PopSample(aTrack);
if (sample) {
decoder.mDecoder->Input(sample);
} else {
{
MonitorAutoLock lock(decoder.mMonitor);
MOZ_ASSERT(!decoder.mDemuxEOS);
decoder.mDemuxEOS = true;
}
// DrainComplete takes care of reporting EOS upwards
decoder.mDecoder->Drain();
}
}
- if (needOutput) {
- if (output) {
- ReturnOutput(output, aTrack);
- } else if (eos) {
- ReturnEOS(aTrack);
- }
- }
}
void
MP4Reader::ReturnOutput(MediaData* aData, TrackType aTrack)
{
auto& decoder = GetDecoderData(aTrack);
- {
- MonitorAutoLock lock(decoder.mMonitor);
- MOZ_ASSERT(decoder.mOutputRequested);
- decoder.mOutputRequested = false;
- if (decoder.mDiscontinuity) {
- decoder.mDiscontinuity = false;
- aData->mDiscontinuity = true;
- }
+ decoder.mMonitor.AssertCurrentThreadOwns();
+ MOZ_ASSERT(decoder.HasPromise());
+ if (decoder.mDiscontinuity) {
+ decoder.mDiscontinuity = false;
+ aData->mDiscontinuity = true;
}
if (aTrack == kAudio) {
AudioData* audioData = static_cast<AudioData*>(aData);
if (audioData->mChannels != mInfo.mAudio.mChannels ||
audioData->mRate != mInfo.mAudio.mRate) {
LOG("MP4Reader::ReturnOutput change of sampling rate:%d->%d",
mInfo.mAudio.mRate, audioData->mRate);
mInfo.mAudio.mRate = audioData->mRate;
mInfo.mAudio.mChannels = audioData->mChannels;
}
- GetCallback()->OnAudioDecoded(audioData);
+ mAudio.mPromise.Resolve(audioData, __func__);
} else if (aTrack == kVideo) {
- GetCallback()->OnVideoDecoded(static_cast<VideoData*>(aData));
+ mVideo.mPromise.Resolve(static_cast<VideoData*>(aData), __func__);
}
}
-void
-MP4Reader::ReturnEOS(TrackType aTrack)
-{
- GetCallback()->OnNotDecoded(aTrack == kAudio ? MediaData::AUDIO_DATA : MediaData::VIDEO_DATA, END_OF_STREAM);
-}
-
MP4Sample*
MP4Reader::PopSample(TrackType aTrack)
{
switch (aTrack) {
case kAudio:
return mDemuxer->DemuxAudioSample();
case kVideo:
@@ -712,17 +712,17 @@ MP4Reader::Output(TrackType aTrack, Medi
if (decoder.mIsFlushing) {
LOG("MP4Reader produced output while flushing, discarding.");
mon.NotifyAll();
return;
}
decoder.mOutput.AppendElement(aSample);
decoder.mNumSamplesOutput++;
- if (NeedInput(decoder) || decoder.mOutputRequested) {
+ if (NeedInput(decoder) || decoder.HasPromise()) {
ScheduleUpdate(aTrack);
}
}
void
MP4Reader::DrainComplete(TrackType aTrack)
{
DecoderData& data = GetDecoderData(aTrack);
@@ -742,18 +742,20 @@ MP4Reader::InputExhausted(TrackType aTra
void
MP4Reader::Error(TrackType aTrack)
{
DecoderData& data = GetDecoderData(aTrack);
{
MonitorAutoLock mon(data.mMonitor);
data.mError = true;
+ if (data.HasPromise()) {
+ data.RejectPromise(DECODE_ERROR, __func__);
+ }
}
- GetCallback()->OnNotDecoded(aTrack == kVideo ? MediaData::VIDEO_DATA : MediaData::AUDIO_DATA, DECODE_ERROR);
}
void
MP4Reader::Flush(TrackType aTrack)
{
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
VLOG("Flush(%s) BEGIN", TrackTypeToStr(aTrack));
DecoderData& data = GetDecoderData(aTrack);
@@ -772,20 +774,19 @@ MP4Reader::Flush(TrackType aTrack)
data.mDecoder->Flush();
{
MonitorAutoLock mon(data.mMonitor);
data.mIsFlushing = false;
data.mOutput.Clear();
data.mNumSamplesInput = 0;
data.mNumSamplesOutput = 0;
data.mInputExhausted = false;
- if (data.mOutputRequested) {
- GetCallback()->OnNotDecoded(aTrack == kVideo ? MediaData::VIDEO_DATA : MediaData::AUDIO_DATA, CANCELED);
+ if (data.HasPromise()) {
+ data.RejectPromise(CANCELED, __func__);
}
- data.mOutputRequested = false;
data.mDiscontinuity = true;
}
if (aTrack == kVideo) {
mQueuedVideoSample = nullptr;
}
VLOG("Flush(%s) END", TrackTypeToStr(aTrack));
}
@@ -797,22 +798,19 @@ MP4Reader::SkipVideoDemuxToNextKeyFrame(
MOZ_ASSERT(mVideo.mDecoder);
Flush(kVideo);
// Loop until we reach the next keyframe after the threshold.
while (true) {
nsAutoPtr<MP4Sample> compressed(PopSample(kVideo));
if (!compressed) {
- // EOS, or error. Let the state machine know.
- GetCallback()->OnNotDecoded(MediaData::VIDEO_DATA, END_OF_STREAM);
- {
- MonitorAutoLock mon(mVideo.mMonitor);
- mVideo.mDemuxEOS = true;
- }
+ // EOS, or error. This code assumes EOS, which may or may not be right.
+ MonitorAutoLock mon(mVideo.mMonitor);
+ mVideo.mDemuxEOS = true;
return false;
}
parsed++;
if (!compressed->is_sync_point ||
compressed->composition_timestamp < aTimeThreshold) {
continue;
}
mQueuedVideoSample = compressed;
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -32,20 +32,20 @@ class MP4Reader MOZ_FINAL : public Media
public:
explicit MP4Reader(AbstractMediaDecoder* aDecoder);
virtual ~MP4Reader();
virtual nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
- virtual void RequestVideoData(bool aSkipToNextKeyframe,
- int64_t aTimeThreshold) MOZ_OVERRIDE;
+ virtual nsRefPtr<VideoDataPromise>
+ RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
- virtual void RequestAudioData() MOZ_OVERRIDE;
+ virtual nsRefPtr<AudioDataPromise> RequestAudioData() MOZ_OVERRIDE;
virtual bool HasAudio() MOZ_OVERRIDE;
virtual bool HasVideo() MOZ_OVERRIDE;
virtual nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags) MOZ_OVERRIDE;
virtual void ReadUpdatedMetadata(MediaInfo* aInfo) MOZ_OVERRIDE;
@@ -70,17 +70,16 @@ public:
MOZ_OVERRIDE;
virtual nsresult ResetDecode() MOZ_OVERRIDE;
virtual nsRefPtr<ShutdownPromise> Shutdown() MOZ_OVERRIDE;
private:
- void ReturnEOS(TrackType aTrack);
void ReturnOutput(MediaData* aData, TrackType aTrack);
// Sends input to decoder for aTrack, and output to the state machine,
// if necessary.
void Update(TrackType aTrack);
// Enqueues a task to call Update(aTrack) on the decoder task queue.
// Lock for corresponding track must be held.
@@ -141,27 +140,28 @@ private:
mReader->ReleaseMediaResources();
}
private:
MP4Reader* mReader;
mp4_demuxer::TrackType mType;
};
struct DecoderData {
- DecoderData(const char* aMonitorName,
+ DecoderData(MediaData::Type aType,
uint32_t aDecodeAhead)
- : mMonitor(aMonitorName)
+ : mType(aType)
+ , mMonitor(aType == MediaData::AUDIO_DATA ? "MP4 audio decoder data"
+ : "MP4 video decoder data")
, mNumSamplesInput(0)
, mNumSamplesOutput(0)
, mDecodeAhead(aDecodeAhead)
, mActive(false)
, mInputExhausted(false)
, mError(false)
, mIsFlushing(false)
- , mOutputRequested(false)
, mUpdateScheduled(false)
, mDemuxEOS(false)
, mDrainComplete(false)
, mDiscontinuity(false)
{
}
// The platform decoder.
@@ -169,36 +169,62 @@ private:
// TaskQueue on which decoder can choose to decode.
// Only non-null up until the decoder is created.
nsRefPtr<MediaTaskQueue> mTaskQueue;
// Callback that receives output and error notifications from the decoder.
nsAutoPtr<DecoderCallback> mCallback;
// Decoded samples returned my mDecoder awaiting being returned to
// state machine upon request.
nsTArray<nsRefPtr<MediaData> > mOutput;
+ // Disambiguate Audio vs Video.
+ MediaData::Type mType;
+
+ // These get overriden in the templated concrete class.
+ virtual bool HasPromise() = 0;
+ virtual void RejectPromise(MediaDecoderReader::NotDecodedReason aReason,
+ const char* aMethodName) = 0;
// Monitor that protects all non-threadsafe state; the primitives
// that follow.
Monitor mMonitor;
uint64_t mNumSamplesInput;
uint64_t mNumSamplesOutput;
uint32_t mDecodeAhead;
// Whether this stream exists in the media.
bool mActive;
bool mInputExhausted;
bool mError;
bool mIsFlushing;
- bool mOutputRequested;
bool mUpdateScheduled;
bool mDemuxEOS;
bool mDrainComplete;
bool mDiscontinuity;
};
- DecoderData mAudio;
- DecoderData mVideo;
+
+ template<typename PromiseType>
+ struct DecoderDataWithPromise : public DecoderData {
+ DecoderDataWithPromise(MediaData::Type aType, uint32_t aDecodeAhead) :
+ DecoderData(aType, aDecodeAhead)
+ {
+ mPromise.SetMonitor(&mMonitor);
+ }
+
+ MediaPromiseHolder<PromiseType> mPromise;
+
+ bool HasPromise() MOZ_OVERRIDE { return !mPromise.IsEmpty(); }
+ void RejectPromise(MediaDecoderReader::NotDecodedReason aReason,
+ const char* aMethodName) MOZ_OVERRIDE
+ {
+ mPromise.Reject(aReason, aMethodName);
+ }
+ };
+
+ DecoderDataWithPromise<AudioDataPromise> mAudio;
+ DecoderDataWithPromise<VideoDataPromise> mVideo;
+
// Queued samples extracted by the demuxer, but not yet sent to the platform
// decoder.
nsAutoPtr<mp4_demuxer::MP4Sample> mQueuedVideoSample;
// Returns true when the decoder for this track needs input.
// aDecoder.mMonitor must be locked.
bool NeedInput(DecoderData& aDecoder);
--- a/dom/media/fmp4/SharedDecoderManager.cpp
+++ b/dom/media/fmp4/SharedDecoderManager.cpp
@@ -7,17 +7,17 @@
#include "SharedDecoderManager.h"
#include "mp4_demuxer/DecoderData.h"
namespace mozilla {
class SharedDecoderCallback : public MediaDataDecoderCallback
{
public:
- SharedDecoderCallback(SharedDecoderManager* aManager) : mManager(aManager) {}
+ explicit SharedDecoderCallback(SharedDecoderManager* aManager) : mManager(aManager) {}
virtual void Output(MediaData* aData) MOZ_OVERRIDE
{
if (mManager->mActiveCallback) {
mManager->mActiveCallback->Output(aData);
}
}
virtual void Error() MOZ_OVERRIDE
--- a/dom/media/fmp4/eme/EMEAudioDecoder.cpp
+++ b/dom/media/fmp4/eme/EMEAudioDecoder.cpp
@@ -29,29 +29,34 @@ EMEAudioDecoder::EMEAudioDecoder(CDMProx
, mAudioFrameSum(0)
, mAudioFrameOffset(0)
, mStreamOffset(0)
, mProxy(aProxy)
, mGMP(nullptr)
, mConfig(aConfig)
, mTaskQueue(aTaskQueue)
, mCallback(aCallback)
+ , mSamplesWaitingForKey(new SamplesWaitingForKey(this, mTaskQueue, mProxy))
, mMonitor("EMEAudioDecoder")
, mFlushComplete(false)
+#ifdef DEBUG
+ , mIsShutdown(false)
+#endif
{
}
EMEAudioDecoder::~EMEAudioDecoder()
{
}
nsresult
EMEAudioDecoder::Init()
{
// Note: this runs on the decode task queue.
+ MOZ_ASSERT(!mIsShutdown);
MOZ_ASSERT((mConfig.bits_per_sample / 8) == 2); // Demuxer guarantees this.
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
MOZ_ASSERT(mMPS);
nsresult rv = mMPS->GetThread(getter_AddRefs(mGMPThread));
NS_ENSURE_SUCCESS(rv, rv);
@@ -63,28 +68,34 @@ EMEAudioDecoder::Init()
return NS_OK;
}
nsresult
EMEAudioDecoder::Input(MP4Sample* aSample)
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
+ MOZ_ASSERT(!mIsShutdown);
+
+ if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
+ return NS_OK;
+ }
nsRefPtr<nsIRunnable> task(new DeliverSample(this, aSample));
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
EMEAudioDecoder::Flush()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
+ MOZ_ASSERT(!mIsShutdown);
{
MonitorAutoLock mon(mMonitor);
mFlushComplete = false;
}
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEAudioDecoder::GmpFlush);
@@ -100,33 +111,42 @@ EMEAudioDecoder::Flush()
return NS_OK;
}
nsresult
EMEAudioDecoder::Drain()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
+ MOZ_ASSERT(!mIsShutdown);
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEAudioDecoder::GmpDrain);
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
EMEAudioDecoder::Shutdown()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
+ MOZ_ASSERT(!mIsShutdown);
+#ifdef DEBUG
+ mIsShutdown = true;
+#endif
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEAudioDecoder::GmpShutdown);
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
+
+ mSamplesWaitingForKey->BreakCycles();
+ mSamplesWaitingForKey = nullptr;
+
return NS_OK;
}
void
EMEAudioDecoder::Decoded(const nsTArray<int16_t>& aPCM,
uint64_t aTimeStamp,
uint32_t aChannels,
uint32_t aRate)
@@ -216,19 +236,25 @@ EMEAudioDecoder::ResetComplete()
mon.NotifyAll();
}
}
void
EMEAudioDecoder::Error(GMPErr aErr)
{
MOZ_ASSERT(IsOnGMPThread());
- EME_LOG("EMEAudioDecoder::Error");
- mCallback->Error();
- GmpShutdown();
+ EME_LOG("EMEAudioDecoder::Error %d", aErr);
+ if (aErr == GMPNoKeyErr) {
+ // The GMP failed to decrypt a frame due to not having a key. This can
+ // happen if a key expires or a session is closed during playback.
+ NS_WARNING("GMP failed to decrypt due to lack of key");
+ } else {
+ mCallback->Error();
+ GmpShutdown();
+ }
}
void
EMEAudioDecoder::Terminated()
{
MOZ_ASSERT(IsOnGMPThread());
GmpShutdown();
}
@@ -270,28 +296,16 @@ EMEAudioDecoder::GmpInput(MP4Sample* aSa
{
MOZ_ASSERT(IsOnGMPThread());
nsAutoPtr<MP4Sample> sample(aSample);
if (!mGMP) {
mCallback->Error();
return NS_ERROR_FAILURE;
}
- if (sample->crypto.valid) {
- CDMCaps::AutoLock caps(mProxy->Capabilites());
- MOZ_ASSERT(caps.CanDecryptAndDecodeAudio());
- const auto& keyid = sample->crypto.key;
- if (!caps.IsKeyUsable(keyid)) {
- // DeliverSample assumes responsibility for deleting aSample.
- nsRefPtr<nsIRunnable> task(new DeliverSample(this, sample.forget()));
- caps.CallWhenKeyUsable(keyid, task, mGMPThread);
- return NS_OK;
- }
- }
-
gmp::GMPAudioSamplesImpl samples(sample, mAudioChannels, mAudioRate);
mGMP->Decode(samples);
mStreamOffset = sample->byte_offset;
return NS_OK;
}
--- a/dom/media/fmp4/eme/EMEAudioDecoder.h
+++ b/dom/media/fmp4/eme/EMEAudioDecoder.h
@@ -8,16 +8,17 @@
#define EMEAACDecoder_h_
#include "PlatformDecoderModule.h"
#include "mp4_demuxer/DecoderData.h"
#include "mozIGeckoMediaPluginService.h"
#include "nsServiceManagerUtils.h"
#include "GMPAudioHost.h"
#include "GMPAudioDecoderProxy.h"
+#include "SamplesWaitingForKey.h"
namespace mozilla {
class EMEAudioDecoder : public MediaDataDecoder
, public GMPAudioDecoderProxyCallback
{
typedef mp4_demuxer::MP4Sample MP4Sample;
typedef mp4_demuxer::AudioDecoderConfig AudioDecoderConfig;
@@ -103,15 +104,21 @@ private:
nsCOMPtr<nsIThread> mGMPThread;
nsRefPtr<CDMProxy> mProxy;
GMPAudioDecoderProxy* mGMP;
const mp4_demuxer::AudioDecoderConfig& mConfig;
nsRefPtr<MediaTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
+ nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
+
Monitor mMonitor;
bool mFlushComplete;
+
+#ifdef DEBUG
+ bool mIsShutdown;
+#endif
};
} // namespace mozilla
#endif
--- a/dom/media/fmp4/eme/EMEDecoderModule.cpp
+++ b/dom/media/fmp4/eme/EMEDecoderModule.cpp
@@ -16,165 +16,158 @@
#include "mozilla/CDMProxy.h"
#include "mozilla/EMELog.h"
#include "MediaTaskQueue.h"
#include "SharedThreadPool.h"
#include "mozilla/EMELog.h"
#include "EMEH264Decoder.h"
#include "EMEAudioDecoder.h"
#include "mozilla/unused.h"
+#include "SamplesWaitingForKey.h"
#include <string>
namespace mozilla {
class EMEDecryptor : public MediaDataDecoder {
typedef mp4_demuxer::MP4Sample MP4Sample;
public:
EMEDecryptor(MediaDataDecoder* aDecoder,
MediaDataDecoderCallback* aCallback,
CDMProxy* aProxy)
: mDecoder(aDecoder)
, mCallback(aCallback)
, mTaskQueue(CreateMediaDecodeTaskQueue())
, mProxy(aProxy)
+ , mSamplesWaitingForKey(new SamplesWaitingForKey(this, mTaskQueue, mProxy))
+#ifdef DEBUG
+ , mIsShutdown(false)
+#endif
{
}
virtual nsresult Init() MOZ_OVERRIDE {
+ MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->SyncDispatch(
NS_NewRunnableMethod(mDecoder, &MediaDataDecoder::Init));
unused << NS_WARN_IF(NS_FAILED(rv));
return rv;
}
- class RedeliverEncryptedInput : public nsRunnable {
- public:
- RedeliverEncryptedInput(EMEDecryptor* aDecryptor,
- MediaTaskQueue* aTaskQueue,
- MP4Sample* aSample)
- : mDecryptor(aDecryptor)
- , mTaskQueue(aTaskQueue)
- , mSample(aSample)
- {}
-
- NS_IMETHOD Run() {
- RefPtr<nsIRunnable> task;
- task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
- &EMEDecryptor::Input,
- mSample.forget());
- mTaskQueue->Dispatch(task.forget());
- mTaskQueue = nullptr;
- mDecryptor = nullptr;
- return NS_OK;
- }
-
- private:
- nsRefPtr<EMEDecryptor> mDecryptor;
- nsRefPtr<MediaTaskQueue> mTaskQueue;
- nsAutoPtr<MP4Sample> mSample;
- };
-
class DeliverDecrypted : public DecryptionClient {
public:
DeliverDecrypted(EMEDecryptor* aDecryptor, MediaTaskQueue* aTaskQueue)
: mDecryptor(aDecryptor)
, mTaskQueue(aTaskQueue)
{}
- virtual void Decrypted(nsresult aResult,
+ virtual void Decrypted(GMPErr aResult,
mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
- if (NS_FAILED(aResult)) {
+ if (aResult == GMPNoKeyErr) {
+ RefPtr<nsIRunnable> task;
+ task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
+ &EMEDecryptor::Input,
+ aSample);
+ mTaskQueue->Dispatch(task.forget());
+ } else if (GMP_FAILED(aResult)) {
mDecryptor->mCallback->Error();
MOZ_ASSERT(!aSample);
} else {
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
&EMEDecryptor::Decrypted,
aSample);
mTaskQueue->Dispatch(task.forget());
- mTaskQueue = nullptr;
- mDecryptor = nullptr;
}
+ mTaskQueue = nullptr;
+ mDecryptor = nullptr;
}
private:
nsRefPtr<EMEDecryptor> mDecryptor;
nsRefPtr<MediaTaskQueue> mTaskQueue;
};
virtual nsresult Input(MP4Sample* aSample) MOZ_OVERRIDE {
+ MOZ_ASSERT(!mIsShutdown);
// We run the PDM on its own task queue. We can't run it on the decode
// task queue, because that calls into Input() in a loop and waits until
// output is delivered. We need to defer some Input() calls while we wait
// for keys to become usable, and once they do we need to dispatch an event
// to run the PDM on the same task queue, but since the decode task queue
// is waiting in MP4Reader::Decode() for output our task would never run.
// So we dispatch tasks to make all calls into the wrapped decoder.
- {
- CDMCaps::AutoLock caps(mProxy->Capabilites());
- if (!caps.IsKeyUsable(aSample->crypto.key)) {
- EME_LOG("Encountered a non-usable key, waiting");
- nsRefPtr<nsIRunnable> task(new RedeliverEncryptedInput(this,
- mTaskQueue,
- aSample));
- caps.CallWhenKeyUsable(aSample->crypto.key, task);
- return NS_OK;
- }
+ if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
+ return NS_OK;
}
+
mProxy->Decrypt(aSample, new DeliverDecrypted(this, mTaskQueue));
return NS_OK;
}
void Decrypted(mp4_demuxer::MP4Sample* aSample) {
+ MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->Dispatch(
NS_NewRunnableMethodWithArg<mp4_demuxer::MP4Sample*>(
mDecoder,
&MediaDataDecoder::Input,
aSample));
unused << NS_WARN_IF(NS_FAILED(rv));
}
virtual nsresult Flush() MOZ_OVERRIDE {
+ MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->SyncDispatch(
NS_NewRunnableMethod(
mDecoder,
&MediaDataDecoder::Flush));
unused << NS_WARN_IF(NS_FAILED(rv));
+ mSamplesWaitingForKey->Flush();
return rv;
}
virtual nsresult Drain() MOZ_OVERRIDE {
+ MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->Dispatch(
NS_NewRunnableMethod(
mDecoder,
&MediaDataDecoder::Drain));
unused << NS_WARN_IF(NS_FAILED(rv));
return rv;
}
virtual nsresult Shutdown() MOZ_OVERRIDE {
+ MOZ_ASSERT(!mIsShutdown);
+#ifdef DEBUG
+ mIsShutdown = true;
+#endif
nsresult rv = mTaskQueue->SyncDispatch(
NS_NewRunnableMethod(
mDecoder,
&MediaDataDecoder::Shutdown));
unused << NS_WARN_IF(NS_FAILED(rv));
+ mSamplesWaitingForKey->BreakCycles();
+ mSamplesWaitingForKey = nullptr;
mDecoder = nullptr;
mTaskQueue->BeginShutdown();
mTaskQueue->AwaitShutdownAndIdle();
mTaskQueue = nullptr;
mProxy = nullptr;
return rv;
}
private:
nsRefPtr<MediaDataDecoder> mDecoder;
MediaDataDecoderCallback* mCallback;
nsRefPtr<MediaTaskQueue> mTaskQueue;
nsRefPtr<CDMProxy> mProxy;
+ nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
+#ifdef DEBUG
+ bool mIsShutdown;
+#endif
};
EMEDecoderModule::EMEDecoderModule(CDMProxy* aProxy,
PlatformDecoderModule* aPDM,
bool aCDMDecodesAudio,
bool aCDMDecodesVideo)
: mProxy(aProxy)
, mPDM(aPDM)
--- a/dom/media/fmp4/eme/EMEH264Decoder.cpp
+++ b/dom/media/fmp4/eme/EMEH264Decoder.cpp
@@ -29,28 +29,33 @@ EMEH264Decoder::EMEH264Decoder(CDMProxy*
: mProxy(aProxy)
, mGMP(nullptr)
, mHost(nullptr)
, mConfig(aConfig)
, mImageContainer(aImageContainer)
, mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mLastStreamOffset(0)
+ , mSamplesWaitingForKey(new SamplesWaitingForKey(this, mTaskQueue, mProxy))
, mMonitor("EMEH264Decoder")
, mFlushComplete(false)
+#ifdef DEBUG
+ , mIsShutdown(false)
+#endif
{
}
EMEH264Decoder::~EMEH264Decoder() {
}
nsresult
EMEH264Decoder::Init()
{
// Note: this runs on the decode task queue.
+ MOZ_ASSERT(!mIsShutdown);
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
MOZ_ASSERT(mMPS);
nsresult rv = mMPS->GetThread(getter_AddRefs(mGMPThread));
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<InitTask> task(new InitTask(this));
@@ -60,28 +65,34 @@ EMEH264Decoder::Init()
return NS_OK;
}
nsresult
EMEH264Decoder::Input(MP4Sample* aSample)
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
+ MOZ_ASSERT(!mIsShutdown);
+
+ if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
+ return NS_OK;
+ }
nsRefPtr<nsIRunnable> task(new DeliverSample(this, aSample));
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
EMEH264Decoder::Flush()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
+ MOZ_ASSERT(!mIsShutdown);
{
MonitorAutoLock mon(mMonitor);
mFlushComplete = false;
}
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpFlush);
@@ -97,33 +108,42 @@ EMEH264Decoder::Flush()
return NS_OK;
}
nsresult
EMEH264Decoder::Drain()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
+ MOZ_ASSERT(!mIsShutdown);
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpDrain);
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
EMEH264Decoder::Shutdown()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
+ MOZ_ASSERT(!mIsShutdown);
+#ifdef DEBUG
+ mIsShutdown = true;
+#endif
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpShutdown);
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
+
+ mSamplesWaitingForKey->BreakCycles();
+ mSamplesWaitingForKey = nullptr;
+
return NS_OK;
}
void
EMEH264Decoder::Decoded(GMPVideoi420Frame* aDecodedFrame)
{
MOZ_ASSERT(IsOnGMPThread());
@@ -206,19 +226,25 @@ EMEH264Decoder::ResetComplete()
mon.NotifyAll();
}
}
void
EMEH264Decoder::Error(GMPErr aErr)
{
MOZ_ASSERT(IsOnGMPThread());
- EME_LOG("EMEH264Decoder::Error");
- mCallback->Error();
- GmpShutdown();
+ EME_LOG("EMEH264Decoder::Error %d", aErr);
+ if (aErr == GMPNoKeyErr) {
+ // The GMP failed to decrypt a frame due to not having a key. This can
+ // happen if a key expires or a session is closed during playback.
+ NS_WARNING("GMP failed to decrypt due to lack of key");
+ } else {
+ mCallback->Error();
+ GmpShutdown();
+ }
}
void
EMEH264Decoder::Terminated()
{
MOZ_ASSERT(IsOnGMPThread());
NS_WARNING("H.264 GMP decoder terminated.");
@@ -272,28 +298,16 @@ EMEH264Decoder::GmpInput(MP4Sample* aSam
MOZ_ASSERT(IsOnGMPThread());
nsAutoPtr<MP4Sample> sample(aSample);
if (!mGMP) {
mCallback->Error();
return NS_ERROR_FAILURE;
}
- if (sample->crypto.valid) {
- CDMCaps::AutoLock caps(mProxy->Capabilites());
- MOZ_ASSERT(caps.CanDecryptAndDecodeVideo());
- const auto& keyid = sample->crypto.key;
- if (!caps.IsKeyUsable(keyid)) {
- nsRefPtr<nsIRunnable> task(new DeliverSample(this, sample.forget()));
- caps.CallWhenKeyUsable(keyid, task, mGMPThread);
- return NS_OK;
- }
- }
-
-
mLastStreamOffset = sample->byte_offset;
GMPVideoFrame* ftmp = nullptr;
GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
if (GMP_FAILED(err)) {
mCallback->Error();
return NS_ERROR_FAILURE;
}
--- a/dom/media/fmp4/eme/EMEH264Decoder.h
+++ b/dom/media/fmp4/eme/EMEH264Decoder.h
@@ -7,16 +7,17 @@
#ifndef EMEH264Decoder_h_
#define EMEH264Decoder_h_
#include "PlatformDecoderModule.h"
#include "mp4_demuxer/DecoderData.h"
#include "ImageContainer.h"
#include "GMPVideoDecoderProxy.h"
#include "mozIGeckoMediaPluginService.h"
+#include "SamplesWaitingForKey.h"
namespace mozilla {
class CDMProxy;
class MediaTaskQueue;
class EMEH264Decoder : public MediaDataDecoder
, public GMPVideoDecoderCallbackProxy
@@ -99,15 +100,22 @@ private:
GMPVideoHost* mHost;
VideoInfo mVideoInfo;
const mp4_demuxer::VideoDecoderConfig& mConfig;
nsRefPtr<layers::ImageContainer> mImageContainer;
nsRefPtr<MediaTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
int64_t mLastStreamOffset;
+
+ nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
+
Monitor mMonitor;
bool mFlushComplete;
+
+#ifdef DEBUG
+ bool mIsShutdown;
+#endif
};
}
#endif // EMEH264Decoder_h_
new file mode 100644
--- /dev/null
+++ b/dom/media/fmp4/eme/SamplesWaitingForKey.cpp
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SamplesWaitingForKey.h"
+#include "mozilla/CDMProxy.h"
+#include "mozilla/CDMCaps.h"
+
+namespace mozilla {
+
+SamplesWaitingForKey::SamplesWaitingForKey(MediaDataDecoder* aDecoder,
+ MediaTaskQueue* aTaskQueue,
+ CDMProxy* aProxy)
+ : mMutex("SamplesWaitingForKey")
+ , mDecoder(aDecoder)
+ , mTaskQueue(aTaskQueue)
+ , mProxy(aProxy)
+{
+}
+
+SamplesWaitingForKey::~SamplesWaitingForKey()
+{
+}
+
+bool
+SamplesWaitingForKey::WaitIfKeyNotUsable(MP4Sample* aSample)
+{
+ if (!aSample || !aSample->crypto.valid || !mProxy) {
+ return false;
+ }
+ CDMCaps::AutoLock caps(mProxy->Capabilites());
+ const auto& keyid = aSample->crypto.key;
+ if (!caps.IsKeyUsable(keyid)) {
+ {
+ MutexAutoLock lock(mMutex);
+ mSamples.AppendElement(aSample);
+ }
+ caps.NotifyWhenKeyIdUsable(aSample->crypto.key, this);
+ return true;
+ }
+ return false;
+}
+
+void
+SamplesWaitingForKey::NotifyUsable(const CencKeyId& aKeyId)
+{
+ MutexAutoLock lock(mMutex);
+ size_t i = 0;
+ while (i < mSamples.Length()) {
+ if (aKeyId == mSamples[i]->crypto.key) {
+ RefPtr<nsIRunnable> task;
+ task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecoder,
+ &MediaDataDecoder::Input,
+ mSamples[i].forget());
+ mSamples.RemoveElementAt(i);
+ mTaskQueue->Dispatch(task.forget());
+ } else {
+ i++;
+ }
+ }
+}
+
+void
+SamplesWaitingForKey::Flush()
+{
+ MutexAutoLock lock(mMutex);
+ mSamples.Clear();
+}
+
+void
+SamplesWaitingForKey::BreakCycles()
+{
+ MutexAutoLock lock(mMutex);
+ mDecoder = nullptr;
+ mTaskQueue = nullptr;
+ mProxy = nullptr;
+ mSamples.Clear();
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/fmp4/eme/SamplesWaitingForKey.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SamplesWaitingForKey_h_
+#define SamplesWaitingForKey_h_
+
+#include "mp4_demuxer/DecoderData.h"
+#include "MediaTaskQueue.h"
+#include "PlatformDecoderModule.h"
+
+namespace mozilla {
+
+typedef nsTArray<uint8_t> CencKeyId;
+
+class CDMProxy;
+
+// Encapsulates the task of waiting for the CDMProxy to have the necessary
+// keys to decypt a given sample.
+class SamplesWaitingForKey {
+ typedef mp4_demuxer::MP4Sample MP4Sample;
+public:
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SamplesWaitingForKey)
+
+ explicit SamplesWaitingForKey(MediaDataDecoder* aDecoder,
+ MediaTaskQueue* aTaskQueue,
+ CDMProxy* aProxy);
+
+ // Returns true if we need to wait for a key to become usable.
+ // Will callback MediaDataDecoder::Input(aSample) on mDecoder once the
+ // sample is ready to be decrypted. The order of input samples is
+ // preserved.
+ bool WaitIfKeyNotUsable(MP4Sample* aSample);
+
+ void NotifyUsable(const CencKeyId& aKeyId);
+
+ void Flush();
+
+ void BreakCycles();
+
+protected:
+ ~SamplesWaitingForKey();
+
+private:
+ Mutex mMutex;
+ nsRefPtr<MediaDataDecoder> mDecoder;
+ nsRefPtr<MediaTaskQueue> mTaskQueue;
+ nsRefPtr<CDMProxy> mProxy;
+ nsTArray<nsAutoPtr<MP4Sample>> mSamples;
+};
+
+} // namespace mozilla
+
+#endif // SamplesWaitingForKey_h_
--- a/dom/media/fmp4/eme/moz.build
+++ b/dom/media/fmp4/eme/moz.build
@@ -1,26 +1,28 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-EXPORTS += [
- 'EMEAudioDecoder.h',
- 'EMEDecoderModule.h',
- 'EMEH264Decoder.h',
-]
-
-UNIFIED_SOURCES += [
- 'EMEAudioDecoder.cpp',
- 'EMEDecoderModule.cpp',
- 'EMEH264Decoder.cpp',
-]
-
-include('/ipc/chromium/chromium-config.mozbuild')
-
-FINAL_LIBRARY = 'xul'
-
-FAIL_ON_WARNINGS = True
-
-if CONFIG['OS_ARCH'] == 'WINNT':
- DEFINES['NOMINMAX'] = True
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS += [
+ 'EMEAudioDecoder.h',
+ 'EMEDecoderModule.h',
+ 'EMEH264Decoder.h',
+ 'SamplesWaitingForKey.h',
+]
+
+UNIFIED_SOURCES += [
+ 'EMEAudioDecoder.cpp',
+ 'EMEDecoderModule.cpp',
+ 'EMEH264Decoder.cpp',
+ 'SamplesWaitingForKey.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ DEFINES['NOMINMAX'] = True
--- a/dom/media/gmp-plugin/gmp-test-decryptor.cpp
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.cpp
@@ -145,19 +145,19 @@ public:
delete this;
}
string mId;
ReadContinuation* mThen;
};
class SendMessageTask : public GMPTask {
public:
- SendMessageTask(const string& aMessage,
- TestManager* aTestManager = nullptr,
- const string& aTestID = "")
+ explicit SendMessageTask(const string& aMessage,
+ TestManager* aTestManager = nullptr,
+ const string& aTestID = "")
: mMessage(aMessage), mTestmanager(aTestManager), mTestID(aTestID) {}
void Run() MOZ_OVERRIDE {
FakeDecryptor::Message(mMessage);
if (mTestmanager) {
mTestmanager->EndTest(mTestID);
}
}
--- a/dom/media/gmp-plugin/gmp-test-decryptor.h
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.h
@@ -9,17 +9,17 @@
#include "gmp-decryption.h"
#include "gmp-async-shutdown.h"
#include <string>
#include "mozilla/Attributes.h"
class FakeDecryptor : public GMPDecryptor {
public:
- FakeDecryptor(GMPDecryptorHost* aHost);
+ explicit FakeDecryptor(GMPDecryptorHost* aHost);
virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE {
mCallback = aCallback;
}
virtual void CreateSession(uint32_t aPromiseId,
const char* aInitDataType,
uint32_t aInitDataTypeSize,
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -356,17 +356,17 @@ GMPChild::PreLoadLibraries(const std::st
}
#endif
#if defined(MOZ_GMP_SANDBOX)
#if defined(XP_MACOSX)
class MacOSXSandboxStarter : public SandboxStarter {
public:
- MacOSXSandboxStarter(GMPChild* aGMPChild)
+ explicit MacOSXSandboxStarter(GMPChild* aGMPChild)
: mGMPChild(aGMPChild)
{}
virtual void Start(const char* aLibPath) MOZ_OVERRIDE {
mGMPChild->StartMacSandbox();
}
private:
GMPChild* mGMPChild;
};
--- a/dom/media/gmp/GMPStorageChild.cpp
+++ b/dom/media/gmp/GMPStorageChild.cpp
@@ -288,17 +288,17 @@ GMPStorageChild::EnumerateRecords(RecvGM
CALL_ON_GMP_THREAD(SendGetRecordNames);
return GMPNoErr;
}
class GMPRecordIteratorImpl : public GMPRecordIterator {
public:
- GMPRecordIteratorImpl(const InfallibleTArray<nsCString>& aRecordNames)
+ explicit GMPRecordIteratorImpl(const InfallibleTArray<nsCString>& aRecordNames)
: mRecordNames(aRecordNames)
, mIndex(0)
{
mRecordNames.Sort();
}
virtual GMPErr GetName(const char** aOutName, uint32_t* aOutNameLength) MOZ_OVERRIDE {
if (!aOutName || !aOutNameLength) {
--- a/dom/media/gmp/gmp-api/gmp-errors.h
+++ b/dom/media/gmp/gmp-api/gmp-errors.h
@@ -42,15 +42,16 @@ typedef enum {
GMPRecordInUse = 5,
GMPQuotaExceededErr = 6,
GMPDecodeErr = 7,
GMPEncodeErr = 8,
GMPNoKeyErr = 9,
GMPCryptoErr = 10,
GMPEndOfEnumeration = 11,
GMPInvalidArgErr = 12,
+ GMPAbortedErr = 13,
GMPLastErr // Placeholder, must be last. This enum's values must remain consecutive!
} GMPErr;
#define GMP_SUCCEEDED(x) ((x) == GMPNoErr)
#define GMP_FAILED(x) ((x) != GMPNoErr)
#endif // GMP_ERRORS_h_
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -56,34 +56,41 @@ ContainerParser::ParseStartAndEndTimesta
int64_t& aStart, int64_t& aEnd)
{
return false;
}
bool
ContainerParser::TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
{
- NS_WARNING("Using default ContainerParser::TimestampFuzzyEquals implementation");
- return aLhs == aRhs;
+ return llabs(aLhs - aRhs) <= GetRoundingError();
+}
+
+int64_t
+ContainerParser::GetRoundingError()
+{
+ NS_WARNING("Using default ContainerParser::GetRoundingError implementation");
+ return 0;
}
const nsTArray<uint8_t>&
ContainerParser::InitData()
{
MOZ_ASSERT(mHasInitData);
return mInitData;
}
class WebMContainerParser : public ContainerParser {
public:
WebMContainerParser()
: mParser(0), mOffset(0)
{}
static const unsigned NS_PER_USEC = 1000;
+ static const unsigned USEC_PER_SEC = 1000000;
bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
{
ContainerParser::IsInitSegmentPresent(aData, aLength);
// XXX: This is overly primitive, needs to collect data as it's appended
// to the SB and handle, rather than assuming everything is present in a
// single aData segment.
// 0x1a45dfa3 // EBML
@@ -177,20 +184,20 @@ public:
this, aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
mapping.RemoveElementsAt(0, endIdx + 1);
mOverlappedMapping.AppendElements(mapping);
return true;
}
- bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
+ int64_t GetRoundingError()
{
int64_t error = mParser.GetTimecodeScale() / NS_PER_USEC;
- return llabs(aLhs - aRhs) <= error * 2;
+ return error * 2;
}
private:
WebMBufferedParser mParser;
nsTArray<WebMTimeDataOffset> mOverlappedMapping;
int64_t mOffset;
};
@@ -271,19 +278,19 @@ public:
}
aStart = compositionRange.start;
aEnd = compositionRange.end;
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld]",
this, aStart, aEnd);
return true;
}
- bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
+ int64_t GetRoundingError()
{
- return llabs(aLhs - aRhs) <= 1000;
+ return 1000;
}
private:
nsRefPtr<mp4_demuxer::BufferStream> mStream;
nsAutoPtr<mp4_demuxer::MoofParser> mParser;
};
/*static*/ ContainerParser*
--- a/dom/media/mediasource/ContainerParser.h
+++ b/dom/media/mediasource/ContainerParser.h
@@ -30,17 +30,19 @@ public:
// segment. aData may not start on a parser sync boundary. Return true
// if aStart and aEnd have been updated.
virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
int64_t& aStart, int64_t& aEnd);
// Compare aLhs and rHs, considering any error that may exist in the
// timestamps from the format's base representation. Return true if aLhs
// == aRhs within the error epsilon.
- virtual bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs);
+ bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs);
+
+ virtual int64_t GetRoundingError();
const nsTArray<uint8_t>& InitData();
bool HasInitData()
{
return mHasInitData;
}
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -89,156 +89,213 @@ MediaSourceReader::IsWaitingMediaResourc
if (!mEssentialTrackBuffers[i]->IsReady()) {
return true;
}
}
return !mHasEssentialTrackBuffers;
}
-void
+nsRefPtr<MediaDecoderReader::AudioDataPromise>
MediaSourceReader::RequestAudioData()
{
+ nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
MSE_DEBUGV("MediaSourceReader(%p)::RequestAudioData", this);
if (!mAudioReader) {
MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called with no audio reader", this);
- GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, DECODE_ERROR);
- return;
+ mAudioPromise.Reject(DECODE_ERROR, __func__);
+ return p;
}
mAudioIsSeeking = false;
SwitchAudioReader(mLastAudioTime);
- mAudioReader->RequestAudioData();
+ mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this,
+ &MediaSourceReader::OnAudioDecoded,
+ &MediaSourceReader::OnAudioNotDecoded);
+ return p;
}
void
MediaSourceReader::OnAudioDecoded(AudioData* aSample)
{
MSE_DEBUGV("MediaSourceReader(%p)::OnAudioDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]",
this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity);
if (mDropAudioBeforeThreshold) {
if (aSample->mTime < mTimeThreshold) {
MSE_DEBUG("MediaSourceReader(%p)::OnAudioDecoded mTime=%lld < mTimeThreshold=%lld",
this, aSample->mTime, mTimeThreshold);
- mAudioReader->RequestAudioData();
+ mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this,
+ &MediaSourceReader::OnAudioDecoded,
+ &MediaSourceReader::OnAudioNotDecoded);
return;
}
mDropAudioBeforeThreshold = false;
}
// Any OnAudioDecoded callbacks received while mAudioIsSeeking must be not
// update our last used timestamp, as these are emitted by the reader we're
// switching away from.
if (!mAudioIsSeeking) {
mLastAudioTime = aSample->mTime + aSample->mDuration;
}
- GetCallback()->OnAudioDecoded(aSample);
+
+ mAudioPromise.Resolve(aSample, __func__);
+}
+
+// Find the closest approximation to the end time for this stream.
+// mLast{Audio,Video}Time differs from the actual end time because of
+// Bug 1065207 - the duration of a WebM fragment is an estimate not the
+// actual duration. In the case of audio time an example of where they
+// differ would be the actual sample duration being small but the
+// previous sample being large. The buffered end time uses that last
+// sample duration as an estimate of the end time duration giving an end
+// time that is greater than mLastAudioTime, which is the actual sample
+// end time.
+// Reader switching is based on the buffered end time though so they can be
+// quite different. By using the EOS_FUZZ_US and the buffered end time we
+// attempt to account for this difference.
+static void
+AdjustEndTime(int64_t* aEndTime, MediaDecoderReader* aReader)
+{
+ if (aReader) {
+ nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
+ aReader->GetBuffered(ranges);
+ if (ranges->Length() > 0) {
+ // End time is a double so we convert to nearest by adding 0.5.
+ int64_t end = ranges->GetEndTime() * USECS_PER_S + 0.5;
+ *aEndTime = std::max(*aEndTime, end);
+ }
+ }
}
void
+MediaSourceReader::OnAudioNotDecoded(NotDecodedReason aReason)
+{
+ MSE_DEBUG("MediaSourceReader(%p)::OnAudioNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded());
+ if (aReason == DECODE_ERROR || aReason == CANCELED) {
+ mAudioPromise.Reject(aReason, __func__);
+ return;
+ }
+
+ // End of stream. Force switching past this stream to another reader by
+ // switching to the end of the buffered range.
+ MOZ_ASSERT(aReason == END_OF_STREAM);
+ if (mAudioReader) {
+ AdjustEndTime(&mLastAudioTime, mAudioReader);
+ }
+
+ // See if we can find a different reader that can pick up where we left off. We use the
+ // EOS_FUZZ_US to allow for the fact that our end time can be inaccurate due to bug
+ // 1065207.
+ if (SwitchAudioReader(mLastAudioTime + EOS_FUZZ_US)) {
+ mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this,
+ &MediaSourceReader::OnAudioDecoded,
+ &MediaSourceReader::OnAudioNotDecoded);
+ return;
+ }
+
+ // If the entire MediaSource is done, generate an EndOfStream.
+ if (IsEnded()) {
+ mAudioPromise.Reject(END_OF_STREAM, __func__);
+ return;
+ }
+
+ // We don't have the data the caller wants. Tell that we're waiting for JS to
+ // give us more data.
+ mAudioPromise.Reject(WAITING_FOR_DATA, __func__);
+}
+
+
+nsRefPtr<MediaDecoderReader::VideoDataPromise>
MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold)
{
+ nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
MSE_DEBUGV("MediaSourceReader(%p)::RequestVideoData(%d, %lld)",
this, aSkipToNextKeyframe, aTimeThreshold);
if (!mVideoReader) {
MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called with no video reader", this);
- GetCallback()->OnNotDecoded(MediaData::VIDEO_DATA, DECODE_ERROR);
- return;
+ mVideoPromise.Reject(DECODE_ERROR, __func__);
+ return p;
}
if (aSkipToNextKeyframe) {
mTimeThreshold = aTimeThreshold;
mDropAudioBeforeThreshold = true;
mDropVideoBeforeThreshold = true;
}
mVideoIsSeeking = false;
SwitchVideoReader(mLastVideoTime);
- mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
+
+ mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold)
+ ->Then(GetTaskQueue(), __func__, this,
+ &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded);
+ return p;
}
void
MediaSourceReader::OnVideoDecoded(VideoData* aSample)
{
MSE_DEBUGV("MediaSourceReader(%p)::OnVideoDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]",
this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity);
if (mDropVideoBeforeThreshold) {
if (aSample->mTime < mTimeThreshold) {
MSE_DEBUG("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld < mTimeThreshold=%lld",
this, aSample->mTime, mTimeThreshold);
- mVideoReader->RequestVideoData(false, 0);
+ mVideoReader->RequestVideoData(false, 0)->Then(GetTaskQueue(), __func__, this,
+ &MediaSourceReader::OnVideoDecoded,
+ &MediaSourceReader::OnVideoNotDecoded);
return;
}
mDropVideoBeforeThreshold = false;
}
// Any OnVideoDecoded callbacks received while mVideoIsSeeking must be not
// update our last used timestamp, as these are emitted by the reader we're
// switching away from.
if (!mVideoIsSeeking) {
mLastVideoTime = aSample->mTime + aSample->mDuration;
}
- GetCallback()->OnVideoDecoded(aSample);
+
+ mVideoPromise.Resolve(aSample, __func__);
}
void
-MediaSourceReader::OnNotDecoded(MediaData::Type aType, NotDecodedReason aReason)
+MediaSourceReader::OnVideoNotDecoded(NotDecodedReason aReason)
{
- MSE_DEBUG("MediaSourceReader(%p)::OnNotDecoded aType=%u aReason=%u IsEnded: %d", this, aType, aReason, IsEnded());
+ MSE_DEBUG("MediaSourceReader(%p)::OnVideoNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded());
if (aReason == DECODE_ERROR || aReason == CANCELED) {
- GetCallback()->OnNotDecoded(aType, aReason);
+ mVideoPromise.Reject(aReason, __func__);
return;
}
+
// End of stream. Force switching past this stream to another reader by
// switching to the end of the buffered range.
MOZ_ASSERT(aReason == END_OF_STREAM);
- nsRefPtr<MediaDecoderReader> reader = aType == MediaData::AUDIO_DATA ?
- mAudioReader : mVideoReader;
-
- // Find the closest approximation to the end time for this stream.
- // mLast{Audio,Video}Time differs from the actual end time because of
- // Bug 1065207 - the duration of a WebM fragment is an estimate not the
- // actual duration. In the case of audio time an example of where they
- // differ would be the actual sample duration being small but the
- // previous sample being large. The buffered end time uses that last
- // sample duration as an estimate of the end time duration giving an end
- // time that is greater than mLastAudioTime, which is the actual sample
- // end time.
- // Reader switching is based on the buffered end time though so they can be
- // quite different. By using the EOS_FUZZ_US and the buffered end time we
- // attempt to account for this difference.
- int64_t* time = aType == MediaData::AUDIO_DATA ? &mLastAudioTime : &mLastVideoTime;
- if (reader) {
- nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
- reader->GetBuffered(ranges);
- if (ranges->Length() > 0) {
- // End time is a double so we convert to nearest by adding 0.5.
- int64_t end = ranges->GetEndTime() * USECS_PER_S + 0.5;
- *time = std::max(*time, end);
- }
+ if (mVideoReader) {
+ AdjustEndTime(&mLastVideoTime, mAudioReader);
}
// See if we can find a different reader that can pick up where we left off. We use the
// EOS_FUZZ_US to allow for the fact that our end time can be inaccurate due to bug
- // 1065207 - the duration of a WebM frame is an estimate.
- if (aType == MediaData::AUDIO_DATA && SwitchAudioReader(*time + EOS_FUZZ_US)) {
- RequestAudioData();
- return;
- }
- if (aType == MediaData::VIDEO_DATA && SwitchVideoReader(*time + EOS_FUZZ_US)) {
- RequestVideoData(false, 0);
+ // 1065207.
+ if (SwitchVideoReader(mLastVideoTime + EOS_FUZZ_US)) {
+ mVideoReader->RequestVideoData(false, 0)
+ ->Then(GetTaskQueue(), __func__, this,
+ &MediaSourceReader::OnVideoDecoded,
+ &MediaSourceReader::OnVideoNotDecoded);
return;
}
// If the entire MediaSource is done, generate an EndOfStream.
if (IsEnded()) {
- GetCallback()->OnNotDecoded(aType, END_OF_STREAM);
+ mVideoPromise.Reject(END_OF_STREAM, __func__);
return;
}
// We don't have the data the caller wants. Tell that we're waiting for JS to
// give us more data.
- GetCallback()->OnNotDecoded(aType, WAITING_FOR_DATA);
+ mVideoPromise.Reject(WAITING_FOR_DATA, __func__);
}
nsRefPtr<ShutdownPromise>
MediaSourceReader::Shutdown()
{
MOZ_ASSERT(mMediaSourceShutdownPromise.IsEmpty());
nsRefPtr<ShutdownPromise> p = mMediaSourceShutdownPromise.Ensure(__func__);
@@ -259,16 +316,19 @@ MediaSourceReader::ContinueShutdown(bool
return;
}
mAudioTrack = nullptr;
mAudioReader = nullptr;
mVideoTrack = nullptr;
mVideoReader = nullptr;
+ MOZ_ASSERT(mAudioPromise.IsEmpty());
+ MOZ_ASSERT(mVideoPromise.IsEmpty());
+
MediaDecoderReader::Shutdown()->ChainTo(mMediaSourceShutdownPromise.Steal(), __func__);
}
void
MediaSourceReader::BreakCycles()
{
MediaDecoderReader::BreakCycles();
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -41,25 +41,24 @@ public:
}
// Indicates the point in time at which the reader should consider
// registered TrackBuffers essential for initialization.
void PrepareInitialization();
bool IsWaitingMediaResources() MOZ_OVERRIDE;
- void RequestAudioData() MOZ_OVERRIDE;
+ nsRefPtr<AudioDataPromise> RequestAudioData() MOZ_OVERRIDE;
+ nsRefPtr<VideoDataPromise>
+ RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
void OnAudioDecoded(AudioData* aSample);
-
- void RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
-
+ void OnAudioNotDecoded(NotDecodedReason aReason);
void OnVideoDecoded(VideoData* aSample);
-
- void OnNotDecoded(MediaData::Type aType, NotDecodedReason aReason);
+ void OnVideoNotDecoded(NotDecodedReason aReason);
void OnSeekCompleted(nsresult aResult);
bool HasVideo() MOZ_OVERRIDE
{
return mInfo.HasVideo();
}
@@ -135,16 +134,19 @@ private:
nsRefPtr<MediaDecoderReader> mVideoReader;
nsTArray<nsRefPtr<TrackBuffer>> mTrackBuffers;
nsTArray<nsRefPtr<TrackBuffer>> mShutdownTrackBuffers;
nsTArray<nsRefPtr<TrackBuffer>> mEssentialTrackBuffers;
nsRefPtr<TrackBuffer> mAudioTrack;
nsRefPtr<TrackBuffer> mVideoTrack;
+ MediaPromiseHolder<AudioDataPromise> mAudioPromise;
+ MediaPromiseHolder<VideoDataPromise> mVideoPromise;
+
#ifdef MOZ_EME
nsRefPtr<CDMProxy> mCDMProxy;
#endif
// These are read and written on the decode task queue threads.
int64_t mLastAudioTime;
int64_t mLastVideoTime;
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -197,39 +197,95 @@ TrackBuffer::AppendDataToCurrentResource
mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
aLength, appendOffset);
mParentDecoder->NotifyBytesDownloaded();
mParentDecoder->NotifyTimeRangesChanged();
return true;
}
+class DecoderSorter
+{
+public:
+ bool LessThan(SourceBufferDecoder* aFirst, SourceBufferDecoder* aSecond) const
+ {
+ nsRefPtr<dom::TimeRanges> first = new dom::TimeRanges();
+ aFirst->GetBuffered(first);
+
+ nsRefPtr<dom::TimeRanges> second = new dom::TimeRanges();
+ aSecond->GetBuffered(second);
+
+ return first->GetStartTime() < second->GetStartTime();
+ }
+
+ bool Equals(SourceBufferDecoder* aFirst, SourceBufferDecoder* aSecond) const
+ {
+ nsRefPtr<dom::TimeRanges> first = new dom::TimeRanges();
+ aFirst->GetBuffered(first);
+
+ nsRefPtr<dom::TimeRanges> second = new dom::TimeRanges();
+ aSecond->GetBuffered(second);
+
+ return first->GetStartTime() == second->GetStartTime();
+ }
+};
+
bool
TrackBuffer::EvictData(uint32_t aThreshold)
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
int64_t totalSize = 0;
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
totalSize += mDecoders[i]->GetResource()->GetSize();
}
int64_t toEvict = totalSize - aThreshold;
if (toEvict <= 0) {
return false;
}
- for (uint32_t i = 0; i < mInitializedDecoders.Length(); ++i) {
+ // Get a list of initialized decoders, sorted by their start times.
+ nsTArray<SourceBufferDecoder*> decoders;
+ decoders.AppendElements(mInitializedDecoders);
+ decoders.Sort(DecoderSorter());
+
+ // First try to evict data before the current play position, starting
+ // with the earliest time.
+ uint32_t i = 0;
+ for (; i < decoders.Length(); ++i) {
MSE_DEBUG("TrackBuffer(%p)::EvictData decoder=%u threshold=%u toEvict=%lld",
this, i, aThreshold, toEvict);
- toEvict -= mInitializedDecoders[i]->GetResource()->EvictData(toEvict);
- if (!mInitializedDecoders[i]->GetResource()->GetSize() &&
- mInitializedDecoders[i] != mCurrentDecoder) {
- RemoveDecoder(mInitializedDecoders[i]);
+ toEvict -= decoders[i]->GetResource()->EvictData(toEvict);
+ if (!decoders[i]->GetResource()->GetSize() &&
+ decoders[i] != mCurrentDecoder) {
+ RemoveDecoder(decoders[i]);
+ }
+ if (toEvict <= 0 || decoders[i] == mCurrentDecoder) {
+ break;
+ }
+ }
+
+ // If we still need to evict more, then try to evict entire decoders,
+ // starting from the end.
+ if (toEvict > 0) {
+ uint32_t end = i;
+ MOZ_ASSERT(decoders[end] == mCurrentDecoder);
+
+ for (i = decoders.Length() - 1; i > end; --i) {
+ MSE_DEBUG("TrackBuffer(%p)::EvictData removing entire decoder=%u from end toEvict=%lld",
+ this, i, toEvict);
+ // TODO: We could implement forward-eviction within a decoder and
+ // be able to evict within the current decoder.
+ toEvict -= decoders[i]->GetResource()->GetSize();
+ RemoveDecoder(decoders[i]);
+ if (toEvict <= 0) {
+ break;
+ }
}
}
return toEvict < (totalSize - aThreshold);
}
void
TrackBuffer::EvictBefore(double aTime)
{
@@ -255,17 +311,17 @@ TrackBuffer::Buffered(dom::TimeRanges* a
double highestEndTime = 0;
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
mDecoders[i]->GetBuffered(r);
if (r->Length() > 0) {
highestEndTime = std::max(highestEndTime, r->GetEndTime());
- aRanges->Union(r);
+ aRanges->Union(r, double(mParser->GetRoundingError()) / USECS_PER_S);
}
}
return highestEndTime;
}
already_AddRefed<SourceBufferDecoder>
TrackBuffer::NewDecoder()
--- a/dom/media/ogg/OggReader.cpp
+++ b/dom/media/ogg/OggReader.cpp
@@ -971,17 +971,17 @@ bool OggReader::ReadOggPage(ogg_page* aP
NS_ENSURE_TRUE(ret == 0, false);
}
return true;
}
ogg_packet* OggReader::NextOggPacket(OggCodecState* aCodecState)
{
- NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+ MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread.");
if (!aCodecState || !aCodecState->mActive) {
return nullptr;
}
ogg_packet* packet;
while ((packet = aCodecState->PacketOut()) == nullptr) {
// The codec state does not have any buffered pages, so try to read another
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -342,16 +342,18 @@ MediaCodecReader::ReleaseMediaResources(
mAudioTrack.mSourceIsStopped = true;
}
ReleaseCriticalResources();
}
nsRefPtr<ShutdownPromise>
MediaCodecReader::Shutdown()
{
+ MOZ_ASSERT(mAudioPromise.IsEmpty());
+ MOZ_ASSERT(mVideoPromise.IsEmpty());
ReleaseResources();
return MediaDecoderReader::Shutdown();
}
void
MediaCodecReader::DispatchAudioTask()
{
if (mAudioTrack.mTaskQueue && mAudioTrack.mTaskQueue->IsEmpty()) {
@@ -369,41 +371,48 @@ MediaCodecReader::DispatchVideoTask(int6
RefPtr<nsIRunnable> task =
NS_NewRunnableMethodWithArg<int64_t>(this,
&MediaCodecReader::DecodeVideoFrameTask,
aTimeThreshold);
mVideoTrack.mTaskQueue->Dispatch(task);
}
}
-void
+nsRefPtr<MediaDecoderReader::AudioDataPromise>
MediaCodecReader::RequestAudioData()
{
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
MOZ_ASSERT(HasAudio());
+
+ nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
if (CheckAudioResources()) {
DispatchAudioTask();
}
+
+ return p;
}
-void
+nsRefPtr<MediaDecoderReader::VideoDataPromise>
MediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
int64_t aTimeThreshold)
{
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
MOZ_ASSERT(HasVideo());
+ nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
int64_t threshold = sInvalidTimestampUs;
if (aSkipToNextKeyframe && IsValidTimestampUs(aTimeThreshold)) {
mVideoTrack.mTaskQueue->Flush();
threshold = aTimeThreshold;
}
if (CheckVideoResources()) {
DispatchVideoTask(threshold);
}
+
+ return p;
}
bool
MediaCodecReader::DecodeAudioDataSync()
{
if (mAudioTrack.mCodec == nullptr || !mAudioTrack.mCodec->allocated() ||
mAudioTrack.mOutputEndOfStream) {
return false;
@@ -479,41 +488,41 @@ MediaCodecReader::DecodeAudioDataTask()
bool result = DecodeAudioDataSync();
if (AudioQueue().GetSize() > 0) {
nsRefPtr<AudioData> a = AudioQueue().PopFront();
if (a) {
if (mAudioTrack.mDiscontinuity) {
a->mDiscontinuity = true;
mAudioTrack.mDiscontinuity = false;
}
- GetCallback()->OnAudioDecoded(a);
+ mAudioPromise.Resolve(a, __func__);
}
}
- if (AudioQueue().AtEndOfStream()) {
- GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, END_OF_STREAM);
+ else if (AudioQueue().AtEndOfStream()) {
+ mAudioPromise.Reject(END_OF_STREAM, __func__);
}
return result;
}
bool
MediaCodecReader::DecodeVideoFrameTask(int64_t aTimeThreshold)
{
bool result = DecodeVideoFrameSync(aTimeThreshold);
if (VideoQueue().GetSize() > 0) {
nsRefPtr<VideoData> v = VideoQueue().PopFront();
if (v) {
if (mVideoTrack.mDiscontinuity) {
v->mDiscontinuity = true;
mVideoTrack.mDiscontinuity = false;
}
- GetCallback()->OnVideoDecoded(v);
+ mVideoPromise.Resolve(v, __func__);
}
}
- if (VideoQueue().AtEndOfStream()) {
- GetCallback()->OnNotDecoded(MediaData::VIDEO_DATA, END_OF_STREAM);
+ else if (VideoQueue().AtEndOfStream()) {
+ mVideoPromise.Reject(END_OF_STREAM, __func__);
}
return result;
}
bool
MediaCodecReader::HasAudio()
{
return mInfo.mAudio.mHasAudio;
--- a/dom/media/omx/MediaCodecReader.h
+++ b/dom/media/omx/MediaCodecReader.h
@@ -74,21 +74,22 @@ public:
// all contents have been continuously parsed. (ex. total duration of some
// variable-bit-rate MP3 files.)
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
// Flush the MediaTaskQueue, flush MediaCodec and raise the mDiscontinuity.
virtual nsresult ResetDecode() MOZ_OVERRIDE;
// Disptach a DecodeVideoFrameTask to decode video data.
- virtual void RequestVideoData(bool aSkipToNextKeyframe,
- int64_t aTimeThreshold) MOZ_OVERRIDE;
+ virtual nsRefPtr<VideoDataPromise>
+ RequestVideoData(bool aSkipToNextKeyframe,
+ int64_t aTimeThreshold) MOZ_OVERRIDE;
// Disptach a DecodeAduioDataTask to decode video data.
- virtual void RequestAudioData() MOZ_OVERRIDE;
+ virtual nsRefPtr<AudioDataPromise> RequestAudioData() MOZ_OVERRIDE;
virtual bool HasAudio();
virtual bool HasVideo();
virtual void PreReadMetadata() MOZ_OVERRIDE;
// Read header data for all bitstreams in the file. Fills aInfo with
// the data required to present the media, and optionally fills *aTags
// with tag metadata from the file.
@@ -426,16 +427,19 @@ private:
Mutex mTextureClientIndexesLock;
nsDataHashtable<nsPtrHashKey<TextureClient>, size_t> mTextureClientIndexes;
// media tracks
AudioTrack mAudioTrack;
VideoTrack mVideoTrack;
AudioTrack mAudioOffloadTrack; // only Track::mSource is valid
+ MediaPromiseHolder<AudioDataPromise> mAudioPromise;
+ MediaPromiseHolder<VideoDataPromise> mVideoPromise;
+
// color converter
android::I420ColorConverterHelper mColorConverter;
nsAutoArrayPtr<uint8_t> mColorConverterBuffer;
size_t mColorConverterBufferSize;
// incremental parser
Monitor mParserMonitor;
bool mParseDataFromCache;
--- a/dom/media/omx/RtspMediaCodecReader.cpp
+++ b/dom/media/omx/RtspMediaCodecReader.cpp
@@ -69,29 +69,29 @@ RtspMediaCodecReader::EnsureActive()
nsIStreamingProtocolController* controller =
mRtspResource->GetMediaStreamController();
if (controller) {
controller->Play();
}
mRtspResource->SetSuspend(false);
}
-void
+nsRefPtr<MediaDecoderReader::AudioDataPromise>
RtspMediaCodecReader::RequestAudioData()
{
EnsureActive();
- MediaCodecReader::RequestAudioData();
+ return MediaCodecReader::RequestAudioData();
}
-void
+nsRefPtr<MediaDecoderReader::VideoDataPromise>
RtspMediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
int64_t aTimeThreshold)
{
EnsureActive();
- MediaCodecReader::RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
+ return MediaCodecReader::RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
}
nsresult
RtspMediaCodecReader::ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags)
{
mRtspResource->DisablePlayoutDelay();
EnsureActive();
--- a/dom/media/omx/RtspMediaCodecReader.h
+++ b/dom/media/omx/RtspMediaCodecReader.h
@@ -50,21 +50,22 @@ public:
// data so the |GetBuffered| function can retrieve useful time ranges.
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE {
return NS_ERROR_NOT_IMPLEMENTED;
}
virtual void SetIdle() MOZ_OVERRIDE;
// Disptach a DecodeVideoFrameTask to decode video data.
- virtual void RequestVideoData(bool aSkipToNextKeyframe,
- int64_t aTimeThreshold) MOZ_OVERRIDE;
+ virtual nsRefPtr<VideoDataPromise>
+ RequestVideoData(bool aSkipToNextKeyframe,
+ int64_t aTimeThreshold) MOZ_OVERRIDE;
// Disptach a DecodeAudioDataTask to decode audio data.
- virtual void RequestAudioData() MOZ_OVERRIDE;
+ virtual nsRefPtr<AudioDataPromise> RequestAudioData() MOZ_OVERRIDE;
virtual nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags) MOZ_OVERRIDE;
private:
// A pointer to RtspMediaResource for calling the Rtsp specific function.
// The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder
// holds the MediaDecoderStateMachine and RtspMediaResource.
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -469,32 +469,32 @@ AudioContext::DecodeAudioData(const Arra
// Neuter the array buffer
size_t length = aBuffer.Length();
JS::RootedObject obj(cx, aBuffer.Obj());
uint8_t* data = static_cast<uint8_t*>(JS_StealArrayBufferContents(cx, obj));
// Sniff the content of the media.
- // Failed type sniffing will be handled by AsyncDecodeMedia.
+ // Failed type sniffing will be handled by AsyncDecodeWebAudio.
nsAutoCString contentType;
NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr, data, length, contentType);
nsRefPtr<DecodeErrorCallback> failureCallback;
nsRefPtr<DecodeSuccessCallback> successCallback;
if (aFailureCallback.WasPassed()) {
failureCallback = &aFailureCallback.Value();
}
if (aSuccessCallback.WasPassed()) {
successCallback = &aSuccessCallback.Value();
}
nsRefPtr<WebAudioDecodeJob> job(
new WebAudioDecodeJob(contentType, this,
promise, successCallback, failureCallback));
- mDecoder.AsyncDecodeMedia(contentType.get(), data, length, *job);
+ AsyncDecodeWebAudio(contentType.get(), data, length, *job);
// Transfer the ownership to mDecodeJobs
mDecodeJobs.AppendElement(job.forget());
return promise.forget();
}
void
AudioContext::RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob)
@@ -580,18 +580,16 @@ AudioContext::Shutdown()
// We mute rather than suspending, because the delay between the ::Shutdown
// call and the CC would make us overbuffer in the MediaStreamGraph.
// See bug 936784 for details.
if (!mIsOffline) {
Mute();
}
- mDecoder.Shutdown();
-
// Release references to active nodes.
// Active AudioNodes don't unregister in destructors, at which point the
// Node is already unregistered.
mActiveNodes.Clear();
// For offline contexts, we can destroy the MediaStreamGraph at this point.
if (mIsOffline && mDestination) {
mDestination->OfflineShutdown();
@@ -696,17 +694,16 @@ AudioContext::SizeOfIncludingThis(mozill
// AudioNodes are tracked separately because we do not want the AudioContext
// to track all of the AudioNodes it creates, so we wouldn't be able to
// traverse them from here.
size_t amount = aMallocSizeOf(this);
if (mListener) {
amount += mListener->SizeOfIncludingThis(aMallocSizeOf);
}
- amount += mDecoder.SizeOfExcludingThis(aMallocSizeOf);
amount += mDecodeJobs.SizeOfExcludingThis(aMallocSizeOf);
for (uint32_t i = 0; i < mDecodeJobs.Length(); ++i) {
amount += mDecodeJobs[i]->SizeOfIncludingThis(aMallocSizeOf);
}
amount += mActiveNodes.SizeOfExcludingThis(nullptr, aMallocSizeOf);
amount += mPannerNodes.SizeOfExcludingThis(nullptr, aMallocSizeOf);
return amount;
}
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -266,17 +266,16 @@ private:
friend struct ::mozilla::WebAudioDecodeJob;
private:
// Note that it's important for mSampleRate to be initialized before
// mDestination, as mDestination's constructor needs to access it!
const float mSampleRate;
nsRefPtr<AudioDestinationNode> mDestination;
nsRefPtr<AudioListener> mListener;
- MediaBufferDecoder mDecoder;
nsTArray<nsRefPtr<WebAudioDecodeJob> > mDecodeJobs;
// See RegisterActiveNode. These will keep the AudioContext alive while it
// is rendering and the window remains alive.
nsTHashtable<nsRefPtrHashKey<AudioNode> > mActiveNodes;
// Hashsets containing all the PannerNodes, to compute the doppler shift.
// These are weak pointers.
nsTHashtable<nsPtrHashKey<PannerNode> > mPannerNodes;
// Number of channels passed in the OfflineAudioContext ctor.
--- a/dom/media/webaudio/BufferDecoder.cpp
+++ b/dom/media/webaudio/BufferDecoder.cpp
@@ -32,20 +32,20 @@ BufferDecoder::BufferDecoder(MediaResour
BufferDecoder::~BufferDecoder()
{
// The dtor may run on any thread, we cannot be sure.
MOZ_COUNT_DTOR(BufferDecoder);
}
void
-BufferDecoder::BeginDecoding(nsIThread* aDecodeThread)
+BufferDecoder::BeginDecoding(MediaTaskQueue* aTaskQueueIdentity)
{
- MOZ_ASSERT(!mDecodeThread && aDecodeThread);
- mDecodeThread = aDecodeThread;
+ MOZ_ASSERT(!mTaskQueueIdentity && aTaskQueueIdentity);
+ mTaskQueueIdentity = aTaskQueueIdentity;
}
ReentrantMonitor&
BufferDecoder::GetReentrantMonitor()
{
return mReentrantMonitor;
}
@@ -61,18 +61,18 @@ BufferDecoder::OnStateMachineThread() co
{
// BufferDecoder doesn't have the concept of a state machine.
return true;
}
bool
BufferDecoder::OnDecodeThread() const
{
- MOZ_ASSERT(mDecodeThread, "Forgot to call BeginDecoding?");
- return IsCurrentThread(mDecodeThread);
+ MOZ_ASSERT(mTaskQueueIdentity, "Forgot to call BeginDecoding?");
+ return mTaskQueueIdentity->IsCurrentThreadIn();
}
MediaResource*
BufferDecoder::GetResource() const
{
return mResource;
}
--- a/dom/media/webaudio/BufferDecoder.h
+++ b/dom/media/webaudio/BufferDecoder.h
@@ -3,16 +3,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef BUFFER_DECODER_H_
#define BUFFER_DECODER_H_
#include "AbstractMediaDecoder.h"
+#include "MediaTaskQueue.h"
#include "mozilla/Attributes.h"
#include "mozilla/ReentrantMonitor.h"
namespace mozilla {
/**
* This class provides a decoder object which decodes a media file that lives in
* a memory buffer.
@@ -22,17 +23,17 @@ class BufferDecoder : public AbstractMed
public:
// This class holds a weak pointer to MediaResource. It's the responsibility
// of the caller to manage the memory of the MediaResource object.
explicit BufferDecoder(MediaResource* aResource);
NS_DECL_THREADSAFE_ISUPPORTS
// This has to be called before decoding begins
- void BeginDecoding(nsIThread* aDecodeThread);
+ void BeginDecoding(MediaTaskQueue* aTaskQueueIdentity);
virtual ReentrantMonitor& GetReentrantMonitor() MOZ_FINAL MOZ_OVERRIDE;
virtual bool IsShutdown() const MOZ_FINAL MOZ_OVERRIDE;
virtual bool OnStateMachineThread() const MOZ_FINAL MOZ_OVERRIDE;
virtual bool OnDecodeThread() const MOZ_FINAL MOZ_OVERRIDE;
@@ -78,15 +79,15 @@ public:
private:
virtual ~BufferDecoder();
// This monitor object is not really used to synchronize access to anything.
// It's just there in order for us to be able to override
// GetReentrantMonitor correctly.
ReentrantMonitor mReentrantMonitor;
- nsCOMPtr<nsIThread> mDecodeThread;
+ nsRefPtr<MediaTaskQueue> mTaskQueueIdentity;
nsRefPtr<MediaResource> mResource;
};
} // namespace mozilla
#endif /* BUFFER_DECODER_H_ */
--- a/dom/media/webaudio/MediaBufferDecoder.cpp
+++ b/dom/media/webaudio/MediaBufferDecoder.cpp
@@ -15,21 +15,19 @@
#include "BufferMediaResource.h"
#include "DecoderTraits.h"
#include "AudioContext.h"
#include "AudioBuffer.h"
#include "nsAutoPtr.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIScriptError.h"
#include "nsMimeTypes.h"
+#include "VideoUtils.h"
#include "WebAudioUtils.h"
#include "mozilla/dom/Promise.h"
-#ifdef XP_WIN
-#include "ThreadPoolCOMListener.h"
-#endif
namespace mozilla {
NS_IMPL_CYCLE_COLLECTION_CLASS(WebAudioDecodeJob)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WebAudioDecodeJob)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutput)
@@ -90,38 +88,37 @@ MOZ_BEGIN_ENUM_CLASS(PhaseEnum, int)
Done
MOZ_END_ENUM_CLASS(PhaseEnum)
class MediaDecodeTask : public nsRunnable
{
public:
MediaDecodeTask(const char* aContentType, uint8_t* aBuffer,
uint32_t aLength,
- WebAudioDecodeJob& aDecodeJob,
- nsIThreadPool* aThreadPool)
+ WebAudioDecodeJob& aDecodeJob)
: mContentType(aContentType)
, mBuffer(aBuffer)
, mLength(aLength)
, mDecodeJob(aDecodeJob)
, mPhase(PhaseEnum::Decode)
- , mThreadPool(aThreadPool)
{
MOZ_ASSERT(aBuffer);
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(mDecodeJob.mContext->GetParentObject());
nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
do_QueryInterface(pWindow);
if (scriptPrincipal) {
mPrincipal = scriptPrincipal->GetPrincipal();
}
}
NS_IMETHOD Run();
bool CreateReader();
+ MediaDecoderReader* Reader() { MOZ_ASSERT(mDecoderReader); return mDecoderReader; }
private:
void ReportFailureOnMainThread(WebAudioDecodeJob::ErrorCode aErrorCode) {
if (NS_IsMainThread()) {
Cleanup();
mDecodeJob.OnFailure(aErrorCode);
} else {
// Take extra care to cleanup on the main thread
@@ -129,16 +126,20 @@ private:
nsCOMPtr<nsIRunnable> event =
new ReportResultTask(mDecodeJob, &WebAudioDecodeJob::OnFailure, aErrorCode);
NS_DispatchToMainThread(event);
}
}
void Decode();
+ void RequestSample();
+ void SampleDecoded(AudioData* aData);
+ void SampleNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
+ void FinishDecode();
void AllocateBuffer();
void CallbackTheResult();
void Cleanup()
{
MOZ_ASSERT(NS_IsMainThread());
// MediaDecoderReader expects that BufferDecoder is alive.
// Destruct MediaDecoderReader first.
@@ -149,20 +150,21 @@ private:
}
private:
nsCString mContentType;
uint8_t* mBuffer;
uint32_t mLength;
WebAudioDecodeJob& mDecodeJob;
PhaseEnum mPhase;
- nsCOMPtr<nsIThreadPool> mThreadPool;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsRefPtr<BufferDecoder> mBufferDecoder;
nsRefPtr<MediaDecoderReader> mDecoderReader;
+ MediaInfo mMediaInfo;
+ MediaQueue<AudioData> mAudioQueue;
};
NS_IMETHODIMP
MediaDecodeTask::Run()
{
MOZ_ASSERT(mBufferDecoder);
MOZ_ASSERT(mDecoderReader);
switch (mPhase) {
@@ -200,16 +202,20 @@ MediaDecodeTask::CreateReader()
return false;
}
nsresult rv = mDecoderReader->Init(nullptr);
if (NS_FAILED(rv)) {
return false;
}
+ if (!mDecoderReader->EnsureTaskQueue()) {
+ return false;
+ }
+
return true;
}
class AutoResampler {
public:
AutoResampler()
: mResampler(nullptr)
{}
@@ -233,60 +239,77 @@ private:
SpeexResamplerState* mResampler;
};
void
MediaDecodeTask::Decode()
{
MOZ_ASSERT(!NS_IsMainThread());
- mBufferDecoder->BeginDecoding(NS_GetCurrentThread());
+ mBufferDecoder->BeginDecoding(mDecoderReader->GetTaskQueue());
// Tell the decoder reader that we are not going to play the data directly,
// and that we should not reject files with more channels than the audio
// bakend support.
mDecoderReader->SetIgnoreAudioOutputFormat();
- MediaInfo mediaInfo;
nsAutoPtr<MetadataTags> tags;
- nsresult rv = mDecoderReader->ReadMetadata(&mediaInfo, getter_Transfers(tags));
+ nsresult rv = mDecoderReader->ReadMetadata(&mMediaInfo, getter_Transfers(tags));
if (NS_FAILED(rv)) {
mDecoderReader->Shutdown();
ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
return;
}
if (!mDecoderReader->HasAudio()) {
mDecoderReader->Shutdown();
ReportFailureOnMainThread(WebAudioDecodeJob::NoAudio);
return;
}
- MediaQueue<AudioData> audioQueue;
- nsRefPtr<AudioDecodeRendezvous> barrier(new AudioDecodeRendezvous());
- mDecoderReader->SetCallback(barrier);
- while (1) {
- mDecoderReader->RequestAudioData();
- nsRefPtr<AudioData> audio;
- if (NS_FAILED(barrier->Await(audio))) {
- mDecoderReader->Shutdown();
- ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
- return;
- }
- if (!audio) {
- // End of stream.
- break;
- }
- audioQueue.Push(audio);
+ RequestSample();
+}
+
+void
+MediaDecodeTask::RequestSample()
+{
+ mDecoderReader->RequestAudioData()->Then(mDecoderReader->GetTaskQueue(), __func__, this,
+ &MediaDecodeTask::SampleDecoded,
+ &MediaDecodeTask::SampleNotDecoded);
+}
+
+void
+MediaDecodeTask::SampleDecoded(AudioData* aData)
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+ mAudioQueue.Push(aData);
+ RequestSample();
+}
+
+void
+MediaDecodeTask::SampleNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+ if (aReason == MediaDecoderReader::DECODE_ERROR) {
+ mDecoderReader->Shutdown();
+ ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
+ } else {
+ MOZ_ASSERT(aReason == MediaDecoderReader::END_OF_STREAM);
+ FinishDecode();
}
+}
+
+void
+MediaDecodeTask::FinishDecode()
+{
mDecoderReader->Shutdown();
- uint32_t frameCount = audioQueue.FrameCount();
- uint32_t channelCount = mediaInfo.mAudio.mChannels;
- uint32_t sampleRate = mediaInfo.mAudio.mRate;
+ uint32_t frameCount = mAudioQueue.FrameCount();
+ uint32_t channelCount = mMediaInfo.mAudio.mChannels;
+ uint32_t sampleRate = mMediaInfo.mAudio.mRate;
if (!frameCount || !channelCount || !sampleRate) {
ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
return;
}
const uint32_t destSampleRate = mDecodeJob.mContext->SampleRate();
AutoResampler resampler;
@@ -324,17 +347,17 @@ MediaDecodeTask::Decode()
}
}
if (!memoryAllocationSuccess) {
ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError);
return;
}
nsRefPtr<AudioData> audioData;
- while ((audioData = audioQueue.PopFront())) {
+ while ((audioData = mAudioQueue.PopFront())) {
audioData->EnsureAudioBuffer(); // could lead to a copy :(
AudioDataValue* bufferData = static_cast<AudioDataValue*>
(audioData->mAudioBuffer->Data());
if (sampleRate != destSampleRate) {
const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex;
for (uint32_t i = 0; i < audioData->mChannels; ++i) {
@@ -437,85 +460,42 @@ WebAudioDecodeJob::AllocateBuffer()
for (uint32_t i = 0; i < mChannelBuffers.Length(); ++i) {
mOutput->SetRawChannelContents(i, mChannelBuffers[i]);
}
return true;
}
void
-MediaBufferDecoder::AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
- uint32_t aLength,
- WebAudioDecodeJob& aDecodeJob)
+AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer,
+ uint32_t aLength, WebAudioDecodeJob& aDecodeJob)
{
// Do not attempt to decode the media if we were not successful at sniffing
// the content type.
if (!*aContentType ||
strcmp(aContentType, APPLICATION_OCTET_STREAM) == 0) {
nsCOMPtr<nsIRunnable> event =
new ReportResultTask(aDecodeJob,
&WebAudioDecodeJob::OnFailure,
WebAudioDecodeJob::UnknownContent);
JS_free(nullptr, aBuffer);
NS_DispatchToMainThread(event);
return;
}
- if (!EnsureThreadPoolInitialized()) {
- nsCOMPtr<nsIRunnable> event =
- new ReportResultTask(aDecodeJob,
- &WebAudioDecodeJob::OnFailure,
- WebAudioDecodeJob::UnknownError);
- JS_free(nullptr, aBuffer);
- NS_DispatchToMainThread(event);
- return;
- }
-
- MOZ_ASSERT(mThreadPool);
-
- nsRefPtr<MediaDecodeTask> task =
- new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob, mThreadPool);
+ RefPtr<MediaDecodeTask> task =
+ new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob);
if (!task->CreateReader()) {
nsCOMPtr<nsIRunnable> event =
new ReportResultTask(aDecodeJob,
&WebAudioDecodeJob::OnFailure,
WebAudioDecodeJob::UnknownError);
NS_DispatchToMainThread(event);
} else {
- mThreadPool->Dispatch(task, nsIThreadPool::DISPATCH_NORMAL);
- }
-}
-
-bool
-MediaBufferDecoder::EnsureThreadPoolInitialized()
-{
- if (!mThreadPool) {
- mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
- if (!mThreadPool) {
- return false;
- }
- mThreadPool->SetName(NS_LITERAL_CSTRING("MediaBufferDecoder"));
-#ifdef XP_WIN
- // Ensure MSCOM is initialized on the thread pools threads.
- nsCOMPtr<nsIThreadPoolListener> listener = new MSCOMInitThreadPoolListener();
- nsresult rv = mThreadPool->SetListener(listener);
- NS_ENSURE_SUCCESS(rv, nullptr);
-#endif
- }
- return true;
-}
-
-void
-MediaBufferDecoder::Shutdown() {
- if (mThreadPool) {
- // Setting threadLimit to 0 causes threads to exit when all events have
- // been run, like nsIThreadPool::Shutdown(), but doesn't run a nested event
- // loop nor wait until this has happened.
- mThreadPool->SetThreadLimit(0);
- mThreadPool = nullptr;
+ task->Reader()->GetTaskQueue()->Dispatch(task);
}
}
WebAudioDecodeJob::WebAudioDecodeJob(const nsACString& aContentType,
AudioContext* aContext,
Promise* aPromise,
DecodeSuccessCallback* aSuccessCallback,
DecodeErrorCallback* aFailureCallback)
--- a/dom/media/webaudio/MediaBufferDecoder.h
+++ b/dom/media/webaudio/MediaBufferDecoder.h
@@ -4,17 +4,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MediaBufferDecoder_h_
#define MediaBufferDecoder_h_
#include "nsWrapperCache.h"
#include "nsCOMPtr.h"
-#include "nsIThreadPool.h"
#include "nsString.h"
#include "nsTArray.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/MemoryReporting.h"
namespace mozilla {
namespace dom {
@@ -65,39 +64,15 @@ struct WebAudioDecodeJob MOZ_FINAL
nsRefPtr<dom::DecodeErrorCallback> mFailureCallback; // can be null
nsRefPtr<dom::AudioBuffer> mOutput;
FallibleTArray<ChannelBuffer> mChannelBuffers;
private:
~WebAudioDecodeJob();
};
-/**
- * This class is used to decode media buffers on a dedicated threadpool.
- *
- * This class manages the resources that it uses internally (such as the
- * thread-pool) and provides a clean external interface.
- */
-class MediaBufferDecoder
-{
-public:
- void AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
- uint32_t aLength, WebAudioDecodeJob& aDecodeJob);
-
- ~MediaBufferDecoder() { Shutdown(); }
- void Shutdown();
-
- size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
- {
- return 0;
- }
-
-private:
- bool EnsureThreadPoolInitialized();
-
-private:
- nsCOMPtr<nsIThreadPool> mThreadPool;
-};
+void AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer,
+ uint32_t aLength, WebAudioDecodeJob& aDecodeJob);
}
#endif
--- a/dom/media/webm/SoftwareWebMVideoDecoder.h
+++ b/dom/media/webm/SoftwareWebMVideoDecoder.h
@@ -19,17 +19,17 @@ public:
virtual nsresult Init(unsigned int aWidth, unsigned int aHeight) MOZ_OVERRIDE;
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold) MOZ_OVERRIDE;
virtual void Shutdown() MOZ_OVERRIDE;
- SoftwareWebMVideoDecoder(WebMReader* aReader);
+ explicit SoftwareWebMVideoDecoder(WebMReader* aReader);
~SoftwareWebMVideoDecoder();
private:
nsRefPtr<WebMReader> mReader;
// VPx decoder state
vpx_codec_ctx_t mVPX;
};
--- a/dom/media/webm/WebMReader.cpp
+++ b/dom/media/webm/WebMReader.cpp
@@ -739,17 +739,17 @@ bool WebMReader::DecodeOpus(const unsign
if (channels > 8) {
return false;
}
if (mPaddingDiscarded) {
// Discard padding should be used only on the final packet, so
// decoding after a padding discard is invalid.
LOG(PR_LOG_DEBUG, ("Opus error, discard padding on interstitial packet"));
- GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, DECODE_ERROR);
+ mHitAudioDecodeError = true;
return false;
}
// Maximum value is 63*2880, so there's no chance of overflow.
int32_t frames_number = opus_packet_get_nb_frames(aData, aLength);
if (frames_number <= 0) {
return false; // Invalid packet header.
}
@@ -793,30 +793,29 @@ bool WebMReader::DecodeOpus(const unsign
mSkip -= skipFrames;
}
int64_t discardPadding = 0;
(void) nestegg_packet_discard_padding(aPacket, &discardPadding);
if (discardPadding < 0) {
// Negative discard padding is invalid.
LOG(PR_LOG_DEBUG, ("Opus error, negative discard padding"));
- GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, DECODE_ERROR);
- return false;
+ mHitAudioDecodeError = true;
}
if (discardPadding > 0) {
CheckedInt64 discardFrames = UsecsToFrames(discardPadding / NS_PER_USEC,
mInfo.mAudio.mRate);
if (!discardFrames.isValid()) {
NS_WARNING("Int overflow in DiscardPadding");
return false;
}
if (discardFrames.value() > frames) {
// Discarding more than the entire packet is invalid.
LOG(PR_LOG_DEBUG, ("Opus error, discard padding larger than packet"));
- GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, DECODE_ERROR);
+ mHitAudioDecodeError = true;
return false;
}
LOG(PR_LOG_DEBUG, ("Opus decoder discarding %d of %d frames",
int32_t(discardFrames.value()), frames));
// Padding discard is only supposed to happen on the final packet.
// Record the discard so we can return an error if another packet is
// decoded.
mPaddingDiscarded = true;
--- a/dom/plugins/base/moz.build
+++ b/dom/plugins/base/moz.build
@@ -93,20 +93,22 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'andr
# using #defines, which results in Wswitch warnings in gcc-4.6.
# Therefore, enable FAIL_ON_WARNINGS only on non-Android platforms.
FAIL_ON_WARNINGS = True
MSVC_ENABLE_PGO = True
LOCAL_INCLUDES += [
'/dom/base',
+ '/dom/plugins/ipc',
'/layout/generic',
'/layout/xul',
'/widget',
'/widget/android',
+ '/widget/cocoa',
'/xpcom/base',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
LOCAL_INCLUDES += [
'/dom/plugins/base/android',
]
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -149,18 +149,17 @@ NPObjWrapper_DelProperty(JSContext *cx,
static bool
NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool strict,
JS::MutableHandle<JS::Value> vp);
static bool
NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
static bool
-NPObjWrapper_newEnumerate(JSContext *cx, JS::Handle<JSObject*> obj, JSIterateOp enum_op,
- JS::Value *statep, jsid *idp);
+NPObjWrapper_Enumerate(JSContext *cx, JS::Handle<JSObject*> obj, JS::AutoIdVector &properties);
static bool
NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
bool *resolvedp);
static bool
NPObjWrapper_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp);
@@ -179,36 +178,57 @@ NPObjWrapper_Construct(JSContext *cx, un
static bool
CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
JS::Handle<jsid> id, NPVariant* getPropertyResult,
JS::MutableHandle<JS::Value> vp);
const static js::Class sNPObjectJSWrapperClass =
{
NPRUNTIME_JSCLASS_NAME,
- JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_ENUMERATE,
+ JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
NPObjWrapper_AddProperty,
NPObjWrapper_DelProperty,
NPObjWrapper_GetProperty,
NPObjWrapper_SetProperty,
- (JSEnumerateOp)NPObjWrapper_newEnumerate,
+ nullptr,
NPObjWrapper_Resolve,
NPObjWrapper_Convert,
NPObjWrapper_Finalize,
NPObjWrapper_Call,
nullptr, /* hasInstance */
NPObjWrapper_Construct,
nullptr, /* trace */
JS_NULL_CLASS_SPEC,
{
nullptr, /* outerObject */
nullptr, /* innerObject */
false, /* isWrappedNative */
nullptr, /* weakmapKeyDelegateOp */
NPObjWrapper_ObjectMoved
+ },
+ {
+ nullptr, // lookupGeneric
+ nullptr, // lookupProperty
+ nullptr, // lookupElement
+ nullptr, // defineGeneric
+ nullptr, // defineProperty
+ nullptr, // defineElement
+ nullptr, // getGeneric
+ nullptr, // getProperty
+ nullptr, // getElement
+ nullptr, // setGeneric
+ nullptr, // setProperty
+ nullptr, // setElement
+ nullptr, // getGenericAttributes
+ nullptr, // setGenericAttributes
+ nullptr, // deleteGeneric
+ nullptr, nullptr, // watch/unwatch
+ nullptr, // getElements
+ NPObjWrapper_Enumerate,
+ nullptr,
}
};
typedef struct NPObjectMemberPrivate {
JS::Heap<JSObject *> npobjWrapper;
JS::Heap<JS::Value> fieldValue;
JS::Heap<jsid> methodName;
NPP npp;
@@ -224,19 +244,18 @@ static bool
NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp);
static void
NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
static const JSClass sNPObjectMemberClass =
{
"NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
- nullptr, nullptr,
- JS_PropertyStub, JS_StrictPropertyStub, nullptr,
- nullptr, NPObjectMember_Convert,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, NPObjectMember_Convert,
NPObjectMember_Finalize, NPObjectMember_Call,
nullptr, nullptr, NPObjectMember_Trace
};
static void
OnWrapperDestroyed();
static void
@@ -1560,99 +1579,55 @@ CallNPMethod(JSContext *cx, unsigned arg
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
if (!obj)
return false;
return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false);
}
-struct NPObjectEnumerateState {
- uint32_t index;
- uint32_t length;
- NPIdentifier *value;
-};
-
static bool
-NPObjWrapper_newEnumerate(JSContext *cx, JS::Handle<JSObject*> obj, JSIterateOp enum_op,
- JS::Value *statep, jsid *idp)
+NPObjWrapper_Enumerate(JSContext *cx, JS::Handle<JSObject*> obj,
+ JS::AutoIdVector &properties)
{
NPObject *npobj = GetNPObject(cx, obj);
- NPIdentifier *enum_value;
- uint32_t length;
- NPObjectEnumerateState *state;
-
if (!npobj || !npobj->_class) {
ThrowJSException(cx, "Bad NPObject as private data!");
return false;
}
PluginDestructionGuard pdg(LookupNPP(npobj));
- NS_ASSERTION(statep, "Must have a statep to enumerate!");
-
- switch(enum_op) {
- case JSENUMERATE_INIT:
- case JSENUMERATE_INIT_ALL:
- state = new NPObjectEnumerateState();
- if (!state) {
- ThrowJSException(cx, "Memory allocation failed for "
- "NPObjectEnumerateState!");
-
- return false;
- }
-
- if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) ||
- !npobj->_class->enumerate) {
- enum_value = 0;
- length = 0;
- } else if (!npobj->_class->enumerate(npobj, &enum_value, &length)) {
- delete state;
-
- if (ReportExceptionIfPending(cx)) {
- // ReportExceptionIfPending returns a return value, which is true
- // if no exception was thrown. In that case, throw our own.
- ThrowJSException(cx, "Error enumerating properties on scriptable "
- "plugin object");
- }
-
- return false;
+ if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) ||
+ !npobj->_class->enumerate) {
+ return true;
+ }
+
+ NPIdentifier *identifiers;
+ uint32_t length;
+ if (!npobj->_class->enumerate(npobj, &identifiers, &length)) {
+ if (ReportExceptionIfPending(cx)) {
+ // ReportExceptionIfPending returns a return value, which is true
+ // if no exception was thrown. In that case, throw our own.
+ ThrowJSException(cx, "Error enumerating properties on scriptable "
+ "plugin object");
}
-
- state->value = enum_value;
- state->length = length;
- state->index = 0;
- *statep = PRIVATE_TO_JSVAL(state);
- if (idp) {
- *idp = INT_TO_JSID(length);
- }
-
- break;
-
- case JSENUMERATE_NEXT:
- state = (NPObjectEnumerateState *)statep->toPrivate();
- enum_value = state->value;
- length = state->length;
- if (state->index != length) {
- *idp = NPIdentifierToJSId(enum_value[state->index++]);
- return true;
- }
-
- // FALL THROUGH
-
- case JSENUMERATE_DESTROY:
- state = (NPObjectEnumerateState *)statep->toPrivate();
- if (state->value)
- PR_Free(state->value);
- delete state;
- *statep = JSVAL_NULL;
-
- break;
+ return false;
}
+ if (!properties.reserve(length))
+ return false;
+
+ JS::Rooted<jsid> id(cx);
+ for (uint32_t i = 0; i < length; i++) {
+ id = NPIdentifierToJSId(identifiers[i]);
+ properties.infallibleAppend(id);
+ }
+
+ PR_Free(identifiers);
return true;
}
static bool
NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
bool *resolvedp)
{
if (JSID_IS_SYMBOL(id))
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -47,16 +47,17 @@
#include "nsIObserverService.h"
#include <prinrval.h>
#ifdef MOZ_WIDGET_COCOA
#include <Carbon/Carbon.h>
#include <ApplicationServices/ApplicationServices.h>
#include <OpenGL/OpenGL.h>
#include "nsCocoaFeatures.h"
+#include "PluginUtilsOSX.h"
#endif
// needed for nppdf plugin
#if (MOZ_WIDGET_GTK)
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#if (MOZ_WIDGET_GTK == 2)
#include "gtk2xtbin.h"
@@ -2116,23 +2117,23 @@ NPError
case NPNVsupportsCoreGraphicsBool: {
*(NPBool*)result = true;
return NPERR_NO_ERROR;
}
case NPNVsupportsCoreAnimationBool: {
- *(NPBool*)result = nsCocoaFeatures::SupportCoreAnimationPlugins();
+ *(NPBool*)result = true;
return NPERR_NO_ERROR;
}
case NPNVsupportsInvalidatingCoreAnimationBool: {
- *(NPBool*)result = nsCocoaFeatures::SupportCoreAnimationPlugins();
+ *(NPBool*)result = true;
return NPERR_NO_ERROR;
}
case NPNVsupportsCompositingCoreAnimationPluginsBool: {
*(NPBool*)result = PR_TRUE;
return NPERR_NO_ERROR;
@@ -2779,21 +2780,60 @@ void
return;
inst->UnscheduleTimer(timerID);
}
NPError
_popupcontextmenu(NPP instance, NPMenu* menu)
{
+#ifdef MOZ_WIDGET_COCOA
nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
- if (!inst)
+
+ double pluginX, pluginY;
+ double screenX, screenY;
+
+ const NPCocoaEvent* currentEvent = static_cast<NPCocoaEvent*>(inst->GetCurrentEvent());
+ if (!currentEvent) {
return NPERR_GENERIC_ERROR;
-
- return inst->PopUpContextMenu(menu);
+ }
+
+ // Ensure that the events has an x/y value.
+ if (currentEvent->type != NPCocoaEventMouseDown &&
+ currentEvent->type != NPCocoaEventMouseUp &&
+ currentEvent->type != NPCocoaEventMouseMoved &&
+ currentEvent->type != NPCocoaEventMouseEntered &&
+ currentEvent->type != NPCocoaEventMouseExited &&
+ currentEvent->type != NPCocoaEventMouseDragged) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ pluginX = currentEvent->data.mouse.pluginX;
+ pluginY = currentEvent->data.mouse.pluginY;
+
+ if ((pluginX < 0.0) || (pluginY < 0.0))
+ return NPERR_GENERIC_ERROR;
+
+ NPBool success = _convertpoint(instance,
+ pluginX, pluginY, NPCoordinateSpacePlugin,
+ &screenX, &screenY, NPCoordinateSpaceScreen);
+
+ if (success) {
+ return mozilla::plugins::PluginUtilsOSX::ShowCocoaContextMenu(menu,
+ screenX, screenY,
+ nullptr,
+ nullptr);
+ } else {
+ NS_WARNING("Convertpoint failed, could not created contextmenu.");
+ return NPERR_GENERIC_ERROR;
+ }
+#else
+ NS_WARNING("Not supported on this platform!");
+ return NPERR_GENERIC_ERROR;
+#endif
}
NPBool
_convertpoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace)
{
nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
if (!inst)
return false;
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -168,35 +168,36 @@ using namespace mozilla;
using namespace mozilla::plugins::parent;
using namespace mozilla::layers;
static NS_DEFINE_IID(kIOutputStreamIID, NS_IOUTPUTSTREAM_IID);
NS_IMPL_ISUPPORTS0(nsNPAPIPluginInstance)
nsNPAPIPluginInstance::nsNPAPIPluginInstance()
- :
- mDrawingModel(kDefaultDrawingModel),
+ : mDrawingModel(kDefaultDrawingModel)
#ifdef MOZ_WIDGET_ANDROID
- mANPDrawingModel(0),
- mFullScreenOrientation(dom::eScreenOrientation_LandscapePrimary),
- mWakeLocked(false),
- mFullScreen(false),
- mInverted(false),
+ , mANPDrawingModel(0)
+ , mFullScreenOrientation(dom::eScreenOrientation_LandscapePrimary)
+ , mWakeLocked(false)
+ , mFullScreen(false)
+ , mInverted(false)
#endif
- mRunning(NOT_STARTED),
- mWindowless(false),
- mTransparent(false),
- mCached(false),
- mUsesDOMForCursor(false),
- mInPluginInitCall(false),
- mPlugin(nullptr),
- mMIMEType(nullptr),
- mOwner(nullptr),
- mCurrentPluginEvent(nullptr)
+ , mRunning(NOT_STARTED)
+ , mWindowless(false)
+ , mTransparent(false)
+ , mCached(false)
+ , mUsesDOMForCursor(false)
+ , mInPluginInitCall(false)
+ , mPlugin(nullptr)
+ , mMIMEType(nullptr)
+ , mOwner(nullptr)
+#ifdef XP_MACOSX
+ , mCurrentPluginEvent(nullptr)
+#endif
#ifdef MOZ_WIDGET_ANDROID
, mOnScreen(true)
#endif
, mHaveJavaC2PJSObjectQuirk(false)
, mCachedParamLength(0)
, mCachedParamNames(nullptr)
, mCachedParamValues(nullptr)
{
@@ -666,31 +667,35 @@ nsresult nsNPAPIPluginInstance::HandleEv
if (!mPlugin || !mPlugin->GetLibrary())
return NS_ERROR_FAILURE;
NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
int16_t tmpResult = kNPEventNotHandled;
if (pluginFunctions->event) {
+#ifdef XP_MACOSX
mCurrentPluginEvent = event;
+#endif
#if defined(XP_WIN)
NS_TRY_SAFE_CALL_RETURN(tmpResult, (*pluginFunctions->event)(&mNPP, event), this,
aSafeToReenterGecko);
#else
MAIN_THREAD_JNI_REF_GUARD;
tmpResult = (*pluginFunctions->event)(&mNPP, event);
#endif
NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
("NPP HandleEvent called: this=%p, npp=%p, event=%p, return=%d\n",
this, &mNPP, event, tmpResult));
if (result)
*result = tmpResult;
+#ifdef XP_MACOSX
mCurrentPluginEvent = nullptr;
+#endif
}
return NS_OK;
}
nsresult nsNPAPIPluginInstance::GetValueFromPlugin(NPPVariable variable, void* value)
{
if (!mPlugin || !mPlugin->GetLibrary())
@@ -1147,17 +1152,18 @@ bool
nsNPAPIPluginInstance::ShouldCache()
{
return mCached;
}
nsresult
nsNPAPIPluginInstance::IsWindowless(bool* isWindowless)
{
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) || defined(XP_MACOSX)
+ // All OS X plugins are windowless.
// On android, pre-honeycomb, all plugins are treated as windowless.
*isWindowless = true;
#else
*isWindowless = mWindowless;
#endif
return NS_OK;
}
@@ -1506,33 +1512,23 @@ nsNPAPIPluginInstance::UnscheduleTimer(u
// remove timer struct from array
mTimers.RemoveElementAt(index);
// delete timer
delete t;
}
-// Show the context menu at the location for the current event.
-// This can only be called from within an NPP_SendEvent call.
-NPError
-nsNPAPIPluginInstance::PopUpContextMenu(NPMenu* menu)
-{
- if (mOwner && mCurrentPluginEvent)
- return mOwner->ShowNativeContextMenu(menu, mCurrentPluginEvent);
-
- return NPERR_GENERIC_ERROR;
-}
-
NPBool
nsNPAPIPluginInstance::ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
double *destX, double *destY, NPCoordinateSpace destSpace)
{
- if (mOwner)
+ if (mOwner) {
return mOwner->ConvertPoint(sourceX, sourceY, sourceSpace, destX, destY, destSpace);
+ }
return false;
}
nsresult
nsNPAPIPluginInstance::GetDOMElement(nsIDOMElement* *result)
{
if (!mOwner) {
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -129,16 +129,20 @@ public:
NPError SetUsesDOMForCursor(bool aUsesDOMForCursor);
bool UsesDOMForCursor();
void SetDrawingModel(NPDrawingModel aModel);
void RedrawPlugin();
#ifdef XP_MACOSX
void SetEventModel(NPEventModel aModel);
+
+ void* GetCurrentEvent() {
+ return mCurrentPluginEvent;
+ }
#endif
#ifdef MOZ_WIDGET_ANDROID
void NotifyForeground(bool aForeground);
void NotifyOnScreen(bool aOnScreen);
void MemoryPressure();
void NotifyFullScreen(bool aFullScreen);
void NotifySize(nsIntSize size);
@@ -256,17 +260,16 @@ public:
nsresult IsPrivateBrowsing(bool *aEnabled);
nsresult GetDOMElement(nsIDOMElement* *result);
nsNPAPITimer* TimerWithID(uint32_t id, uint32_t* index);
uint32_t ScheduleTimer(uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID));
void UnscheduleTimer(uint32_t timerID);
- NPError PopUpContextMenu(NPMenu* menu);
NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace);
nsTArray<nsNPAPIPluginStreamListener*> *StreamListeners();
nsTArray<nsPluginStreamListenerPeer*> *FileCachedStreamListeners();
nsresult AsyncSetWindow(NPWindow& window);
@@ -363,18 +366,20 @@ private:
char* mMIMEType;
// Weak pointer to the owner. The owner nulls this out (by calling
// InvalidateOwner()) when it's no longer our owner.
nsPluginInstanceOwner *mOwner;
nsTArray<nsNPAPITimer*> mTimers;
+#ifdef XP_MACOSX
// non-null during a HandleEvent call
void* mCurrentPluginEvent;
+#endif
// Timestamp for the last time this plugin was stopped.
// This is only valid when the plugin is actually stopped!
mozilla::TimeStamp mStopTime;
#ifdef MOZ_WIDGET_ANDROID
void EnsureSharedTexture();
mozilla::TemporaryRef<mozilla::gl::AndroidSurfaceTexture> CreateSurfaceTexture();
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -52,30 +52,33 @@ using mozilla::DefaultXDisplay;
#include "EGLUtils.h"
#include "nsIContentInlines.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/TextEvents.h"
#include "mozilla/dom/HTMLObjectElementBinding.h"
#include "mozilla/dom/TabChild.h"
#include "nsFrameSelection.h"
+#include "PuppetWidget.h"
+#include "nsPIWindowRoot.h"
#include "nsContentCID.h"
#include "nsWidgetsCID.h"
static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID);
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
#ifdef XP_WIN
#include <wtypes.h>
#include <winuser.h>
#endif
#ifdef XP_MACOSX
-#include <Carbon/Carbon.h>
-#include "nsPluginUtilsOSX.h"
+#include "ComplexTextInputPanel.h"
+#include "nsIDOMXULDocument.h"
+#include "nsIDOMXULCommandDispatcher.h"
#endif
#ifdef MOZ_WIDGET_GTK
#include <gdk/gdk.h>
#include <gtk/gtk.h>
#endif
#ifdef MOZ_WIDGET_ANDROID
@@ -90,16 +93,20 @@ using namespace mozilla::dom;
#include <android/log.h>
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
#endif
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::layers;
+static inline nsPoint AsNsPoint(const nsIntPoint &p) {
+ return nsPoint(p.x, p.y);
+}
+
// special class for handeling DOM context menu events because for
// some reason it starves other mouse events if implemented on the
// same class
class nsPluginDOMContextMenuListener : public nsIDOMEventListener
{
virtual ~nsPluginDOMContextMenuListener();
public:
@@ -325,18 +332,21 @@ nsPluginInstanceOwner::nsPluginInstanceO
mPluginFrame = nullptr;
mContent = nullptr;
mWidgetCreationComplete = false;
#ifdef XP_MACOSX
memset(&mCGPluginPortCopy, 0, sizeof(NP_CGContext));
mInCGPaintLevel = 0;
mSentInitialTopLevelWindowEvent = false;
+ mLastWindowIsActive = false;
+ mLastContentFocused = false;
+ mLastScaleFactor = 1.0;
mColorProfile = nullptr;
- mPluginPortChanged = false;
+ mShouldBlurOnActivate = false;
#endif
mContentFocused = false;
mWidgetVisible = true;
mPluginWindowVisible = false;
mPluginDocumentActiveState = true;
mLastMouseDownButtonType = -1;
#ifdef XP_MACOSX
@@ -724,43 +734,262 @@ NS_IMETHODIMP nsPluginInstanceOwner::Set
#ifdef XP_MACOSX
mEventModel = static_cast<NPEventModel>(eventModel);
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
+// This is no longer used, just leaving it here so we don't have to change
+// the nsIPluginInstanceOwner interface.
NPError nsPluginInstanceOwner::ShowNativeContextMenu(NPMenu* menu, void* event)
{
- if (!menu || !event)
- return NPERR_GENERIC_ERROR;
+ return NPERR_GENERIC_ERROR;
+}
#ifdef XP_MACOSX
- if (GetEventModel() != NPEventModelCocoa)
- return NPERR_INCOMPATIBLE_VERSION_ERROR;
-
- return NS_NPAPI_ShowCocoaContextMenu(static_cast<void*>(menu), mWidget,
- static_cast<NPCocoaEvent*>(event));
-#else
- return NPERR_INCOMPATIBLE_VERSION_ERROR;
-#endif
+NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget *widget,
+ nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double *destX, double *destY,
+ NPCoordinateSpace destSpace)
+{
+ NS_ENSURE_TRUE(widget && widget->GetOwningTabChild() && pluginFrame, false);
+ // Caller has to want a result.
+ NS_ENSURE_TRUE(destX || destY, false);
+
+ if (sourceSpace == destSpace) {
+ if (destX) {
+ *destX = sourceX;
+ }
+ if (destY) {
+ *destY = sourceY;
+ }
+ return true;
+ }
+
+ nsPresContext* presContext = pluginFrame->PresContext();
+ double scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
+ presContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+
+ PuppetWidget *puppetWidget = static_cast<PuppetWidget*>(widget);
+ PuppetWidget *rootWidget = static_cast<PuppetWidget*>(widget->GetTopLevelWidget());
+ if (!rootWidget) {
+ return false;
+ }
+ nsPoint chromeSize = AsNsPoint(rootWidget->GetChromeDimensions()) / scaleFactor;
+ nsIntSize intScreenDims = rootWidget->GetScreenDimensions();
+ nsSize screenDims = nsSize(intScreenDims.width / scaleFactor,
+ intScreenDims.height / scaleFactor);
+ int32_t screenH = screenDims.height;
+ nsPoint windowPosition = AsNsPoint(rootWidget->GetWindowPosition()) / scaleFactor;
+
+ // Window size is tab size + chrome size.
+ nsIntRect tabContentBounds;
+ NS_ENSURE_SUCCESS(puppetWidget->GetBounds(tabContentBounds), false);
+ tabContentBounds.ScaleInverseRoundOut(scaleFactor);
+ int32_t windowH = tabContentBounds.height + int(chromeSize.y);
+
+ // This is actually relative to window-chrome.
+ nsPoint pluginPosition = AsNsPoint(pluginFrame->GetScreenRect().TopLeft());
+
+ // Convert (sourceX, sourceY) to 'real' (not PuppetWidget) screen space.
+ // In OSX, the Y-axis increases upward, which is the reverse of ours.
+ // We want OSX coordinates for window and screen so those equations are swapped.
+ nsPoint sourcePoint(sourceX, sourceY);
+ nsPoint screenPoint;
+ switch (sourceSpace) {
+ case NPCoordinateSpacePlugin:
+ screenPoint = sourcePoint + pluginFrame->GetContentRectRelativeToSelf().TopLeft() +
+ chromeSize + pluginPosition + windowPosition;
+ break;
+ case NPCoordinateSpaceWindow:
+ screenPoint = nsPoint(sourcePoint.x, windowH-sourcePoint.y) +
+ windowPosition;
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ screenPoint = sourcePoint + windowPosition;
+ break;
+ case NPCoordinateSpaceScreen:
+ screenPoint = nsPoint(sourcePoint.x, screenH-sourcePoint.y);
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ screenPoint = sourcePoint;
+ break;
+ default:
+ return false;
+ }
+
+ // Convert from screen to dest space.
+ nsPoint destPoint;
+ switch (destSpace) {
+ case NPCoordinateSpacePlugin:
+ destPoint = screenPoint - pluginFrame->GetContentRectRelativeToSelf().TopLeft() -
+ chromeSize - pluginPosition - windowPosition;
+ break;
+ case NPCoordinateSpaceWindow:
+ destPoint = screenPoint - windowPosition;
+ destPoint.y = windowH - destPoint.y;
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ destPoint = screenPoint - windowPosition;
+ break;
+ case NPCoordinateSpaceScreen:
+ destPoint = nsPoint(screenPoint.x, screenH-screenPoint.y);
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ destPoint = screenPoint;
+ break;
+ default:
+ return false;
+ }
+
+ if (destX) {
+ *destX = destPoint.x;
+ }
+ if (destY) {
+ *destY = destPoint.y;
+ }
+
+ return true;
}
+NPBool nsPluginInstanceOwner::ConvertPointNoPuppet(nsIWidget *widget,
+ nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double *destX, double *destY,
+ NPCoordinateSpace destSpace)
+{
+ NS_ENSURE_TRUE(widget && pluginFrame, false);
+ // Caller has to want a result.
+ NS_ENSURE_TRUE(destX || destY, false);
+
+ if (sourceSpace == destSpace) {
+ if (destX) {
+ *destX = sourceX;
+ }
+ if (destY) {
+ *destY = sourceY;
+ }
+ return true;
+ }
+
+ nsPresContext* presContext = pluginFrame->PresContext();
+ double scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
+ presContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+
+ nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
+ if (!screenMgr) {
+ return false;
+ }
+ nsCOMPtr<nsIScreen> screen;
+ screenMgr->ScreenForNativeWidget(widget->GetNativeData(NS_NATIVE_WINDOW), getter_AddRefs(screen));
+ if (!screen) {
+ return false;
+ }
+
+ int32_t screenX, screenY, screenWidth, screenHeight;
+ screen->GetRect(&screenX, &screenY, &screenWidth, &screenHeight);
+ screenHeight /= scaleFactor;
+
+ nsIntRect windowScreenBounds;
+ NS_ENSURE_SUCCESS(widget->GetScreenBounds(windowScreenBounds), false);
+ windowScreenBounds.ScaleInverseRoundOut(scaleFactor);
+ int32_t windowX = windowScreenBounds.x;
+ int32_t windowY = windowScreenBounds.y;
+ int32_t windowHeight = windowScreenBounds.height;
+
+ nsIntRect pluginScreenRect = pluginFrame->GetScreenRect();
+
+ double screenXGecko, screenYGecko;
+ switch (sourceSpace) {
+ case NPCoordinateSpacePlugin:
+ screenXGecko = pluginScreenRect.x + sourceX;
+ screenYGecko = pluginScreenRect.y + sourceY;
+ break;
+ case NPCoordinateSpaceWindow:
+ screenXGecko = windowX + sourceX;
+ screenYGecko = windowY + (windowHeight - sourceY);
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ screenXGecko = windowX + sourceX;
+ screenYGecko = windowY + sourceY;
+ break;
+ case NPCoordinateSpaceScreen:
+ screenXGecko = sourceX;
+ screenYGecko = screenHeight - sourceY;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ screenXGecko = sourceX;
+ screenYGecko = sourceY;
+ break;
+ default:
+ return false;
+ }
+
+ double destXCocoa, destYCocoa;
+ switch (destSpace) {
+ case NPCoordinateSpacePlugin:
+ destXCocoa = screenXGecko - pluginScreenRect.x;
+ destYCocoa = screenYGecko - pluginScreenRect.y;
+ break;
+ case NPCoordinateSpaceWindow:
+ destXCocoa = screenXGecko - windowX;
+ destYCocoa = windowHeight - (screenYGecko - windowY);
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ destXCocoa = screenXGecko - windowX;
+ destYCocoa = screenYGecko - windowY;
+ break;
+ case NPCoordinateSpaceScreen:
+ destXCocoa = screenXGecko;
+ destYCocoa = screenHeight - screenYGecko;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ destXCocoa = screenXGecko;
+ destYCocoa = screenYGecko;
+ break;
+ default:
+ return false;
+ }
+
+ if (destX) {
+ *destX = destXCocoa;
+ }
+ if (destY) {
+ *destY = destYCocoa;
+ }
+
+ return true;
+}
+#endif // XP_MACOSX
+
NPBool nsPluginInstanceOwner::ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
double *destX, double *destY, NPCoordinateSpace destSpace)
{
#ifdef XP_MACOSX
- if (!mWidget)
+ if (!mPluginFrame) {
return false;
-
- return NS_NPAPI_ConvertPointCocoa(mWidget->GetNativeData(NS_NATIVE_WIDGET),
- sourceX, sourceY, sourceSpace, destX, destY, destSpace);
+ }
+
+ MOZ_ASSERT(mPluginFrame->GetNearestWidget());
+
+ if (nsIWidget::UsePuppetWidgets()) {
+ return ConvertPointPuppet(static_cast<PuppetWidget*>(mPluginFrame->GetNearestWidget()),
+ mPluginFrame, sourceX, sourceY, sourceSpace,
+ destX, destY, destSpace);
+ }
+
+ return ConvertPointNoPuppet(mPluginFrame->GetNearestWidget(),
+ mPluginFrame, sourceX, sourceY, sourceSpace,
+ destX, destY, destSpace);
#else
- // we should implement this for all platforms
return false;
#endif
}
NS_IMETHODIMP nsPluginInstanceOwner::GetTagType(nsPluginTagType *result)
{
NS_ENSURE_ARG_POINTER(result);
@@ -999,38 +1228,22 @@ void* nsPluginInstanceOwner::GetPluginPo
{
if (GetDrawingModel() == NPDrawingModelCoreGraphics ||
GetDrawingModel() == NPDrawingModelCoreAnimation ||
GetDrawingModel() == NPDrawingModelInvalidatingCoreAnimation)
return &mCGPluginPortCopy;
return nullptr;
}
-// Currently (on OS X in Cocoa widgets) any changes made as a result of
-// calling GetPluginPortFromWidget() are immediately reflected in the NPWindow
-// structure that has been passed to the plugin via SetWindow(). This is
-// because calls to nsChildView::GetNativeData(NS_NATIVE_PLUGIN_PORT_CG)
-// always return a pointer to the same internal (private) object, but may
-// make changes inside that object. All calls to GetPluginPortFromWidget() made while
-// the plugin is active (i.e. excluding those made at our initialization)
-// need to take this into account. The easiest way to do so is to replace
-// them with calls to SetPluginPortAndDetectChange(). This method keeps track
-// of when calls to GetPluginPortFromWidget() result in changes, and sets a flag to make
-// sure SetWindow() gets called the next time through FixUpPluginWindow(), so
-// that the plugin is notified of these changes.
-void* nsPluginInstanceOwner::SetPluginPortAndDetectChange()
+void nsPluginInstanceOwner::SetPluginPort()
{
- if (!mPluginWindow)
- return nullptr;
- void* pluginPort = GetPluginPortFromWidget();
- if (!pluginPort)
- return nullptr;
+ void* pluginPort = GetPluginPort();
+ if (!pluginPort || !mPluginWindow)
+ return;
mPluginWindow->window = pluginPort;
-
- return mPluginWindow->window;
}
void nsPluginInstanceOwner::BeginCGPaint()
{
++mInCGPaintLevel;
}
void nsPluginInstanceOwner::EndCGPaint()
@@ -1266,17 +1479,16 @@ nsresult nsPluginInstanceOwner::Dispatch
if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) {
// continue only for cases without child window
return aFocusEvent->PreventDefault(); // consume event
}
#endif
WidgetEvent* theEvent = aFocusEvent->GetInternalNSEvent();
if (theEvent) {
- // we only care about the message in ProcessEvent
WidgetGUIEvent focusEvent(theEvent->mFlags.mIsTrusted, theEvent->message,
nullptr);
nsEventStatus rv = ProcessEvent(focusEvent);
if (nsEventStatus_eConsumeNoDefault == rv) {
aFocusEvent->PreventDefault();
aFocusEvent->StopPropagation();
}
}
@@ -1390,40 +1602,45 @@ nsresult nsPluginInstanceOwner::Dispatch
nsresult
nsPluginInstanceOwner::HandleEvent(nsIDOMEvent* aEvent)
{
NS_ASSERTION(mInstance, "Should have a valid plugin instance or not receive events.");
nsAutoString eventType;
aEvent->GetType(eventType);
+
+#ifdef XP_MACOSX
+ if (eventType.EqualsLiteral("activate") ||
+ eventType.EqualsLiteral("deactivate")) {
+ WindowFocusMayHaveChanged();
+ return NS_OK;
+ }
+ if (eventType.EqualsLiteral("MozPerformDelayedBlur")) {
+ if (mShouldBlurOnActivate) {
+ WidgetGUIEvent blurEvent(true, NS_BLUR_CONTENT, nullptr);
+ ProcessEvent(blurEvent);
+ mShouldBlurOnActivate = false;
+ }
+ return NS_OK;
+ }
+#endif
+
if (eventType.EqualsLiteral("focus")) {
mContentFocused = true;
return DispatchFocusToPlugin(aEvent);
}
if (eventType.EqualsLiteral("blur")) {
mContentFocused = false;
return DispatchFocusToPlugin(aEvent);
}
if (eventType.EqualsLiteral("mousedown")) {
return ProcessMouseDown(aEvent);
}
if (eventType.EqualsLiteral("mouseup")) {
- // Don't send a mouse-up event to the plugin if its button type doesn't
- // match that of the preceding mouse-down event (if any). This kind of
- // mismatch can happen if the previous mouse-down event was sent to a DOM
- // element above the plugin, the mouse is still above the plugin, and the
- // mouse-down event caused the element to disappear. See bug 627649 and
- // bug 909678.
- WidgetMouseEvent* mouseEvent = aEvent->GetInternalNSEvent()->AsMouseEvent();
- if (mouseEvent &&
- static_cast<int>(mouseEvent->button) != mLastMouseDownButtonType) {
- aEvent->PreventDefault();
- return NS_OK;
- }
return DispatchMouseToPlugin(aEvent);
}
if (eventType.EqualsLiteral("mousemove")) {
return DispatchMouseToPlugin(aEvent, true);
}
if (eventType.EqualsLiteral("click") ||
eventType.EqualsLiteral("dblclick") ||
eventType.EqualsLiteral("mouseover") ||
@@ -1459,123 +1676,296 @@ static unsigned int XInputEventState(con
if (anEvent.IsShift()) state |= ShiftMask;
if (anEvent.IsControl()) state |= ControlMask;
if (anEvent.IsAlt()) state |= Mod1Mask;
if (anEvent.IsMeta()) state |= Mod4Mask;
return state;
}
#endif
+#ifdef XP_MACOSX
+
+// Returns whether or not content is the content that is or would be
+// focused if the top-level chrome window was active.
+static bool
+ContentIsFocusedWithinWindow(nsIContent* aContent)
+{
+ nsPIDOMWindow* outerWindow = aContent->OwnerDoc()->GetWindow();
+ if (!outerWindow) {
+ return false;
+ }
+
+ nsPIDOMWindow* rootWindow = outerWindow->GetPrivateRoot();
+ if (!rootWindow) {
+ return false;
+ }
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return false;
+ }
+
+ nsCOMPtr<nsPIDOMWindow> focusedFrame;
+ nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedDescendant(rootWindow, true, getter_AddRefs(focusedFrame));
+ return (focusedContent.get() == aContent);
+}
+
+static NPCocoaEventType
+CocoaEventTypeForEvent(const WidgetGUIEvent& anEvent, nsIFrame* aObjectFrame)
+{
+ const NPCocoaEvent* event = static_cast<const NPCocoaEvent*>(anEvent.mPluginEvent);
+ if (event) {
+ return event->type;
+ }
+
+ switch (anEvent.message) {
+ case NS_MOUSE_ENTER_SYNTH:
+ return NPCocoaEventMouseEntered;
+ case NS_MOUSE_EXIT_SYNTH:
+ return NPCocoaEventMouseExited;
+ case NS_MOUSE_MOVE:
+ {
+ // We don't know via information on events from the widget code whether or not
+ // we're dragging. The widget code just generates mouse move events from native
+ // drag events. If anybody is capturing, this is a drag event.
+ if (nsIPresShell::GetCapturingContent()) {
+ return NPCocoaEventMouseDragged;
+ }
+
+ return NPCocoaEventMouseMoved;
+ }
+ case NS_MOUSE_BUTTON_DOWN:
+ return NPCocoaEventMouseDown;
+ case NS_MOUSE_BUTTON_UP:
+ return NPCocoaEventMouseUp;
+ case NS_KEY_DOWN:
+ return NPCocoaEventKeyDown;
+ case NS_KEY_UP:
+ return NPCocoaEventKeyUp;
+ case NS_FOCUS_CONTENT:
+ case NS_BLUR_CONTENT:
+ return NPCocoaEventFocusChanged;
+ case NS_MOUSE_SCROLL:
+ return NPCocoaEventScrollWheel;
+ default:
+ return (NPCocoaEventType)0;
+ }
+}
+
+static NPCocoaEvent
+TranslateToNPCocoaEvent(WidgetGUIEvent* anEvent, nsIFrame* aObjectFrame)
+{
+ NPCocoaEvent cocoaEvent;
+ InitializeNPCocoaEvent(&cocoaEvent);
+ cocoaEvent.type = CocoaEventTypeForEvent(*anEvent, aObjectFrame);
+
+ if (anEvent->message == NS_MOUSE_MOVE ||
+ anEvent->message == NS_MOUSE_BUTTON_DOWN ||
+ anEvent->message == NS_MOUSE_BUTTON_UP ||
+ anEvent->message == NS_MOUSE_SCROLL ||
+ anEvent->message == NS_MOUSE_ENTER_SYNTH ||
+ anEvent->message == NS_MOUSE_EXIT_SYNTH)
+ {
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(anEvent, aObjectFrame) -
+ aObjectFrame->GetContentRectRelativeToSelf().TopLeft();
+ nsPresContext* presContext = aObjectFrame->PresContext();
+ // Plugin event coordinates need to be translated from device pixels
+ // into "display pixels" in HiDPI modes.
+ double scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
+ aObjectFrame->PresContext()->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+ size_t intScaleFactor = ceil(scaleFactor);
+ nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x) / intScaleFactor,
+ presContext->AppUnitsToDevPixels(pt.y) / intScaleFactor);
+ cocoaEvent.data.mouse.pluginX = double(ptPx.x);
+ cocoaEvent.data.mouse.pluginY = double(ptPx.y);
+ }
+
+ switch (anEvent->message) {
+ case NS_MOUSE_BUTTON_DOWN:
+ case NS_MOUSE_BUTTON_UP:
+ {
+ WidgetMouseEvent* mouseEvent = anEvent->AsMouseEvent();
+ if (mouseEvent) {
+ switch (mouseEvent->button) {
+ case WidgetMouseEvent::eLeftButton:
+ cocoaEvent.data.mouse.buttonNumber = 0;
+ break;
+ case WidgetMouseEvent::eRightButton:
+ cocoaEvent.data.mouse.buttonNumber = 1;
+ break;
+ case WidgetMouseEvent::eMiddleButton:
+ cocoaEvent.data.mouse.buttonNumber = 2;
+ break;
+ default:
+ NS_WARNING("Mouse button we don't know about?");
+ }
+ cocoaEvent.data.mouse.clickCount = mouseEvent->clickCount;
+ } else {
+ NS_WARNING("NS_MOUSE_BUTTON_UP/DOWN is not a WidgetMouseEvent?");
+ }
+ break;
+ }
+ case NS_MOUSE_SCROLL:
+ {
+ WidgetWheelEvent* wheelEvent = anEvent->AsWheelEvent();
+ if (wheelEvent) {
+ cocoaEvent.data.mouse.deltaX = wheelEvent->lineOrPageDeltaX;
+ cocoaEvent.data.mouse.deltaY = wheelEvent->lineOrPageDeltaY;
+ } else {
+ NS_WARNING("NS_MOUSE_SCROLL is not a WidgetWheelEvent? (could be, haven't checked)");
+ }
+ break;
+ }
+ case NS_KEY_DOWN:
+ case NS_KEY_UP:
+ {
+ WidgetKeyboardEvent* keyEvent = anEvent->AsKeyboardEvent();
+
+ cocoaEvent.data.key.keyCode = keyEvent->mNativeKeyCode;
+ cocoaEvent.data.key.isARepeat = keyEvent->mIsRepeat;
+ cocoaEvent.data.key.modifierFlags = keyEvent->mNativeModifierFlags;
+ const char16_t* nativeChars = keyEvent->mNativeCharacters.get();
+ cocoaEvent.data.key.characters =
+ (NPNSString*)::CFStringCreateWithCharacters(NULL,
+ reinterpret_cast<const UniChar*>(nativeChars),
+ keyEvent->mNativeCharacters.Length());
+ const char16_t* nativeCharsIgnoringModifiers = keyEvent->mNativeCharactersIgnoringModifiers.get();
+ cocoaEvent.data.key.charactersIgnoringModifiers =
+ (NPNSString*)::CFStringCreateWithCharacters(NULL,
+ reinterpret_cast<const UniChar*>(nativeCharsIgnoringModifiers),
+ keyEvent->mNativeCharactersIgnoringModifiers.Length());
+ break;
+ }
+ case NS_FOCUS_CONTENT:
+ case NS_BLUR_CONTENT:
+ cocoaEvent.data.focus.hasFocus = (anEvent->message == NS_FOCUS_CONTENT);
+ break;
+ default:
+ break;
+ }
+ return cocoaEvent;
+}
+
+void nsPluginInstanceOwner::PerformDelayedBlurs()
+{
+ nsCOMPtr<EventTarget> windowRoot = mContent->OwnerDoc()->GetWindow()->GetTopWindowRoot();
+ nsContentUtils::DispatchTrustedEvent(mContent->OwnerDoc(),
+ windowRoot,
+ NS_LITERAL_STRING("MozPerformDelayedBlur"),
+ false, false, nullptr);
+}
+
+#endif
+
nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent)
{
nsEventStatus rv = nsEventStatus_eIgnore;
- if (!mInstance || !mPluginFrame) // if mInstance is null, we shouldn't be here
+ if (!mInstance || !mPluginFrame) {
return nsEventStatus_eIgnore;
+ }
#ifdef XP_MACOSX
- if (!mWidget)
+ NPEventModel eventModel = GetEventModel();
+ if (eventModel != NPEventModelCocoa) {
return nsEventStatus_eIgnore;
-
- // we never care about synthesized mouse enter
- if (anEvent.message == NS_MOUSE_ENTER_SYNTH)
+ }
+
+ // In the Cocoa event model, focus is per-window. Don't tell a plugin it lost
+ // focus unless it lost focus within the window. For example, ignore a blur
+ // event if it's coming due to the plugin's window deactivating.
+ if (anEvent.message == NS_BLUR_CONTENT &&
+ ContentIsFocusedWithinWindow(mContent)) {
+ mShouldBlurOnActivate = true;
return nsEventStatus_eIgnore;
-
- nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
- if (!pluginWidget || NS_FAILED(pluginWidget->StartDrawPlugin()))
+ }
+
+ // Also, don't tell the plugin it gained focus again after we've already given
+ // it focus. This might happen if it has focus, its window is blurred, then the
+ // window is made active again. The plugin never lost in-window focus, so it
+ // shouldn't get a focus event again.
+ if (anEvent.message == NS_FOCUS_CONTENT &&
+ mLastContentFocused == true) {
+ mShouldBlurOnActivate = false;
return nsEventStatus_eIgnore;
-
- NPEventModel eventModel = GetEventModel();
-
- // If we have to synthesize an event we'll use one of these.
- NPCocoaEvent synthCocoaEvent;
- const NPCocoaEvent* event = static_cast<const NPCocoaEvent*>(anEvent.mPluginEvent);
- nsPoint pt =
- nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mPluginFrame) -
- mPluginFrame->GetContentRectRelativeToSelf().TopLeft();
- nsPresContext* presContext = mPluginFrame->PresContext();
- // Plugin event coordinates need to be translated from device pixels
- // into "display pixels" in HiDPI modes.
- double scaleFactor = 1.0;
- GetContentsScaleFactor(&scaleFactor);
- size_t intScaleFactor = ceil(scaleFactor);
- nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x) / intScaleFactor,
- presContext->AppUnitsToDevPixels(pt.y) / intScaleFactor);
-
- if (!event) {
- InitializeNPCocoaEvent(&synthCocoaEvent);
- switch (anEvent.message) {
- case NS_MOUSE_MOVE:
- {
- // Ignore mouse-moved events that happen as part of a dragging
- // operation that started over another frame. See bug 525078.
- nsRefPtr<nsFrameSelection> frameselection = mPluginFrame->GetFrameSelection();
- if (!frameselection->GetDragState() ||
- (nsIPresShell::GetCapturingContent() == mPluginFrame->GetContent())) {
- synthCocoaEvent.type = NPCocoaEventMouseMoved;
- synthCocoaEvent.data.mouse.pluginX = static_cast<double>(ptPx.x);
- synthCocoaEvent.data.mouse.pluginY = static_cast<double>(ptPx.y);
- event = &synthCocoaEvent;
- }
+ }
+
+ // Now, if we're going to send a focus event, update mLastContentFocused and
+ // tell any plugins in our window that we have taken focus, so they should
+ // perform any delayed blurs.
+ if (anEvent.message == NS_FOCUS_CONTENT ||
+ anEvent.message == NS_BLUR_CONTENT) {
+ mLastContentFocused = (anEvent.message == NS_FOCUS_CONTENT);
+ mShouldBlurOnActivate = false;
+ PerformDelayedBlurs();
+ }
+
+ NPCocoaEvent cocoaEvent = TranslateToNPCocoaEvent(const_cast<WidgetGUIEvent*>(&anEvent), mPluginFrame);
+ if (cocoaEvent.type == (NPCocoaEventType)0) {
+ return nsEventStatus_eIgnore;
+ }
+
+ if (cocoaEvent.type == NPCocoaEventKeyDown) {
+ ComplexTextInputPanel* ctiPanel = ComplexTextInputPanel::GetSharedComplexTextInputPanel();
+ if (ctiPanel && ctiPanel->IsInComposition()) {
+ nsAutoString outText;
+ ctiPanel->InterpretKeyEvent(&cocoaEvent, outText);
+ if (!outText.IsEmpty()) {
+ CFStringRef cfString = ::CFStringCreateWithCharacters(kCFAllocatorDefault,
+ reinterpret_cast<const UniChar*>(outText.get()),
+ outText.Length());
+
+ NPCocoaEvent textEvent;
+ InitializeNPCocoaEvent(&textEvent);
+ textEvent.type = NPCocoaEventTextInput;
+ textEvent.data.text.text = (NPNSString*)cfString;
+
+ mInstance->HandleEvent(&textEvent, nullptr);
}
- break;
- case NS_MOUSE_BUTTON_DOWN:
- synthCocoaEvent.type = NPCocoaEventMouseDown;
- synthCocoaEvent.data.mouse.pluginX = static_cast<double>(ptPx.x);
- synthCocoaEvent.data.mouse.pluginY = static_cast<double>(ptPx.y);
- event = &synthCocoaEvent;
- break;
- case NS_MOUSE_BUTTON_UP:
- // If we're in a dragging operation that started over another frame,
- // convert it into a mouse-entered event (in the Cocoa Event Model).
- // See bug 525078.
- if (anEvent.AsMouseEvent()->button == WidgetMouseEvent::eLeftButton &&
- (nsIPresShell::GetCapturingContent() != mPluginFrame->GetContent())) {
- synthCocoaEvent.type = NPCocoaEventMouseEntered;
- synthCocoaEvent.data.mouse.pluginX = static_cast<double>(ptPx.x);
- synthCocoaEvent.data.mouse.pluginY = static_cast<double>(ptPx.y);
- event = &synthCocoaEvent;
- } else {
- synthCocoaEvent.type = NPCocoaEventMouseUp;
- synthCocoaEvent.data.mouse.pluginX = static_cast<double>(ptPx.x);
- synthCocoaEvent.data.mouse.pluginY = static_cast<double>(ptPx.y);
- event = &synthCocoaEvent;
- }
- break;
- default:
- break;
- }
-
- // If we still don't have an event, bail.
- if (!event) {
- pluginWidget->EndDrawPlugin();
- return nsEventStatus_eIgnore;
+ return nsEventStatus_eConsumeNoDefault;
}
}
int16_t response = kNPEventNotHandled;
- void* window = FixUpPluginWindow(ePluginPaintEnable);
- if (window || (eventModel == NPEventModelCocoa)) {
- mInstance->HandleEvent(const_cast<NPCocoaEvent*>(event),
- &response,
- NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+ mInstance->HandleEvent(&cocoaEvent,
+ &response,
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+ if (response == kNPEventStartIME) {
+ nsAutoString outText;
+ ComplexTextInputPanel* ctiPanel = ComplexTextInputPanel::GetSharedComplexTextInputPanel();
+
+ // Place ctiPanel by passing the coordinates of the bottom-left of the plugin,
+ // in screen-coordinates.
+ double screenX, screenY;
+ ConvertPoint(0.0, mPluginFrame->GetScreenRect().height, NPCoordinateSpacePlugin,
+ &screenX, &screenY, NPCoordinateSpaceScreen);
+
+ ctiPanel->PlacePanel(screenX, screenY);
+ ctiPanel->InterpretKeyEvent(&cocoaEvent, outText);
+
+ if (!outText.IsEmpty()) {
+ CFStringRef cfString = ::CFStringCreateWithCharacters(kCFAllocatorDefault,
+ reinterpret_cast<const UniChar*>(outText.get()),
+ outText.Length());
+
+ NPCocoaEvent textEvent;
+ InitializeNPCocoaEvent(&textEvent);
+ textEvent.type = NPCocoaEventTextInput;
+ textEvent.data.text.text = (NPNSString*)cfString;
+
+ mInstance->HandleEvent(&textEvent, nullptr);
+ }
}
- if (eventModel == NPEventModelCocoa && response == kNPEventStartIME) {
- pluginWidget->StartComplexTextInputForCurrentEvent();
- }
-
- if ((response == kNPEventHandled || response == kNPEventStartIME) &&
- !(anEvent.message == NS_MOUSE_BUTTON_DOWN &&
- anEvent.AsMouseEvent()->button == WidgetMouseEvent::eLeftButton &&
- !mContentFocused)) {
+ bool handled = (response == kNPEventHandled || response == kNPEventStartIME);
+ bool leftMouseButtonDown = (anEvent.message == NS_MOUSE_BUTTON_DOWN) &&
+ (anEvent.AsMouseEvent()->button == WidgetMouseEvent::eLeftButton);
+ if (handled && !(leftMouseButtonDown && !mContentFocused)) {
rv = nsEventStatus_eConsumeNoDefault;
}
-
- pluginWidget->EndDrawPlugin();
#endif
#ifdef XP_WIN
// this code supports windowless plugins
const NPEvent *pPluginEvent = static_cast<const NPEvent*>(anEvent.mPluginEvent);
// we can get synthetic events from the EventStateManager... these
// have no pluginEvent
NPEvent pluginEvent;
@@ -2039,21 +2429,17 @@ void nsPluginInstanceOwner::Paint(const
GetContentsScaleFactor(&scaleFactor);
if (scaleFactor != 1.0) {
::CGContextScaleCTM(cgContext, scaleFactor, scaleFactor);
// Convert aDirtyRect from device pixels to "display pixels"
// for HiDPI modes
dirtyRectCopy.ScaleRoundOut(1.0 / scaleFactor);
}
- nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
- if (pluginWidget && NS_SUCCEEDED(pluginWidget->StartDrawPlugin())) {
- DoCocoaEventDrawRect(dirtyRectCopy, cgContext);
- pluginWidget->EndDrawPlugin();
- }
+ DoCocoaEventDrawRect(dirtyRectCopy, cgContext);
}
void nsPluginInstanceOwner::DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext)
{
if (!mInstance || !mPluginFrame)
return;
// The context given here is only valid during the HandleEvent call.
@@ -2404,36 +2790,30 @@ nsresult nsPluginInstanceOwner::Init(nsI
mContent->AddEventListener(NS_LITERAL_STRING("dragexit"), this, true);
mContent->AddEventListener(NS_LITERAL_STRING("dragstart"), this, true);
mContent->AddEventListener(NS_LITERAL_STRING("draggesture"), this, true);
mContent->AddEventListener(NS_LITERAL_STRING("dragend"), this, true);
return NS_OK;
}
-void* nsPluginInstanceOwner::GetPluginPortFromWidget()
+void* nsPluginInstanceOwner::GetPluginPort()
{
//!!! Port must be released for windowless plugins on Windows, because it is HDC !!!
void* result = nullptr;
if (mWidget) {
#ifdef XP_WIN
if (mPluginWindow && (mPluginWindow->type == NPWindowTypeDrawable))
result = mWidget->GetNativeData(NS_NATIVE_GRAPHIC); // HDC
else
#endif
-#ifdef XP_MACOSX
- if (GetDrawingModel() == NPDrawingModelCoreGraphics ||
- GetDrawingModel() == NPDrawingModelCoreAnimation ||
- GetDrawingModel() == NPDrawingModelInvalidatingCoreAnimation)
- result = mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT_CG);
- else
-#endif
result = mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT); // HWND/gdk window
}
+
return result;
}
void nsPluginInstanceOwner::ReleasePluginPort(void * pluginPort)
{
#ifdef XP_WIN
if (mWidget && mPluginWindow &&
mPluginWindow->type == NPWindowTypeDrawable) {
@@ -2498,31 +2878,16 @@ NS_IMETHODIMP nsPluginInstanceOwner::Cre
return rv;
}
}
mWidget->EnableDragDrop(true);
mWidget->Show(false);
mWidget->Enable(false);
-
-#ifdef XP_MACOSX
- // Now that we have a widget we want to set the event model before
- // any events are processed.
- nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
- if (!pluginWidget) {
- return NS_ERROR_FAILURE;
- }
- pluginWidget->SetPluginEventModel(GetEventModel());
- pluginWidget->SetPluginDrawingModel(GetDrawingModel());
-
- if (GetDrawingModel() == NPDrawingModelCoreAnimation) {
- AddToCARefreshTimer();
- }
-#endif
}
if (mPluginFrame) {
// nullptr widget is fine, will result in windowless setup.
mPluginFrame->PrepForDrawing(mWidget);
}
if (windowless) {
@@ -2543,100 +2908,65 @@ NS_IMETHODIMP nsPluginInstanceOwner::Cre
GetPluginDescription(description);
NS_NAMED_LITERAL_CSTRING(flash10Head, "Shockwave Flash 10.");
mFlash10Quirks = StringBeginsWith(description, flash10Head);
#endif
} else if (mWidget) {
// mPluginWindow->type is used in |GetPluginPort| so it must
// be initialized first
mPluginWindow->type = NPWindowTypeWindow;
- mPluginWindow->window = GetPluginPortFromWidget();
+ mPluginWindow->window = GetPluginPort();
// tell the plugin window about the widget
mPluginWindow->SetPluginWidget(mWidget);
// tell the widget about the current plugin instance owner.
nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
if (pluginWidget) {
pluginWidget->SetPluginInstanceOwner(this);
}
}
+#ifdef XP_MACOSX
+ if (GetDrawingModel() == NPDrawingModelCoreAnimation) {
+ AddToCARefreshTimer();
+ }
+#endif
+
mWidgetCreationComplete = true;
return NS_OK;
}
// Mac specific code to fix up the port location and clipping region
#ifdef XP_MACOSX
-void* nsPluginInstanceOwner::FixUpPluginWindow(int32_t inPaintState)
+void nsPluginInstanceOwner::FixUpPluginWindow(int32_t inPaintState)
{
- if (!mWidget || !mPluginWindow || !mInstance || !mPluginFrame)
- return nullptr;
-
- nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
- if (!pluginWidget)
- return nullptr;
+ if (!mPluginWindow || !mInstance || !mPluginFrame) {
+ return;
+ }
// If we've already set up a CGContext in nsPluginFrame::PaintPlugin(), we
- // don't want calls to SetPluginPortAndDetectChange() to step on our work.
+ // don't want calls to SetPluginPort() to step on our work.
if (mInCGPaintLevel < 1) {
- SetPluginPortAndDetectChange();
- }
-
- // We'll need the top-level Cocoa window for the Cocoa event model.
- nsIWidget* widget = mPluginFrame->GetNearestWidget();
- if (!widget)
- return nullptr;
- void *cocoaTopLevelWindow = widget->GetNativeData(NS_NATIVE_WINDOW);
- // We don't expect to have a top level window in a content process
- if (!cocoaTopLevelWindow && XRE_GetProcessType() == GeckoProcessType_Default) {
- return nullptr;
- }
-
- nsIntPoint pluginOrigin;
- nsIntRect widgetClip;
- bool widgetVisible;
- pluginWidget->GetPluginClipRect(widgetClip, pluginOrigin, widgetVisible);
- // TODO: Detect visibility for e10s mac plugins
- if (XRE_GetProcessType() != GeckoProcessType_Default) {
- widgetVisible = true;
+ SetPluginPort();
}
- mWidgetVisible = widgetVisible;
-
- // printf("GetPluginClipRect returning visible %d\n", widgetVisible);
-
- // This would be a lot easier if we could use obj-c here,
- // but we can't. Since we have only nsIWidget and we can't
- // use its native widget (an obj-c object) we have to go
- // from the widget's screen coordinates to its window coords
- // instead of straight to window coords.
- nsIntPoint geckoScreenCoords = mWidget->WidgetToScreenOffset();
-
- nsRect windowRect;
- if (cocoaTopLevelWindow) {
- NS_NPAPI_CocoaWindowFrame(cocoaTopLevelWindow, windowRect);
- }
-
- double scaleFactor = 1.0;
- GetContentsScaleFactor(&scaleFactor);
- int intScaleFactor = ceil(scaleFactor);
-
- // Convert geckoScreenCoords from device pixels to "display pixels"
- // for HiDPI modes.
- mPluginWindow->x = geckoScreenCoords.x/intScaleFactor - windowRect.x;
- mPluginWindow->y = geckoScreenCoords.y/intScaleFactor - windowRect.y;
+
+ nsIntSize widgetClip = mPluginFrame->GetWidgetlessClipRect().Size();
+
+ mPluginWindow->x = 0;
+ mPluginWindow->y = 0;
NPRect oldClipRect = mPluginWindow->clipRect;
// fix up the clipping region
- mPluginWindow->clipRect.top = widgetClip.y;
- mPluginWindow->clipRect.left = widgetClip.x;
-
- if (!mWidgetVisible || inPaintState == ePluginPaintDisable) {
+ mPluginWindow->clipRect.top = 0;
+ mPluginWindow->clipRect.left = 0;
+
+ if (inPaintState == ePluginPaintDisable) {
mPluginWindow->clipRect.bottom = mPluginWindow->clipRect.top;
mPluginWindow->clipRect.right = mPluginWindow->clipRect.left;
}
else if (XRE_GetProcessType() != GeckoProcessType_Default)
{
// For e10s we only support async windowless plugin. This means that
// we're always going to allocate a full window for the plugin to draw
// for even if the plugin is mostly outside of the scroll port. Thus
@@ -2650,44 +2980,88 @@ void* nsPluginInstanceOwner::FixUpPlugin
mPluginWindow->clipRect.right = mPluginWindow->clipRect.left + widgetClip.width;
}
// if the clip rect changed, call SetWindow()
// (RealPlayer needs this to draw correctly)
if (mPluginWindow->clipRect.left != oldClipRect.left ||
mPluginWindow->clipRect.top != oldClipRect.top ||
mPluginWindow->clipRect.right != oldClipRect.right ||
- mPluginWindow->clipRect.bottom != oldClipRect.bottom ||
- mPluginPortChanged)
+ mPluginWindow->clipRect.bottom != oldClipRect.bottom)
{
if (UseAsyncRendering()) {
mInstance->AsyncSetWindow(mPluginWindow);
}
else {
mPluginWindow->CallSetWindow(mInstance);
}
- mPluginPortChanged = false;
}
// After the first NPP_SetWindow call we need to send an initial
// top-level window focus event.
if (!mSentInitialTopLevelWindowEvent) {
// Set this before calling ProcessEvent to avoid endless recursion.
mSentInitialTopLevelWindowEvent = true;
- WidgetPluginEvent pluginEvent(true, NS_PLUGIN_FOCUS_EVENT, nullptr);
- NPCocoaEvent cocoaEvent;
- InitializeNPCocoaEvent(&cocoaEvent);
- cocoaEvent.type = NPCocoaEventWindowFocusChanged;
- cocoaEvent.data.focus.hasFocus = cocoaTopLevelWindow ? NS_NPAPI_CocoaWindowIsMain(cocoaTopLevelWindow) : true;
- pluginEvent.mPluginEvent.Copy(cocoaEvent);
- ProcessEvent(pluginEvent);
+ bool isActive = WindowIsActive();
+ SendWindowFocusChanged(isActive);
+ mLastWindowIsActive = isActive;
+ }
+}
+
+void
+nsPluginInstanceOwner::WindowFocusMayHaveChanged()
+{
+ if (!mSentInitialTopLevelWindowEvent) {
+ return;
+ }
+
+ bool isActive = WindowIsActive();
+ if (isActive != mLastWindowIsActive) {
+ SendWindowFocusChanged(isActive);
+ mLastWindowIsActive = isActive;
+ }
+}
+
+bool
+nsPluginInstanceOwner::WindowIsActive()
+{
+ if (!mPluginFrame) {
+ return false;
}
- return nullptr;
+ EventStates docState = mPluginFrame->GetContent()->OwnerDoc()->GetDocumentState();
+ return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
+}
+
+void
+nsPluginInstanceOwner::SendWindowFocusChanged(bool aIsActive)
+{
+ if (!mInstance) {
+ return;
+ }
+
+ NPCocoaEvent cocoaEvent;
+ InitializeNPCocoaEvent(&cocoaEvent);
+ cocoaEvent.type = NPCocoaEventWindowFocusChanged;
+ cocoaEvent.data.focus.hasFocus = aIsActive;
+ mInstance->HandleEvent(&cocoaEvent,
+ nullptr,
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+}
+
+void
+nsPluginInstanceOwner::ResolutionMayHaveChanged()
+{
+ double scaleFactor = 1.0;
+ GetContentsScaleFactor(&scaleFactor);
+ if (scaleFactor != mLastScaleFactor) {
+ ContentsScaleFactorChanged(scaleFactor);
+ mLastScaleFactor = scaleFactor;
+ }
}
void
nsPluginInstanceOwner::HidePluginWindow()
{
if (!mPluginWindow || !mInstance) {
return;
}
@@ -2831,16 +3205,28 @@ void nsPluginInstanceOwner::SetFrame(nsP
{
// Don't do anything if the frame situation hasn't changed.
if (mPluginFrame == aFrame) {
return;
}
// If we already have a frame that is changing or going away...
if (mPluginFrame) {
+ if (mContent && mContent->OwnerDoc() && mContent->OwnerDoc()->GetWindow()) {
+ nsCOMPtr<EventTarget> windowRoot = mContent->OwnerDoc()->GetWindow()->GetTopWindowRoot();
+ if (windowRoot) {
+ windowRoot->RemoveEventListener(NS_LITERAL_STRING("activate"),
+ this, false);
+ windowRoot->RemoveEventListener(NS_LITERAL_STRING("deactivate"),
+ this, false);
+ windowRoot->RemoveEventListener(NS_LITERAL_STRING("MozPerformDelayedBlur"),
+ this, false);
+ }
+ }
+
// Make sure the old frame isn't holding a reference to us.
mPluginFrame->SetInstanceOwner(nullptr);
}
// Swap in the new frame (or no frame)
mPluginFrame = aFrame;
// Set up a new frame
@@ -2854,16 +3240,29 @@ void nsPluginInstanceOwner::SetFrame(nsP
mPluginFrame->FixupWindow(mPluginFrame->GetContentRectRelativeToSelf().Size());
mPluginFrame->InvalidateFrame();
nsFocusManager* fm = nsFocusManager::GetFocusManager();
const nsIContent* content = aFrame->GetContent();
if (fm && content) {
mContentFocused = (content == fm->GetFocusedContent());
}
+
+ // Register for widget-focus events on the window root.
+ if (mContent && mContent->OwnerDoc() && mContent->OwnerDoc()->GetWindow()) {
+ nsCOMPtr<EventTarget> windowRoot = mContent->OwnerDoc()->GetWindow()->GetTopWindowRoot();
+ if (windowRoot) {
+ windowRoot->AddEventListener(NS_LITERAL_STRING("activate"),
+ this, false, false);
+ windowRoot->AddEventListener(NS_LITERAL_STRING("deactivate"),
+ this, false, false);
+ windowRoot->AddEventListener(NS_LITERAL_STRING("MozPerformDelayedBlur"),
+ this, false, false);
+ }
+ }
}
}
nsPluginFrame* nsPluginInstanceOwner::GetFrame()
{
return mPluginFrame;
}
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -28,17 +28,22 @@ struct nsIntRect;
class nsPluginDOMContextMenuListener;
class nsPluginFrame;
class nsDisplayListBuilder;
namespace mozilla {
namespace dom {
struct MozPluginParameter;
}
+namespace widget {
+class PuppetWidget;
}
+}
+
+using mozilla::widget::PuppetWidget;
#ifdef MOZ_X11
class gfxXlibSurface;
#ifdef MOZ_WIDGET_QT
#include "gfxQtNativeRenderer.h"
#else
#include "gfxXlibNativeRenderer.h"
#endif
@@ -57,16 +62,17 @@ public:
NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
NS_IMETHOD GetURL(const char *aURL, const char *aTarget,
nsIInputStream *aPostStream,
void *aHeadersData, uint32_t aHeadersDataLen) MOZ_OVERRIDE;
NS_IMETHOD ShowStatus(const char16_t *aStatusMsg) MOZ_OVERRIDE;
+ // This can go away, just leaving it here to avoid changing the interface.
NPError ShowNativeContextMenu(NPMenu* menu, void* event) MOZ_OVERRIDE;
NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
double *destX, double *destY, NPCoordinateSpace destSpace) MOZ_OVERRIDE;
/**
* Get the type of the HTML tag that was used ot instantiate this
* plugin. Currently supported tags are EMBED, OBJECT and APPLET.
@@ -103,45 +109,45 @@ public:
const gfxRect& aFrameRect,
const gfxRect& aDirtyRect);
#endif
//locals
nsresult Init(nsIContent* aContent);
- void* GetPluginPortFromWidget();
+ void* GetPluginPort();
void ReleasePluginPort(void* pluginPort);
nsEventStatus ProcessEvent(const mozilla::WidgetGUIEvent& anEvent);
#ifdef XP_MACOSX
enum { ePluginPaintEnable, ePluginPaintDisable };
-
+
+ void WindowFocusMayHaveChanged();
+ void ResolutionMayHaveChanged();
+
+ bool WindowIsActive();
+ void SendWindowFocusChanged(bool aIsActive);
NPDrawingModel GetDrawingModel();
bool IsRemoteDrawingCoreAnimation();
nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
NPEventModel GetEventModel();
static void CARefresh(nsITimer *aTimer, void *aClosure);
void AddToCARefreshTimer();
void RemoveFromCARefreshTimer();
// This calls into the plugin (NPP_SetWindow) and can run script.
- void* FixUpPluginWindow(int32_t inPaintState);
+ void FixUpPluginWindow(int32_t inPaintState);
void HidePluginWindow();
- // Set a flag that (if true) indicates the plugin port info has changed and
- // SetWindow() needs to be called.
- void SetPluginPortChanged(bool aState) { mPluginPortChanged = aState; }
// Return a pointer to the internal nsPluginPort structure that's used to
// store a copy of plugin port info and to detect when it's been changed.
void* GetPluginPortCopy();
// Set plugin port info in the plugin (in the 'window' member of the
- // NPWindow structure passed to the plugin by SetWindow()) and set a
- // flag (mPluginPortChanged) to indicate whether or not this info has
- // changed, and SetWindow() needs to be called again.
- void* SetPluginPortAndDetectChange();
+ // NPWindow structure passed to the plugin by SetWindow()).
+ void SetPluginPort();
// Flag when we've set up a Thebes (and CoreGraphics) context in
// nsPluginFrame::PaintPlugin(). We need to know this in
// FixUpPluginWindow() (i.e. we need to know when FixUpPluginWindow() has
// been called from nsPluginFrame::PaintPlugin() when we're using the
// CoreGraphics drawing model).
void BeginCGPaint();
void EndCGPaint();
#else // XP_MACOSX
@@ -283,27 +289,29 @@ private:
NP_CGContext mCGPluginPortCopy;
int32_t mInCGPaintLevel;
mozilla::RefPtr<MacIOSurface> mIOSurface;
mozilla::RefPtr<nsCARenderer> mCARenderer;
CGColorSpaceRef mColorProfile;
static nsCOMPtr<nsITimer> *sCATimer;
static nsTArray<nsPluginInstanceOwner*> *sCARefreshListeners;
bool mSentInitialTopLevelWindowEvent;
+ bool mLastWindowIsActive;
+ bool mLastContentFocused;
+ double mLastScaleFactor;
+ // True if, the next time the window is activated, we should blur ourselves.
+ bool mShouldBlurOnActivate;
#endif
// Initially, the event loop nesting level we were created on, it's updated
// if we detect the appshell is on a lower level as long as we're not stopped.
// We delay DoStopPlugin() until the appshell reaches this level or lower.
uint32_t mLastEventloopNestingLevel;
bool mContentFocused;
bool mWidgetVisible; // used on Mac to store our widget's visible state
-#ifdef XP_MACOSX
- bool mPluginPortChanged;
-#endif
#ifdef MOZ_X11
// Used with windowless plugins only, initialized in CreateWidget().
bool mFlash10Quirks;
#endif
bool mPluginWindowVisible;
bool mPluginDocumentActiveState;
#ifdef XP_MACOSX
@@ -318,16 +326,26 @@ private:
// pointer to wrapper for nsIDOMContextMenuListener
nsRefPtr<nsPluginDOMContextMenuListener> mCXMenuListener;
nsresult DispatchKeyToPlugin(nsIDOMEvent* aKeyEvent);
nsresult DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent,
bool aAllowPropagate = false);
nsresult DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent);
+#ifdef XP_MACOSX
+ static NPBool ConvertPointPuppet(PuppetWidget *widget, nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
+ double *destX, double *destY, NPCoordinateSpace destSpace);
+ static NPBool ConvertPointNoPuppet(nsIWidget *widget, nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
+ double *destX, double *destY, NPCoordinateSpace destSpace);
+ void PerformDelayedBlurs();
+#endif // XP_MACOSX
+
int mLastMouseDownButtonType;
#ifdef MOZ_X11
class Renderer
#if defined(MOZ_WIDGET_QT)
: public gfxQtNativeRenderer
#else
: public gfxXlibNativeRenderer
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -383,22 +383,22 @@ PluginInstanceChild::NPN_GetValue(NPNVar
#ifdef XP_MACOSX
case NPNVsupportsCoreGraphicsBool: {
*((NPBool*)aValue) = true;
return NPERR_NO_ERROR;
}
case NPNVsupportsCoreAnimationBool: {
- *((NPBool*)aValue) = nsCocoaFeatures::SupportCoreAnimationPlugins();
+ *((NPBool*)aValue) = true;
return NPERR_NO_ERROR;
}
case NPNVsupportsInvalidatingCoreAnimationBool: {
- *((NPBool*)aValue) = nsCocoaFeatures::SupportCoreAnimationPlugins();
+ *((NPBool*)aValue) = true;
return NPERR_NO_ERROR;
}
case NPNVsupportsCompositingCoreAnimationPluginsBool: {
*((NPBool*)aValue) = true;
return NPERR_NO_ERROR;
}
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -2,16 +2,17 @@
* vim: sw=4 ts=4 et :
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/DebugOnly.h"
#include <stdint.h> // for intptr_t
+#include "mozilla/Telemetry.h"
#include "PluginInstanceParent.h"
#include "BrowserStreamParent.h"
#include "PluginBackgroundDestroyer.h"
#include "PluginModuleParent.h"
#include "PluginStreamParent.h"
#include "StreamNotifyParent.h"
#include "npfunctions.h"
#include "nsAutoPtr.h"
@@ -140,18 +141,23 @@ PluginInstanceParent::ActorDestroy(Actor
#endif
}
}
NPError
PluginInstanceParent::Destroy()
{
NPError retval;
- if (!CallNPP_Destroy(&retval))
- retval = NPERR_GENERIC_ERROR;
+ { // Scope for timer
+ Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGIN_INSTANCE_DESTROY_MS>
+ timer(Module()->GetHistogramKey());
+ if (!CallNPP_Destroy(&retval)) {
+ retval = NPERR_GENERIC_ERROR;
+ }
+ }
#if defined(OS_WIN)
SharedSurfaceRelease();
UnsubclassPluginWindow();
#endif
return retval;
}
@@ -1299,25 +1305,30 @@ PluginInstanceParent::NPP_NewStream(NPMI
NPBool seekable, uint16_t* stype)
{
PLUGIN_LOG_DEBUG(("%s (type=%s, stream=%p, seekable=%i)",
FULLFUNCTION, (char*) type, (void*) stream, (int) seekable));
BrowserStreamParent* bs = new BrowserStreamParent(this, stream);
NPError err;
- if (!CallPBrowserStreamConstructor(bs,
- NullableString(stream->url),
- stream->end,
- stream->lastmodified,
- static_cast<PStreamNotifyParent*>(stream->notifyData),
- NullableString(stream->headers),
- NullableString(type), seekable,
- &err, stype))
- return NPERR_GENERIC_ERROR;
+ { // Scope for timer
+ Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGIN_STREAM_INIT_MS>
+ timer(Module()->GetHistogramKey());
+ if (!CallPBrowserStreamConstructor(bs,
+ NullableString(stream->url),
+ stream->end,
+ stream->lastmodified,
+ static_cast<PStreamNotifyParent*>(stream->notifyData),
+ NullableString(stream->headers),
+ NullableString(type), seekable,
+ &err, stype)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ }
if (NPERR_NO_ERROR != err)
unused << PBrowserStreamParent::Send__delete__(bs);
return err;
}
NPError
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -145,17 +145,17 @@ protected:
virtual bool RecvStartProfiler(const uint32_t& aEntries,
const double& aInterval,
const nsTArray<nsCString>& aFeatures,
const nsTArray<nsCString>& aThreadNameFilters) MOZ_OVERRIDE;
virtual bool RecvStopProfiler() MOZ_OVERRIDE;
virtual bool AnswerGetProfile(nsCString* aProfile) MOZ_OVERRIDE;
public:
- PluginModuleChild(bool aIsChrome);
+ explicit PluginModuleChild(bool aIsChrome);
virtual ~PluginModuleChild();
bool CommonInit(base::ProcessHandle aParentProcessHandle,
MessageLoop* aIOLoop,
IPC::Channel* aChannel);
// aPluginFilename is UTF8, not native-charset!
bool InitForChrome(const std::string& aPluginFilename,
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -16,16 +16,17 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/PCrashReporterParent.h"
#include "mozilla/ipc/MessageChannel.h"
#include "mozilla/plugins/BrowserStreamParent.h"
#include "mozilla/plugins/PluginBridge.h"
#include "mozilla/plugins/PluginInstanceParent.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
+#include "mozilla/Telemetry.h"
#include "mozilla/unused.h"
#include "nsAutoPtr.h"
#include "nsCRT.h"
#include "nsIFile.h"
#include "nsIObserverService.h"
#include "nsNPAPIPlugin.h"
#include "nsPrintfCString.h"
#include "prsystem.h"
@@ -92,108 +93,56 @@ mozilla::plugins::SetupBridge(uint32_t a
nsresult rv = host->GetPluginForContentProcess(aPluginId, getter_AddRefs(plugin));
if (NS_FAILED(rv)) {
return false;
}
PluginModuleParent* chromeParent = static_cast<PluginModuleParent*>(plugin->GetLibrary());
return PPluginModule::Bridge(aContentParent, chromeParent);
}
-// A linked list used to fetch newly created PluginModuleContentParent instances
-// for LoadModule. Each pending LoadModule call owns an element in this list.
-// The element's mModule field is filled in when the new
-// PluginModuleContentParent arrives from chrome.
-struct MOZ_STACK_CLASS SavedPluginModule
-{
- SavedPluginModule() : mModule(nullptr), mNext(sSavedModuleStack)
- {
- sSavedModuleStack = this;
- }
- ~SavedPluginModule()
- {
- MOZ_ASSERT(sSavedModuleStack == this);
- sSavedModuleStack = mNext;
- }
-
- PluginModuleContentParent* GetModule() { return mModule; }
-
- // LoadModule can be on the stack multiple times since the intr message it
- // sends will dispatch arbitrary incoming messages from the chrome process,
- // which can include new HTTP data. This makes it somewhat tricky to match
- // up the object created in PluginModuleContentParent::Create with the
- // LoadModule call that asked for it.
- //
- // Each invocation of LoadModule pushes a new SavedPluginModule object on
- // the sSavedModuleStack stack, with the most recent invocation at the
- // front. LoadModule messages are always processed by the chrome process in
- // order, and PluginModuleContentParent allocation messages will also be
- // received in order. Therefore, we need to match up the first received
- // PluginModuleContentParent creation message with the first sent LoadModule
- // call. This call will be the last one in the list that doesn't already
- // have a module attached to it.
- static void SaveModule(PluginModuleContentParent* module)
- {
- SavedPluginModule* saved = sSavedModuleStack;
- SavedPluginModule* prev = nullptr;
- while (saved && !saved->mModule) {
- prev = saved;
- saved = saved->mNext;
- }
- MOZ_ASSERT(prev);
- MOZ_ASSERT(!prev->mModule);
- prev->mModule = module;
- }
-
-private:
- PluginModuleContentParent* mModule;
- SavedPluginModule* mNext;
-
- static SavedPluginModule* sSavedModuleStack;
-};
-
-SavedPluginModule* SavedPluginModule::sSavedModuleStack;
+PluginModuleContentParent* PluginModuleContentParent::sSavedModuleParent;
/* static */ PluginLibrary*
PluginModuleContentParent::LoadModule(uint32_t aPluginId)
{
- SavedPluginModule saved;
-
+ MOZ_ASSERT(!sSavedModuleParent);
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
/*
* We send a LoadPlugin message to the chrome process using an intr
- * message. Before it sends its response, it sends a message to create a
- * PluginModuleContentParent instance. That message is handled by
- * PluginModuleContentParent::Create. See SavedPluginModule for details
- * about how we match up the result of PluginModuleContentParent::Create
- * with the LoadModule call that requested it.
+ * message. Before it sends its response, it sends a message to create
+ * PluginModuleParent instance. That message is handled by
+ * PluginModuleContentParent::Create, which saves the instance in
+ * sSavedModuleParent. We fetch it from there after LoadPlugin finishes.
*/
dom::ContentChild* cp = dom::ContentChild::GetSingleton();
if (!cp->CallLoadPlugin(aPluginId)) {
return nullptr;
}
- PluginModuleContentParent* parent = saved.GetModule();
+ PluginModuleContentParent* parent = sSavedModuleParent;
MOZ_ASSERT(parent);
+ sSavedModuleParent = nullptr;
return parent;
}
/* static */ PluginModuleContentParent*
PluginModuleContentParent::Create(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)
{
nsAutoPtr<PluginModuleContentParent> parent(new PluginModuleContentParent());
ProcessHandle handle;
if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
// Bug 1090578 - need to kill |aOtherProcess|, it's boned.
return nullptr;
}
- SavedPluginModule::SaveModule(parent);
+ MOZ_ASSERT(!sSavedModuleParent);
+ sSavedModuleParent = parent;
DebugOnly<bool> ok = parent->Open(aTransport, handle, XRE_GetIOMessageLoop(),
mozilla::ipc::ParentSide);
MOZ_ASSERT(ok);
// Request Windows message deferral behavior on our channel. This
// applies to the top level and all sub plugin protocols since they
// all share the same channel.
@@ -207,22 +156,25 @@ PluginLibrary*
PluginModuleChromeParent::LoadModule(const char* aFilePath, uint32_t aPluginId)
{
PLUGIN_LOG_DEBUG_FUNCTION;
int32_t prefSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
// Block on the child process being launched and initialized.
nsAutoPtr<PluginModuleChromeParent> parent(new PluginModuleChromeParent(aFilePath, aPluginId));
+ TimeStamp launchStart = TimeStamp::Now();
bool launched = parent->mSubprocess->Launch(prefSecs * 1000);
if (!launched) {
// We never reached open
parent->mShutdown = true;
return nullptr;
}
+ TimeStamp launchEnd = TimeStamp::Now();
+ parent->mTimeBlocked = (launchEnd - launchStart);
parent->Open(parent->mSubprocess->GetChannel(),
parent->mSubprocess->GetChildProcessHandle());
// Request Windows message deferral behavior on our channel. This
// applies to the top level and all sub plugin protocols since they
// all share the same channel.
parent->GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
@@ -1425,24 +1377,35 @@ PluginModuleParent::NP_Initialize(NPNets
*error = NPERR_GENERIC_ERROR;
return NS_ERROR_FAILURE;
}
*error = NPERR_NO_ERROR;
if (IsChrome()) {
PluginSettings settings;
GetSettings(&settings);
+ TimeStamp callNpInitStart = TimeStamp::Now();
if (!CallNP_Initialize(settings, error)) {
Close();
return NS_ERROR_FAILURE;
}
else if (*error != NPERR_NO_ERROR) {
Close();
return NS_OK;
}
+ TimeStamp callNpInitEnd = TimeStamp::Now();
+ mTimeBlocked += (callNpInitEnd - callNpInitStart);
+ /** mTimeBlocked measures the time that the main thread has been blocked
+ * on plugin module initialization. As implemented, this is the sum of
+ * plugin-container launch + NP_Initialize
+ */
+ Telemetry::Accumulate(Telemetry::BLOCKED_ON_PLUGIN_MODULE_INIT_MS,
+ GetHistogramKey(),
+ static_cast<uint32_t>(mTimeBlocked.ToMilliseconds()));
+ mTimeBlocked = TimeDuration();
}
SetPluginFuncs(pFuncs);
return NS_OK;
}
#else
nsresult
@@ -1465,24 +1428,27 @@ nsresult
PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
{
nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error);
if (NS_FAILED(rv))
return rv;
PluginSettings settings;
GetSettings(&settings);
+ TimeStamp callNpInitStart = TimeStamp::Now();
if (!CallNP_Initialize(settings, error)) {
Close();
return NS_ERROR_FAILURE;
}
if (*error != NPERR_NO_ERROR) {
Close();
return NS_OK;
}
+ TimeStamp callNpInitEnd = TimeStamp::Now();
+ mTimeBlocked += (callNpInitEnd - callNpInitStart);
#if defined XP_WIN
// Send the info needed to join the chrome process's audio session to the
// plugin process
nsID id;
nsString sessionName;
nsString iconPath;
@@ -1490,16 +1456,27 @@ PluginModuleChromeParent::NP_Initialize(
iconPath)))
unused << SendSetAudioSessionData(id, sessionName, iconPath);
#endif
#ifdef MOZ_CRASHREPORTER_INJECTOR
InitializeInjector();
#endif
+ /** This Accumulate must be placed below the call to InitializeInjector()
+ * because mTimeBlocked is modified in that function.
+ * mTimeBlocked measures the time that the main thread has been blocked
+ * on plugin module initialization. As implemented, this is the sum of
+ * plugin-container launch + toolhelp32 snapshot + NP_Initialize
+ */
+ Telemetry::Accumulate(Telemetry::BLOCKED_ON_PLUGIN_MODULE_INIT_MS,
+ GetHistogramKey(),
+ static_cast<uint32_t>(mTimeBlocked.ToMilliseconds()));
+ mTimeBlocked = TimeDuration();
+
return NS_OK;
}
#endif
nsresult
PluginModuleParent::NP_Shutdown(NPError* error)
{
PLUGIN_LOG_DEBUG_METHOD;
@@ -1603,27 +1580,31 @@ PluginModuleParent::NPP_New(NPMIMEType p
if (!parentInstance->Init()) {
delete parentInstance;
return NS_ERROR_FAILURE;
}
instance->pdata = parentInstance;
- if (!CallPPluginInstanceConstructor(parentInstance,
- nsDependentCString(pluginType), mode,
- names, values, error)) {
- // |parentInstance| is automatically deleted.
- instance->pdata = nullptr;
- // if IPC is down, we'll get an immediate "failed" return, but
- // without *error being set. So make sure that the error
- // condition is signaled to nsNPAPIPluginInstance
- if (NPERR_NO_ERROR == *error)
- *error = NPERR_GENERIC_ERROR;
- return NS_ERROR_FAILURE;
+ { // Scope for timer
+ Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGIN_INSTANCE_INIT_MS>
+ timer(GetHistogramKey());
+ if (!CallPPluginInstanceConstructor(parentInstance,
+ nsDependentCString(pluginType), mode,
+ names, values, error)) {
+ // |parentInstance| is automatically deleted.
+ instance->pdata = nullptr;
+ // if IPC is down, we'll get an immediate "failed" return, but
+ // without *error being set. So make sure that the error
+ // condition is signaled to nsNPAPIPluginInstance
+ if (NPERR_NO_ERROR == *error)
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
}
if (*error != NPERR_NO_ERROR) {
NPP_Destroy(instance, 0);
return NS_ERROR_FAILURE;
}
UpdatePluginTimeout();
@@ -1960,19 +1941,22 @@ PluginModuleChromeParent::InitializeInje
int32_t lastSlash = path.RFindCharInSet("\\/");
if (kNotFound == lastSlash)
return;
if (!StringBeginsWith(Substring(path, lastSlash + 1),
NS_LITERAL_CSTRING(FLASH_PLUGIN_PREFIX)))
return;
+ TimeStamp th32Start = TimeStamp::Now();
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == snapshot)
return;
+ TimeStamp th32End = TimeStamp::Now();
+ mTimeBlocked += (th32End - th32Start);
DWORD pluginProcessPID = GetProcessId(Process()->GetChildProcessHandle());
mFlashProcess1 = GetFlashChildOfPID(pluginProcessPID, snapshot);
if (mFlashProcess1) {
InjectCrashReporterIntoProcess(mFlashProcess1, this);
mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, snapshot);
if (mFlashProcess2) {
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -11,16 +11,17 @@
#include "mozilla/FileUtils.h"
#include "mozilla/HangMonitor.h"
#include "mozilla/PluginLibrary.h"
#include "mozilla/plugins/ScopedMethodFactory.h"
#include "mozilla/plugins/PluginProcessParent.h"
#include "mozilla/plugins/PPluginModuleParent.h"
#include "mozilla/plugins/PluginMessageUtils.h"
#include "mozilla/plugins/PluginTypes.h"
+#include "mozilla/TimeStamp.h"
#include "npapi.h"
#include "npfunctions.h"
#include "nsAutoPtr.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsIObserver.h"
#ifdef MOZ_CRASHREPORTER
@@ -100,16 +101,20 @@ public:
}
bool OkToCleanup() const {
return !IsOnCxxStack();
}
void ProcessRemoteNativeEventsInInterruptCall();
+ nsCString GetHistogramKey() const {
+ return mPluginName + mPluginVersion;
+ }
+
protected:
virtual mozilla::ipc::RacyInterruptPolicy
MediateInterruptRace(const Message& parent, const Message& child) MOZ_OVERRIDE
{
return MediateRace(parent, child);
}
virtual bool
@@ -240,16 +245,17 @@ protected:
bool mGetSitesWithDataSupported;
const NPNetscapeFuncs* mNPNIface;
nsNPAPIPlugin* mPlugin;
ScopedMethodFactory<PluginModuleParent> mTaskFactory;
nsString mPluginDumpID;
nsString mBrowserDumpID;
nsString mHangID;
nsRefPtr<nsIObserver> mProfilerObserver;
+ TimeDuration mTimeBlocked;
nsCString mPluginName;
nsCString mPluginVersion;
#ifdef MOZ_X11
// Dup of plugin's X socket, used to scope its resources to this
// object instead of the plugin process's lifetime
ScopedClose mPluginXSocketFdDup;
#endif
@@ -269,16 +275,18 @@ class PluginModuleContentParent : public
base::ProcessId aOtherProcess);
private:
explicit PluginModuleContentParent();
#ifdef MOZ_CRASHREPORTER_INJECTOR
void OnCrash(DWORD processID) MOZ_OVERRIDE {}
#endif
+
+ static PluginModuleContentParent* sSavedModuleParent;
};
class PluginModuleChromeParent
: public PluginModuleParent
, public mozilla::HangMonitor::Annotator
{
public:
/**
--- a/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
+++ b/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
@@ -55,22 +55,26 @@ mozilla::plugins::ConvertToVariant(const
case Variant::Tdouble: {
DOUBLE_TO_NPVARIANT(aRemoteVariant.get_double(), aVariant);
break;
}
case Variant::TnsCString: {
const nsCString& string = aRemoteVariant.get_nsCString();
- NPUTF8* buffer = reinterpret_cast<NPUTF8*>(strdup(string.get()));
+ const size_t length = string.Length();
+ NPUTF8* buffer = static_cast<NPUTF8*>(::malloc(sizeof(NPUTF8) * (length + 1)));
if (!buffer) {
NS_ERROR("Out of memory!");
return false;
}
- STRINGN_TO_NPVARIANT(buffer, string.Length(), aVariant);
+
+ std::copy(string.get(), string.get() + length, buffer);
+ buffer[length] = '\0';
+ STRINGN_TO_NPVARIANT(buffer, length, aVariant);
break;
}
case Variant::TPPluginScriptableObjectParent: {
NS_ASSERTION(aInstance, "Must have an instance!");
NPObject* object = NPObjectFromVariant(aRemoteVariant);
if (!object) {
NS_ERROR("Er, this shouldn't fail!");
--- a/dom/plugins/ipc/PluginUtilsOSX.mm
+++ b/dom/plugins/ipc/PluginUtilsOSX.mm
@@ -215,36 +215,42 @@ NPError mozilla::plugins::PluginUtilsOSX
// Set the native cursor to the OS default (an arrow) before displaying the
// context menu. Otherwise (if the plugin has changed the cursor) it may
// stay as the plugin has set it -- which means it may be invisible. We
// need to do this because we display the context menu without making the
// plugin process the foreground process. If we did, the cursor would
// change to an arrow cursor automatically -- as it does in Chrome.
[[NSCursor arrowCursor] set];
- // Create a timer to process browser events while waiting
- // on the menu. This prevents the browser from hanging
- // during the lifetime of the menu.
- EventProcessor* eventProcessor = [[EventProcessor alloc] init];
- [eventProcessor setRemoteEvents:remoteEvent pluginModule:pluginModule];
- NSTimer *eventTimer = [NSTimer timerWithTimeInterval:EVENT_PROCESS_DELAY
- target:eventProcessor selector:@selector(onTick)
- userInfo:nil repeats:TRUE];
- // Use NSEventTrackingRunLoopMode otherwise the timer will
- // not fire during the right click menu.
- [[NSRunLoop currentRunLoop] addTimer:eventTimer
- forMode:NSEventTrackingRunLoopMode];
+ EventProcessor* eventProcessor = nullptr;
+ NSTimer *eventTimer = nullptr;
+ if (pluginModule) {
+ // Create a timer to process browser events while waiting
+ // on the menu. This prevents the browser from hanging
+ // during the lifetime of the menu.
+ eventProcessor = [[EventProcessor alloc] init];
+ [eventProcessor setRemoteEvents:remoteEvent pluginModule:pluginModule];
+ eventTimer = [NSTimer timerWithTimeInterval:EVENT_PROCESS_DELAY
+ target:eventProcessor selector:@selector(onTick)
+ userInfo:nil repeats:TRUE];
+ // Use NSEventTrackingRunLoopMode otherwise the timer will
+ // not fire during the right click menu.
+ [[NSRunLoop currentRunLoop] addTimer:eventTimer
+ forMode:NSEventTrackingRunLoopMode];
+ }
NSMenu* nsmenu = reinterpret_cast<NSMenu*>(aMenu);
NSPoint screen_point = ::NSMakePoint(aX, aY);
[nsmenu popUpMenuPositioningItem:nil atLocation:screen_point inView:nil];
- [eventTimer invalidate];
- [eventProcessor release];
+ if (pluginModule) {
+ [eventTimer invalidate];
+ [eventProcessor release];
+ }
return NPERR_NO_ERROR;
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NPERR_GENERIC_ERROR);
}
void mozilla::plugins::PluginUtilsOSX::InvokeNativeEventLoop()
{
--- a/dom/plugins/test/mochitest/cocoa_focus.html
+++ b/dom/plugins/test/mochitest/cocoa_focus.html
@@ -54,64 +54,64 @@
}
is(initialStateUnknown, true, "Initial state should be unknown, assumed false.");
// Give the plugin focus (the window is already focused).
utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseDown, 0, plugin1);
utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseUp, 0, plugin1);
expectedEventCount++;
- is(plugin1.getFocusState(), true, "Plugin should have focus.");
+ is(plugin1.getFocusState(), true, "(1) Plugin should have focus.");
is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
// Make sure window activation state changes don't spontaneously
// change plugin focus.
// Blur the window.
window.blur();
- is(plugin1.getFocusState(), true, "Plugin should still have focus.");
+ is(plugin1.getFocusState(), true, "(2) Plugin should still have focus.");
is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
// Focus the window.
window.focus();
- is(plugin1.getFocusState(), true, "Plugin should still have focus.");
+ is(plugin1.getFocusState(), true, "(3) Plugin should still have focus.");
is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
// Take focus from the plugin.
utils.sendNativeMouseEvent(plugin2X, plugin2Y, NSLeftMouseDown, 0, plugin2);
utils.sendNativeMouseEvent(plugin2X, plugin2Y, NSLeftMouseUp, 0, plugin2);
expectedEventCount++;
- is(plugin1.getFocusState(), false, "Plugin should not have focus.");
+ is(plugin1.getFocusState(), false, "(4) Plugin should not have focus.");
is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
// Make sure window activation causes the plugin to be informed of focus
// changes that took place while the window was inactive.
// Give the plugin focus (the window is already focused).
utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseDown, 0, plugin1);
utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseUp, 0, plugin1);
expectedEventCount++;
// Blur the window.
window.blur();
// Take focus from the plugin while the window is blurred.
plugin2.focus();
- is(plugin1.getFocusState(), true, "Plugin should still have focus.");
+ is(plugin1.getFocusState(), true, "(5) Plugin should still have focus.");
is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
// Focus the window.
window.focus();
expectedEventCount++;
- is(plugin1.getFocusState(), false, "Plugin should not have focus.");
+ is(plugin1.getFocusState(), false, "(6) Plugin should not have focus.");
is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
window.opener.testsFinished();
}
// Onload hander doesn't work for these tests -- no events arrive at the plugin.
window.opener.SimpleTest.waitForFocus(runTests, window);
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -105,15 +105,16 @@ skip-if = true # disabled due to oddness
[test_propertyAndMethod.html]
[test_queryContentsScaleFactor.html]
skip-if = toolkit != "cocoa"
[test_redirect_handling.html]
[test_secondPlugin.html]
[test_src_url_change.html]
[test_streamNotify.html]
skip-if = e10s
+[test_stringHandling.html]
[test_streamatclose.html]
[test_twostreams.html]
[test_windowed_invalidate.html]
skip-if = os != "win"
[test_visibility.html]
skip-if = toolkit == "cocoa"
[test_zero_opacity.html]
--- a/dom/plugins/test/mochitest/test_convertpoint.xul
+++ b/dom/plugins/test/mochitest/test_convertpoint.xul
@@ -24,21 +24,20 @@ function runTests() {
// the plugin hasn't been placed yet.
if (pluginElement.convertPointX(1, 0, 0, 2) == 0) {
setTimeout(runTests, 0);
return;
}
var domWindowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
- var devPxPerCSSPx = domWindowUtils.screenPixelsPerCSSPixel;
var pluginRect = pluginElement.getBoundingClientRect();
- var pluginX = (pluginRect.left * devPxPerCSSPx) + ((window.mozInnerScreenX * devPxPerCSSPx) - window.screenX);
- var pluginY = (pluginRect.top * devPxPerCSSPx) + ((window.mozInnerScreenY * devPxPerCSSPx) - window.screenY);
+ var pluginX = pluginRect.left + (window.mozInnerScreenX - window.screenX);
+ var pluginY = pluginRect.top + (window.mozInnerScreenY - window.screenY);
var windowX = window.screenX;
var windowY = window.screenY;
var windowHeight = window.outerHeight;
var screenHeight = window.screen.height;
// arbitrary coordinates of test point in plugin top-left origin terms
new file mode 100644
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_stringHandling.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+ <title>NPAPI string test</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="utils.js"></script>
+
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function runTests() {
+ try {
+ var plugin = document.getElementById("plugin1");
+ var badData = 'foo ' + '\x00'.repeat(260000);
+ var ret = plugin.echoString(badData);
+ ok(true, "Did not crash.");
+ is(ret, badData, "Returned string should equal what we passed in.");
+ } catch (e) {
+ ok(false, "Failed to call plugin.echoString() properly.");
+ } finally {
+ SimpleTest.finish();
+ }
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+</body>
+</html>
--- a/dom/plugins/test/testplugin/nptest.cpp
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -166,16 +166,17 @@ static bool isVisible(NPObject* npobj, c
static bool getWindowPosition(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool constructObject(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool setSitesWithData(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool setSitesWithDataCapabilities(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool getLastKeyText(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool getNPNVdocumentOrigin(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool getMouseUpEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool queryContentsScaleFactor(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool echoString(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static const NPUTF8* sPluginMethodIdentifierNames[] = {
"npnEvaluateTest",
"npnInvokeTest",
"npnInvokeDefaultTest",
"setUndefinedValueTest",
"identifierToStringTest",
"timerTest",
@@ -230,17 +231,18 @@ static const NPUTF8* sPluginMethodIdenti
"isVisible",
"getWindowPosition",
"constructObject",
"setSitesWithData",
"setSitesWithDataCapabilities",
"getLastKeyText",
"getNPNVdocumentOrigin",
"getMouseUpEventCount",
- "queryContentsScaleFactor"
+ "queryContentsScaleFactor",
+ "echoString",
};
static NPIdentifier sPluginMethodIdentifiers[ARRAY_LENGTH(sPluginMethodIdentifierNames)];
static const ScriptableFunction sPluginMethodFunctions[] = {
npnEvaluateTest,
npnInvokeTest,
npnInvokeDefaultTest,
setUndefinedValueTest,
identifierToStringTest,
@@ -296,17 +298,18 @@ static const ScriptableFunction sPluginM
isVisible,
getWindowPosition,
constructObject,
setSitesWithData,
setSitesWithDataCapabilities,
getLastKeyText,
getNPNVdocumentOrigin,
getMouseUpEventCount,
- queryContentsScaleFactor
+ queryContentsScaleFactor,
+ echoString,
};
STATIC_ASSERT(ARRAY_LENGTH(sPluginMethodIdentifierNames) ==
ARRAY_LENGTH(sPluginMethodFunctions));
static const NPUTF8* sPluginPropertyIdentifierNames[] = {
"propertyAndMethod"
};
@@ -3389,17 +3392,17 @@ getTopLevelWindowActivationEventCount(NP
InstanceData* id = static_cast<InstanceData*>(npp->pdata);
INT32_TO_NPVARIANT(id->topLevelWindowActivationEventCount, *result);
return true;
}
// Returns top-level window activation state as indicated by Cocoa NPAPI's
-// NPCocoaEventWindowFocusChanged events - 'true' if active, 'false' if not.
+// NPCocoaEventFocusChanged events - 'true' if active, 'false' if not.
// Throws an exception if no events have been received and thus this state
// is unknown.
bool
getFocusState(NPObject* npobj, const NPVariant* args, uint32_t argCount,
NPVariant* result)
{
if (argCount != 0)
return false;
@@ -3679,8 +3682,31 @@ bool queryContentsScaleFactor(NPObject*
NPNVcontentsScaleFactor, &scaleFactor);
if (err != NPERR_NO_ERROR) {
return false;
}
#endif
DOUBLE_TO_NPVARIANT(scaleFactor, *result);
return true;
}
+
+bool echoString(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 1) {
+ return false;
+ }
+
+ if (!NPVARIANT_IS_STRING(args[0])) {
+ return false;
+ }
+
+ const NPString& arg = NPVARIANT_TO_STRING(args[0]);
+ NPUTF8* buffer = static_cast<NPUTF8*>(NPN_MemAlloc(sizeof(NPUTF8) * arg.UTF8Length));
+ if (!buffer) {
+ return false;
+ }
+
+ std::copy(arg.UTF8Characters, arg.UTF8Characters + arg.UTF8Length, buffer);
+ STRINGN_TO_NPVARIANT(buffer, arg.UTF8Length, *result);
+
+ return true;
+}
+
--- a/dom/plugins/test/testplugin/nptest_macosx.mm
+++ b/dom/plugins/test/testplugin/nptest_macosx.mm
@@ -233,16 +233,17 @@ pluginHandleEvent(InstanceData* instance
switch (cocoaEvent->type) {
case NPCocoaEventDrawRect:
pluginDraw(instanceData, cocoaEvent);
break;
case NPCocoaEventMouseDown:
case NPCocoaEventMouseUp:
case NPCocoaEventMouseMoved:
+ case NPCocoaEventMouseDragged:
instanceData->lastMouseX = (int32_t)cocoaEvent->data.mouse.pluginX;
instanceData->lastMouseY = (int32_t)cocoaEvent->data.mouse.pluginY;
if (cocoaEvent->type == NPCocoaEventMouseUp) {
instanceData->mouseUpEventCount++;
}
break;
case NPCocoaEventWindowFocusChanged:
instanceData->topLevelWindowActivationState = cocoaEvent->data.focus.hasFocus ?
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/QuotaManager.cpp
@@ -4686,24 +4686,20 @@ StorageDirectoryHelper::CreateOrUpgradeM
if (mPersistent) {
rv = MaybeUpgradeOriginDirectory(originDir);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
if (leafName.EqualsLiteral(kChromeOrigin)) {
- if (mPersistent) {
- OriginProps* originProps = mOriginProps.AppendElement();
- originProps->mDirectory = originDir;
- originProps->mSpec = kChromeOrigin;
- originProps->mType = OriginProps::eChrome;
- } else {
- NS_WARNING("chrome in temporary storage directory?!");
- }
+ OriginProps* originProps = mOriginProps.AppendElement();
+ originProps->mDirectory = originDir;
+ originProps->mSpec = kChromeOrigin;
+ originProps->mType = OriginProps::eChrome;
} else {
nsCString spec;
uint32_t appId;
bool inMozBrowser;
if (NS_WARN_IF(!OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName),
&appId, &inMozBrowser, spec))) {
return NS_ERROR_FAILURE;
}
@@ -4830,18 +4826,16 @@ StorageDirectoryHelper::RunOnMainThread(
for (uint32_t count = mOriginProps.Length(), index = 0;
index < count;
index++) {
OriginProps& originProps = mOriginProps[index];
switch (originProps.mType) {
case OriginProps::eChrome: {
- MOZ_ASSERT(mPersistent);
-
QuotaManager::GetInfoForChrome(&originProps.mGroup,
&originProps.mOrigin,
&originProps.mIsApp,
nullptr);
break;
}
case OriginProps::eContent: {
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -130,16 +130,23 @@ nsCSPContext::ShouldLoad(nsContentPolicy
if (isCached && cacheKey.Length() > 0) {
// this is cached, use the cached value.
return NS_OK;
}
// Default decision, CSP can revise it if there's a policy to enforce
*outDecision = nsIContentPolicy::ACCEPT;
+ // If the content type doesn't map to a CSP directive, there's nothing for
+ // CSP to do.
+ CSPDirective dir = CSP_ContentTypeToDirective(aContentType);
+ if (dir == nsIContentSecurityPolicy::NO_DIRECTIVE) {
+ return NS_OK;
+ }
+
// This may be a load or a preload. If it is a preload, the document will
// not have been fully parsed yet, and aRequestContext will be an
// nsIDOMHTMLDocument rather than the nsIDOMHTMLElement associated with the
// resource. As a result, we cannot extract the element's corresponding
// nonce attribute, and so we cannot correctly check the nonce on a preload.
//
// Therefore, the decision returned here for a preload may be *incorrect* as
// it cannot take the nonce into account. We will still check the load, but
@@ -160,63 +167,106 @@ nsCSPContext::ShouldLoad(nsContentPolicy
if (!isPreload) {
nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(aRequestContext);
if (htmlElement) {
rv = htmlElement->GetAttribute(NS_LITERAL_STRING("nonce"), nonce);
NS_ENSURE_SUCCESS(rv, rv);
}
}
- nsAutoString violatedDirective;
- for (uint32_t p = 0; p < mPolicies.Length(); p++) {
- if (!mPolicies[p]->permits(aContentType,
- aContentLocation,
- nonce,
- // aExtra is only non-null if
- // the channel got redirected.
- (aExtra != nullptr),
- violatedDirective)) {
- // If the policy is violated and not report-only, reject the load and
- // report to the console
- if (!mPolicies[p]->getReportOnlyFlag()) {
- CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, nsIContentPolicy::REJECT_SERVER"));
- *outDecision = nsIContentPolicy::REJECT_SERVER;
- }
+ // aExtra is only non-null if the channel got redirected.
+ bool wasRedirected = (aExtra != nullptr);
+ nsCOMPtr<nsIURI> originalURI = do_QueryInterface(aExtra);
- // Do not send a report or notify observers if this is a preload - the
- // decision may be wrong due to the inability to get the nonce, and will
- // incorrectly fail the unit tests.
- if (!isPreload) {
- nsCOMPtr<nsIURI> originalURI = do_QueryInterface(aExtra);
- this->AsyncReportViolation(aContentLocation,
- originalURI, /* in case of redirect originalURI is not null */
- violatedDirective,
- p, /* policy index */
- EmptyString(), /* no observer subject */
- EmptyString(), /* no source file */
- EmptyString(), /* no script sample */
- 0); /* no line number */
- }
- }
- }
+ bool permitted = permitsInternal(dir,
+ aContentLocation,
+ originalURI,
+ nonce,
+ wasRedirected,
+ isPreload,
+ false, // allow fallback to default-src
+ true, // send violation reports
+ true); // send blocked URI in violation reports
+
+ *outDecision = permitted ? nsIContentPolicy::ACCEPT
+ : nsIContentPolicy::REJECT_SERVER;
+
// Done looping, cache any relevant result
if (cacheKey.Length() > 0 && !isPreload) {
mShouldLoadCache.Put(cacheKey, *outDecision);
}
#ifdef PR_LOGGING
{
nsAutoCString spec;
aContentLocation->GetSpec(spec);
CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, decision: %s, aContentLocation: %s", *outDecision ? "load" : "deny", spec.get()));
}
#endif
return NS_OK;
}
+bool
+nsCSPContext::permitsInternal(CSPDirective aDir,
+ nsIURI* aContentLocation,
+ nsIURI* aOriginalURI,
+ const nsAString& aNonce,
+ bool aWasRedirected,
+ bool aIsPreload,
+ bool aSpecific,
+ bool aSendViolationReports,
+ bool aSendContentLocationInViolationReports)
+{
+ bool permits = true;
+
+ nsAutoString violatedDirective;
+ for (uint32_t p = 0; p < mPolicies.Length(); p++) {
+
+ // According to the W3C CSP spec, frame-ancestors checks are ignored for
+ // report-only policies (when "monitoring").
+ if (aDir == nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE &&
+ mPolicies[p]->getReportOnlyFlag()) {
+ continue;
+ }
+
+ if (!mPolicies[p]->permits(aDir,
+ aContentLocation,
+ aNonce,
+ aWasRedirected,
+ aSpecific,
+ violatedDirective)) {
+ // If the policy is violated and not report-only, reject the load and
+ // report to the console
+ if (!mPolicies[p]->getReportOnlyFlag()) {
+ CSPCONTEXTLOG(("nsCSPContext::permitsInternal, false"));
+ permits = false;
+ }
+
+ // Do not send a report or notify observers if this is a preload - the
+ // decision may be wrong due to the inability to get the nonce, and will
+ // incorrectly fail the unit tests.
+ if (!aIsPreload && aSendViolationReports) {
+ this->AsyncReportViolation((aSendContentLocationInViolationReports ?
+ aContentLocation : nullptr),
+ aOriginalURI, /* in case of redirect originalURI is not null */
+ violatedDirective,
+ p, /* policy index */
+ EmptyString(), /* no observer subject */
+ EmptyString(), /* no source file */
+ EmptyString(), /* no script sample */
+ 0); /* no line number */
+ }
+ }
+ }
+
+ return permits;
+}
+
+
+
/* ===== nsISupports implementation ========== */
NS_IMPL_CLASSINFO(nsCSPContext,
nullptr,
nsIClassInfo::MAIN_THREAD_ONLY,
NS_CSPCONTEXT_CID)
NS_IMPL_ISUPPORTS_CI(nsCSPContext,
@@ -1036,138 +1086,76 @@ nsCSPContext::PermitsAncestry(nsIDocShel
// next ancestor
treeItem = parentTreeItem;
}
nsAutoString violatedDirective;
// Now that we've got the ancestry chain in ancestorsArray, time to check
// them against any CSP.
- for (uint32_t i = 0; i < mPolicies.Length(); i++) {
-
- // According to the W3C CSP spec, frame-ancestors checks are ignored for
- // report-only policies (when "monitoring").
- if (mPolicies[i]->getReportOnlyFlag()) {
- continue;
- }
+ // NOTE: the ancestors are not allowed to be sent cross origin; this is a
+ // restriction not placed on subresource loads.
- for (uint32_t a = 0; a < ancestorsArray.Length(); a++) {
- // TODO(sid) the mapping from frame-ancestors context to TYPE_DOCUMENT is
- // forced. while this works for now, we will implement something in
- // bug 999656.
+ for (uint32_t a = 0; a < ancestorsArray.Length(); a++) {
#ifdef PR_LOGGING
- {
- nsAutoCString spec;
- ancestorsArray[a]->GetSpec(spec);
- CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, checking ancestor: %s", spec.get()));
- }
+ {
+ nsAutoCString spec;
+ ancestorsArray[a]->GetSpec(spec);
+ CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, checking ancestor: %s", spec.get()));
+ }
#endif
- if (!mPolicies[i]->permits(nsIContentPolicy::TYPE_DOCUMENT,
- ancestorsArray[a],
- EmptyString(), // no nonce
- false, // no redirect
- violatedDirective)) {
- // Policy is violated
- // Send reports, but omit the ancestor URI if cross-origin as per spec
- // (it is a violation of the same-origin policy).
- bool okToSendAncestor = NS_SecurityCompareURIs(ancestorsArray[a], mSelfURI, true);
+ // omit the ancestor URI in violation reports if cross-origin as per spec
+ // (it is a violation of the same-origin policy).
+ bool okToSendAncestor = NS_SecurityCompareURIs(ancestorsArray[a], mSelfURI, true);
+
- this->AsyncReportViolation((okToSendAncestor ? ancestorsArray[a] : nullptr),
- nullptr, /* originalURI in case of redirect */
- violatedDirective,
- i, /* policy index */
- EmptyString(), /* no observer subject */
- EmptyString(), /* no source file */
- EmptyString(), /* no script sample */
- 0); /* no line number */
- *outPermitsAncestry = false;
- }
+ bool permits = permitsInternal(nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE,
+ ancestorsArray[a],
+ nullptr, // no redirect here.
+ EmptyString(), // no nonce
+ false, // no redirect here.
+ false, // not a preload.
+ true, // specific, do not use default-src
+ true, // send violation reports
+ okToSendAncestor);
+ if (!permits) {
+ *outPermitsAncestry = false;
}
}
return NS_OK;
}
NS_IMETHODIMP
-nsCSPContext::PermitsBaseURI(nsIURI* aURI, bool* outPermitsBaseURI)
+nsCSPContext::Permits(nsIURI* aURI,
+ CSPDirective aDir,
+ bool aSpecific,
+ bool* outPermits)
{
// Can't perform check without aURI
if (aURI == nullptr) {
return NS_ERROR_FAILURE;
}
- *outPermitsBaseURI = true;
-
- for (uint32_t i = 0; i < mPolicies.Length(); i++) {
- if (!mPolicies[i]->permitsBaseURI(aURI)) {
- // policy is violated, report to caller if not report-only
- if (!mPolicies[i]->getReportOnlyFlag()) {
- *outPermitsBaseURI = false;
- }
- nsAutoString violatedDirective;
- mPolicies[i]->getDirectiveAsString(CSP_BASE_URI, violatedDirective);
- this->AsyncReportViolation(aURI,
- nullptr, /* originalURI in case of redirect */
- violatedDirective,
- i, /* policy index */
- EmptyString(), /* no observer subject */
- EmptyString(), /* no source file */
- EmptyString(), /* no script sample */
- 0); /* no line number */
- }
- }
+ *outPermits = permitsInternal(aDir,
+ aURI,
+ nullptr, // no original (pre-redirect) URI
+ EmptyString(), // no nonce
+ false, // not redirected.
+ false, // not a preload.
+ aSpecific,
+ true, // send violation reports
+ true); // send blocked URI in violation reports
#ifdef PR_LOGGING
{
nsAutoCString spec;
aURI->GetSpec(spec);
- CSPCONTEXTLOG(("nsCSPContext::PermitsBaseURI, aUri: %s, isAllowed: %s",
- spec.get(),
- *outPermitsBaseURI ? "allow" : "deny"));
- }
-#endif
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsCSPContext::PermitsFormAction(nsIURI* aURI, bool* outPermitsFormAction)
-{
- // Can't perform check without aURI
- if (!aURI) {
- return NS_ERROR_FAILURE;
- }
-
- *outPermitsFormAction = true;
-
- for (uint32_t i = 0; i < mPolicies.Length(); i++) {
- if (!mPolicies[i]->permitsFormAction(aURI)) {
- // policy is violated, report to caller if not report-only
- if (!mPolicies[i]->getReportOnlyFlag()) {
- *outPermitsFormAction = false;
- }
- nsAutoString violatedDirective;
- mPolicies[i]->getDirectiveAsString(CSP_FORM_ACTION, violatedDirective);
- this->AsyncReportViolation(aURI,
- mSelfURI,
- violatedDirective,
- i, /* policy index */
- EmptyString(), /* no observer subject */
- EmptyString(), /* no source file */
- EmptyString(), /* no script sample */
- 0); /* no line number */
- }
- }
-
-#ifdef PR_LOGGING
- {
- nsAutoCString spec;
- aURI->GetSpec(spec);
- CSPCONTEXTLOG(("nsCSPContext::PermitsFormAction, aUri: %s, isAllowed: %s",
- spec.get(),
- *outPermitsFormAction ? "allow" : "deny"));
+ CSPCONTEXTLOG(("nsCSPContext::Permits, aUri: %s, aDir: %d, isAllowed: %s",
+ spec.get(), aDir,
+ *outPermits ? "allow" : "deny"));
}
#endif
return NS_OK;
}
/* ========== CSPViolationReportListener implementation ========== */
--- a/dom/security/nsCSPContext.h
+++ b/dom/security/nsCSPContext.h
@@ -56,16 +56,26 @@ class nsCSPContext : public nsIContentSe
private:
NS_IMETHODIMP getAllowsInternal(nsContentPolicyType aContentType,
enum CSPKeyword aKeyword,
const nsAString& aNonceOrContent,
bool* outShouldReportViolations,
bool* outIsAllowed) const;
+ bool permitsInternal(CSPDirective aDir,
+ nsIURI* aContentLocation,
+ nsIURI* aOriginalURI,
+ const nsAString& aNonce,
+ bool aWasRedirected,
+ bool aIsPreload,
+ bool aSpecific,
+ bool aSendViolationReports,
+ bool aSendContentLocationInViolationReports);
+
nsCOMPtr<nsIURI> mReferrer;
uint64_t mInnerWindowID; // used for web console logging
nsTArray<nsCSPPolicy*> mPolicies;
nsCOMPtr<nsIURI> mSelfURI;
nsDataHashtable<nsCStringHashKey, int16_t> mShouldLoadCache;
nsCOMPtr<nsILoadGroup> mCallingChannelLoadGroup;
nsWeakPtr mLoadingContext;
};
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -891,17 +891,17 @@ nsCSPParser::reportURIList(nsTArray<nsCS
void
nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs)
{
CSPPARSERLOG(("nsCSPParser::directiveValue"));
// The tokenzier already generated an array in the form of
// [ name, src, src, ... ], no need to parse again, but
// special case handling in case the directive is report-uri.
- if (CSP_IsDirective(mCurDir[0], CSP_REPORT_URI)) {
+ if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
reportURIList(outSrcs);
return;
}
// Otherwise just forward to sourceList
sourceList(outSrcs);
}
// directive-name = 1*( ALPHA / DIGIT / "-" )
@@ -919,32 +919,32 @@ nsCSPParser::directiveName()
params, ArrayLength(params));
return nullptr;
}
// The directive 'reflected-xss' is part of CSP 1.1, see:
// http://www.w3.org/TR/2014/WD-CSP11-20140211/#reflected-xss
// Currently we are not supporting that directive, hence we log a
// warning to the console and ignore the directive including its values.
- if (CSP_IsDirective(mCurToken, CSP_REFLECTED_XSS)) {
+ if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::REFLECTED_XSS_DIRECTIVE)) {
const char16_t* params[] = { mCurToken.get() };
logWarningErrorToConsole(nsIScriptError::warningFlag, "notSupportingDirective",
params, ArrayLength(params));
return nullptr;
}
// Make sure the directive does not already exist
// (see http://www.w3.org/TR/CSP11/#parsing)
- if (mPolicy->directiveExists(CSP_DirectiveToEnum(mCurToken))) {
+ if (mPolicy->hasDirective(CSP_StringToCSPDirective(mCurToken))) {
const char16_t* params[] = { mCurToken.get() };
logWarningErrorToConsole(nsIScriptError::warningFlag, "duplicateDirective",
params, ArrayLength(params));
return nullptr;
}
- return new nsCSPDirective(CSP_DirectiveToEnum(mCurToken));
+ return new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
}
// directive = *WSP [ directive-name [ WSP directive-value ] ]
void
nsCSPParser::directive()
{
// Set the directiveName to mCurToken
// Remember, the directive name is stored at index 0
@@ -1034,17 +1034,17 @@ nsCSPParser::parseContentSecurityPolicy(
nsCSPParser parser(tokens, aSelfURI, aInnerWindowID);
// Start the parser to generate a new CSPPolicy using the generated tokens.
nsCSPPolicy* policy = parser.policy();
// Check that report-only policies define a report-uri, otherwise log warning.
if (aReportOnly) {
policy->setReportOnlyFlag(true);
- if (!policy->directiveExists(CSP_REPORT_URI)) {
+ if (!policy->hasDirective(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
nsAutoCString prePath;
nsresult rv = aSelfURI->GetPrePath(prePath);
NS_ENSURE_SUCCESS(rv, policy);
NS_ConvertUTF8toUTF16 unicodePrePath(prePath);
const char16_t* params[] = { unicodePrePath.get() };
parser.logWarningErrorToConsole(nsIScriptError::warningFlag, "reportURInotInReportOnlyHeader",
params, ArrayLength(params));
}
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -122,16 +122,69 @@ CSP_LogLocalizedStr(const char16_t* aNam
nsXPIDLString logMsg;
CSP_GetLocalizedStr(aName, aParams, aLength, getter_Copies(logMsg));
CSP_LogMessage(logMsg, aSourceName, aSourceLine,
aLineNumber, aColumnNumber, aFlags,
aCategory, aInnerWindowID);
}
/* ===== Helpers ============================ */
+CSPDirective
+CSP_ContentTypeToDirective(nsContentPolicyType aType)
+{
+ switch (aType) {
+ case nsIContentPolicy::TYPE_IMAGE:
+ case nsIContentPolicy::TYPE_IMAGESET:
+ return nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE;
+
+ // BLock XSLT as script, see bug 910139
+ case nsIContentPolicy::TYPE_XSLT:
+ case nsIContentPolicy::TYPE_SCRIPT:
+ return nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE;
+
+ case nsIContentPolicy::TYPE_STYLESHEET:
+ return nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE;
+
+ case nsIContentPolicy::TYPE_FONT:
+ return nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE;
+
+ case nsIContentPolicy::TYPE_MEDIA:
+ return nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE;
+
+ // TYPE_DOCUMENT shouldn't be used since it's specifically whitelisted by
+ // the CSPService, but in case we do want to know which directive to check,
+ // FRAME_SRC is the best fit.
+ case nsIContentPolicy::TYPE_DOCUMENT:
+ case nsIContentPolicy::TYPE_SUBDOCUMENT:
+ return nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE;
+
+ case nsIContentPolicy::TYPE_WEBSOCKET:
+ case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
+ case nsIContentPolicy::TYPE_BEACON:
+ case nsIContentPolicy::TYPE_FETCH:
+ return nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE;
+
+ case nsIContentPolicy::TYPE_OBJECT:
+ case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
+ return nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE;
+
+ case nsIContentPolicy::TYPE_XBL:
+ case nsIContentPolicy::TYPE_PING:
+ case nsIContentPolicy::TYPE_DTD:
+ case nsIContentPolicy::TYPE_OTHER:
+ return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
+
+ // CSP can not block csp reports, fall through to error
+ case nsIContentPolicy::TYPE_CSP_REPORT:
+ // Fall through to error for all other directives
+ default:
+ MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective");
+ }
+ return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
+}
nsCSPHostSrc*
CSP_CreateHostSrcFromURI(nsIURI* aURI)
{
// Create the host first
nsCString host;
aURI->GetHost(host);
nsCSPHostSrc *hostsrc = new nsCSPHostSrc(NS_ConvertUTF8toUTF16(host));
@@ -150,31 +203,29 @@ CSP_CreateHostSrcFromURI(nsIURI* aURI)
hostsrc->setPort(portStr);
}
return hostsrc;
}
bool
CSP_IsValidDirective(const nsAString& aDir)
{
- static_assert(CSP_LAST_DIRECTIVE_VALUE ==
- (sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0])),
- "CSP_LAST_DIRECTIVE_VALUE does not match length of CSPStrDirectives");
+ uint32_t numDirs = (sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]));
- for (uint32_t i = 0; i < CSP_LAST_DIRECTIVE_VALUE; i++) {
+ for (uint32_t i = 0; i < numDirs; i++) {
if (aDir.LowerCaseEqualsASCII(CSPStrDirectives[i])) {
return true;
}
}
return false;
}
bool
-CSP_IsDirective(const nsAString& aValue, enum CSPDirective aDir)
+CSP_IsDirective(const nsAString& aValue, CSPDirective aDir)
{
- return aValue.LowerCaseEqualsASCII(CSP_EnumToDirective(aDir));
+ return aValue.LowerCaseEqualsASCII(CSP_CSPDirectiveToString(aDir));
}
bool
CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey)
{
return aValue.LowerCaseEqualsASCII(CSP_EnumToKeyword(aKey));
}
@@ -465,17 +516,17 @@ void
nsCSPHostSrc::appendPath(const nsAString& aPath)
{
mPath.Append(aPath);
ToLowerCase(mPath);
}
/* ===== nsCSPKeywordSrc ===================== */
-nsCSPKeywordSrc::nsCSPKeywordSrc(CSPKeyword aKeyword)
+nsCSPKeywordSrc::nsCSPKeywordSrc(enum CSPKeyword aKeyword)
{
NS_ASSERTION((aKeyword != CSP_SELF),
"'self' should have been replaced in the parser");
mKeyword = aKeyword;
}
nsCSPKeywordSrc::~nsCSPKeywordSrc()
{
@@ -619,17 +670,17 @@ nsCSPReportURI::toString(nsAString& outS
if (NS_FAILED(rv)) {
return;
}
outStr.AppendASCII(spec.get());
}
/* ===== nsCSPDirective ====================== */
-nsCSPDirective::nsCSPDirective(enum CSPDirective aDirective)
+nsCSPDirective::nsCSPDirective(CSPDirective aDirective)
{
mDirective = aDirective;
}
nsCSPDirective::~nsCSPDirective()
{
for (uint32_t i = 0; i < mSrcs.Length(); i++) {
delete mSrcs[i];
@@ -675,99 +726,43 @@ nsCSPDirective::allows(enum CSPKeyword a
}
return false;
}
void
nsCSPDirective::toString(nsAString& outStr) const
{
// Append directive name
- outStr.AppendASCII(CSP_EnumToDirective(mDirective));
+ outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective));
outStr.AppendASCII(" ");
// Append srcs
uint32_t length = mSrcs.Length();
for (uint32_t i = 0; i < length; i++) {
mSrcs[i]->toString(outStr);
if (i != (length - 1)) {
outStr.AppendASCII(" ");
}
}
}
-enum CSPDirective
-CSP_ContentTypeToDirective(nsContentPolicyType aType)
-{
- switch (aType) {
- case nsIContentPolicy::TYPE_IMAGE:
- case nsIContentPolicy::TYPE_IMAGESET:
- return CSP_IMG_SRC;
-
- case nsIContentPolicy::TYPE_SCRIPT:
- return CSP_SCRIPT_SRC;
-
- case nsIContentPolicy::TYPE_STYLESHEET:
- return CSP_STYLE_SRC;
-
- case nsIContentPolicy::TYPE_FONT:
- return CSP_FONT_SRC;
-
- case nsIContentPolicy::TYPE_MEDIA:
- return CSP_MEDIA_SRC;
-
- case nsIContentPolicy::TYPE_SUBDOCUMENT:
- return CSP_FRAME_SRC;
-
- // BLock XSLT as script, see bug 910139
- case nsIContentPolicy::TYPE_XSLT:
- return CSP_SCRIPT_SRC;
-
- // TODO(sid): fix this mapping to be more precise (bug 999656)
- case nsIContentPolicy::TYPE_DOCUMENT:
- return CSP_FRAME_ANCESTORS;
-
- case nsIContentPolicy::TYPE_WEBSOCKET:
- case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
- case nsIContentPolicy::TYPE_BEACON:
- case nsIContentPolicy::TYPE_FETCH:
- return CSP_CONNECT_SRC;
-
- case nsIContentPolicy::TYPE_OBJECT:
- case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
- return CSP_OBJECT_SRC;
-
- case nsIContentPolicy::TYPE_XBL:
- case nsIContentPolicy::TYPE_PING:
- case nsIContentPolicy::TYPE_DTD:
- case nsIContentPolicy::TYPE_OTHER:
- return CSP_DEFAULT_SRC;
-
- // CSP can not block csp reports, fall through to error
- case nsIContentPolicy::TYPE_CSP_REPORT:
- // Fall through to error for all other directives
- default:
- NS_ASSERTION(false, "Can not map nsContentPolicyType to CSPDirective");
- }
- return CSP_DEFAULT_SRC;
-}
-
bool
nsCSPDirective::restrictsContentType(nsContentPolicyType aContentType) const
{
// make sure we do not check for the default src before any other sources
if (isDefaultDirective()) {
return false;
}
return mDirective == CSP_ContentTypeToDirective(aContentType);
}
void
nsCSPDirective::getReportURIs(nsTArray<nsString> &outReportURIs) const
{
- NS_ASSERTION((mDirective == CSP_REPORT_URI), "not a report-uri directive");
+ NS_ASSERTION((mDirective == nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE), "not a report-uri directive");
// append uris
nsString tmpReportURI;
for (uint32_t i = 0; i < mSrcs.Length(); i++) {
tmpReportURI.Truncate();
mSrcs[i]->toString(tmpReportURI);
outReportURIs.AppendElement(tmpReportURI);
}
@@ -786,118 +781,72 @@ nsCSPPolicy::~nsCSPPolicy()
CSPUTILSLOG(("nsCSPPolicy::~nsCSPPolicy"));
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
delete mDirectives[i];
}
}
bool
-nsCSPPolicy::permits(nsContentPolicyType aContentType,
+nsCSPPolicy::permits(CSPDirective aDir,
+ nsIURI* aUri,
+ bool aSpecific) const
+{
+ nsString outp;
+ return this->permits(aDir, aUri, EmptyString(), false, aSpecific, outp);
+}
+
+bool
+nsCSPPolicy::permits(CSPDirective aDir,
nsIURI* aUri,
const nsAString& aNonce,
bool aWasRedirected,
+ bool aSpecific,
nsAString& outViolatedDirective) const
{
#ifdef PR_LOGGING
{
nsAutoCString spec;
aUri->GetSpec(spec);
- CSPUTILSLOG(("nsCSPPolicy::permits, aContentType: %d, aUri: %s, aNonce: %s",
- aContentType, spec.get(), NS_ConvertUTF16toUTF8(aNonce).get()));
+ CSPUTILSLOG(("nsCSPPolicy::permits, aUri: %s, aDir: %d, aSpecific: %s",
+ spec.get(), aDir, aSpecific ? "true" : "false"));
}
#endif
NS_ASSERTION(aUri, "permits needs an uri to perform the check!");
nsCSPDirective* defaultDir = nullptr;
+ // Try to find a relevant directive
// These directive arrays are short (1-5 elements), not worth using a hashtable.
-
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
- // Check if the directive name matches
- if (mDirectives[i]->restrictsContentType(aContentType)) {
+ if (mDirectives[i]->equals(aDir)) {
if (!mDirectives[i]->permits(aUri, aNonce, aWasRedirected)) {
mDirectives[i]->toString(outViolatedDirective);
return false;
}
return true;
}
if (mDirectives[i]->isDefaultDirective()) {
defaultDir = mDirectives[i];
}
}
- // If [frame-ancestors] is not listed explicitly then default to true
- // without consulting [default-src]
- // TODO: currently [frame-ancestors] is mapped to TYPE_DOCUMENT (needs to be fixed)
- if (aContentType == nsIContentPolicy::TYPE_DOCUMENT) {
- return true;
- }
-
// If the above loop runs through, we haven't found a matching directive.
// Avoid relooping, just store the result of default-src while looping.
- if (defaultDir) {
+ if (!aSpecific && defaultDir) {
if (!defaultDir->permits(aUri, aNonce, aWasRedirected)) {
defaultDir->toString(outViolatedDirective);
return false;
}
return true;
}
- // unspecified default-src should default to no restrictions
- // see bug 764937
- return true;
-}
-
-bool
-nsCSPPolicy::permitsBaseURI(nsIURI* aUri) const
-{
-#ifdef PR_LOGGING
- {
- nsAutoCString spec;
- aUri->GetSpec(spec);
- CSPUTILSLOG(("nsCSPPolicy::permitsBaseURI, aUri: %s", spec.get()));
- }
-#endif
-
- // Try to find a base-uri directive
- for (uint32_t i = 0; i < mDirectives.Length(); i++) {
- if (mDirectives[i]->equals(CSP_BASE_URI)) {
- return mDirectives[i]->permits(aUri);
- }
- }
-
- // base-uri is only enforced if explicitly defined in the
- // policy - do *not* consult default-src, see:
- // http://www.w3.org/TR/CSP11/#directive-default-src
- return true;
-}
-
-bool
-nsCSPPolicy::permitsFormAction(nsIURI* aUri) const
-{
-#ifdef PR_LOGGING
- {
- nsAutoCString spec;
- aUri->GetSpec(spec);
- CSPUTILSLOG(("nsCSPPolicy::permitsFormAction, aUri: %s", spec.get()));
- }
-#endif
-
- // Try to find a form-action directive
- for (uint32_t i = 0; i < mDirectives.Length(); i++) {
- if (mDirectives[i]->equals(CSP_FORM_ACTION)) {
- return mDirectives[i]->permits(aUri);
- }
- }
-
- // form-action is only enforced if explicitly defined in the
- // policy - do *not* consult default-src, see:
- // http://www.w3.org/TR/CSP2/#directive-default-src
+ // Nothing restricts this, so we're allowing the load
+ // See bug 764937
return true;
}
bool
nsCSPPolicy::allows(nsContentPolicyType aContentType,
enum CSPKeyword aKeyword,
const nsAString& aHashOrNonce) const
{
@@ -953,17 +902,17 @@ nsCSPPolicy::toString(nsAString& outStr)
mDirectives[i]->toString(outStr);
if (i != (length - 1)) {
outStr.AppendASCII("; ");
}
}
}
bool
-nsCSPPolicy::directiveExists(enum CSPDirective aDir) const
+nsCSPPolicy::hasDirective(CSPDirective aDir) const
{
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
if (mDirectives[i]->equals(aDir)) {
return true;
}
}
return false;
}
@@ -994,28 +943,28 @@ nsCSPPolicy::getDirectiveStringForConten
defaultDir->toString(outDirective);
return;
}
NS_ASSERTION(false, "Can not query directive string for contentType!");
outDirective.AppendASCII("couldNotQueryViolatedDirective");
}
void
-nsCSPPolicy::getDirectiveAsString(enum CSPDirective aDir, nsAString& outDirective) const
+nsCSPPolicy::getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const
{
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
if (mDirectives[i]->equals(aDir)) {
mDirectives[i]->toString(outDirective);
return;
}
}
}
void
nsCSPPolicy::getReportURIs(nsTArray<nsString>& outReportURIs) const
{
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
- if (mDirectives[i]->equals(CSP_REPORT_URI)) {
+ if (mDirectives[i]->equals(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
mDirectives[i]->getReportURIs(outReportURIs);
return;
}
}
}
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -3,16 +3,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsCSPUtils_h___
#define nsCSPUtils_h___
#include "nsCOMPtr.h"
#include "nsIContentPolicy.h"
+#include "nsIContentSecurityPolicy.h"
#include "nsIURI.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsUnicharUtils.h"
#include "prlog.h"
/* =============== Logging =================== */
@@ -49,82 +50,55 @@ void CSP_LogMessage(const nsAString& aMe
#define INLINE_STYLE_VIOLATION_OBSERVER_TOPIC "violated base restriction: Inline Stylesheets will not apply"
#define INLINE_SCRIPT_VIOLATION_OBSERVER_TOPIC "violated base restriction: Inline Scripts will not execute"
#define EVAL_VIOLATION_OBSERVER_TOPIC "violated base restriction: Code will not be created from strings"
#define SCRIPT_NONCE_VIOLATION_OBSERVER_TOPIC "Inline Script had invalid nonce"
#define STYLE_NONCE_VIOLATION_OBSERVER_TOPIC "Inline Style had invalid nonce"
#define SCRIPT_HASH_VIOLATION_OBSERVER_TOPIC "Inline Script had invalid hash"
#define STYLE_HASH_VIOLATION_OBSERVER_TOPIC "Inline Style had invalid hash"
-
-// Please add any new enum items not only to CSPDirective, but also add
-// a string version for every enum >> using the same index << to
-// CSPStrDirectives underneath.
-enum CSPDirective {
- CSP_DEFAULT_SRC = 0,
- CSP_SCRIPT_SRC,
- CSP_OBJECT_SRC,
- CSP_STYLE_SRC,
- CSP_IMG_SRC,
- CSP_MEDIA_SRC,
- CSP_FRAME_SRC,
- CSP_FONT_SRC,
- CSP_CONNECT_SRC,
- CSP_REPORT_URI,
- CSP_FRAME_ANCESTORS,
- CSP_REFLECTED_XSS,
- CSP_BASE_URI,
- CSP_FORM_ACTION,
- // CSP_LAST_DIRECTIVE_VALUE always needs to be the last element in the enum
- // because we use it to calculate the size for the char* array.
- CSP_LAST_DIRECTIVE_VALUE
+// these strings map to the CSPDirectives in nsIContentSecurityPolicy
+// NOTE: When implementing a new directive, you will need to add it here but also
+// add a corresponding entry to the constants in nsIContentSecurityPolicy.idl
+static const char* CSPStrDirectives[] = {
+ "-error-", // NO_DIRECTIVE
+ "default-src", // DEFAULT_SRC_DIRECTIVE
+ "script-src", // SCRIPT_SRC_DIRECTIVE
+ "object-src", // OBJECT_SRC_DIRECTIVE
+ "style-src", // STYLE_SRC_DIRECTIVE
+ "img-src", // IMG_SRC_DIRECTIVE
+ "media-src", // MEDIA_SRC_DIRECTIVE
+ "frame-src", // FRAME_SRC_DIRECTIVE
+ "font-src", // FONT_SRC_DIRECTIVE
+ "connect-src", // CONNECT_SRC_DIRECTIVE
+ "report-uri", // REPORT_URI_DIRECTIVE
+ "frame-ancestors", // FRAME_ANCESTORS_DIRECTIVE
+ "reflected-xss", // REFLECTED_XSS_DIRECTIVE
+ "base-uri", // BASE_URI_DIRECTIVE
+ "form-action" // FORM_ACTION_DIRECTIVE
};
-static const char* CSPStrDirectives[] = {
- "default-src", // CSP_DEFAULT_SRC = 0
- "script-src", // CSP_SCRIPT_SRC
- "object-src", // CSP_OBJECT_SRC
- "style-src", // CSP_STYLE_SRC
- "img-src", // CSP_IMG_SRC
- "media-src", // CSP_MEDIA_SRC
- "frame-src", // CSP_FRAME_SRC
- "font-src", // CSP_FONT_SRC
- "connect-src", // CSP_CONNECT_SRC
- "report-uri", // CSP_REPORT_URI
- "frame-ancestors", // CSP_FRAME_ANCESTORS
- "reflected-xss", // CSP_REFLECTED_XSS
- "base-uri", // CSP_BASE_URI
- "form-action" // CSP_FORM_ACTION
-};
-
-inline const char* CSP_EnumToDirective(enum CSPDirective aDir)
+inline const char* CSP_CSPDirectiveToString(CSPDirective aDir)
{
- // Make sure all elements in enum CSPDirective got added to CSPStrDirectives.
- static_assert((sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]) ==
- static_cast<uint32_t>(CSP_LAST_DIRECTIVE_VALUE)),
- "CSP_LAST_DIRECTIVE_VALUE does not match length of CSPStrDirectives");
return CSPStrDirectives[static_cast<uint32_t>(aDir)];
}
-inline CSPDirective CSP_DirectiveToEnum(const nsAString& aDir)
+inline CSPDirective CSP_StringToCSPDirective(const nsAString& aDir)
{
nsString lowerDir = PromiseFlatString(aDir);
ToLowerCase(lowerDir);
- static_assert(CSP_LAST_DIRECTIVE_VALUE ==
- (sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0])),
- "CSP_LAST_DIRECTIVE_VALUE does not match length of CSPStrDirectives");
-
- for (uint32_t i = 0; i < CSP_LAST_DIRECTIVE_VALUE; i++) {
+ uint32_t numDirs = (sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]));
+ for (uint32_t i = 1; i < numDirs; i++) {
if (lowerDir.EqualsASCII(CSPStrDirectives[i])) {
return static_cast<CSPDirective>(i);
}
}
- NS_ASSERTION(false, "Can not convert unknown Directive to Enum");
- return CSP_LAST_DIRECTIVE_VALUE;
+ NS_ASSERTION(false, "Can not convert unknown Directive to Integer");
+ return nsIContentSecurityPolicy::NO_DIRECTIVE;
}
// Please add any new enum items not only to CSPKeyword, but also add
// a string version for every enum >> using the same index << to
// CSPStrKeywords underneath.
enum CSPKeyword {
CSP_SELF = 0,
CSP_UNSAFE_INLINE,
@@ -177,19 +151,21 @@ inline CSPKeyword CSP_KeywordToEnum(cons
}
/* =============== Helpers ================== */
class nsCSPHostSrc;
nsCSPHostSrc* CSP_CreateHostSrcFromURI(nsIURI* aURI);
bool CSP_IsValidDirective(const nsAString& aDir);
-bool CSP_IsDirective(const nsAString& aValue, enum CSPDirective aDir);
+bool CSP_IsDirective(const nsAString& aValue, CSPDirective aDir);
bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey);
bool CSP_IsQuotelessKeyword(const nsAString& aKey);
+CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType);
+
/* =============== nsCSPSrc ================== */
class nsCSPBaseSrc {
public:
nsCSPBaseSrc();
virtual ~nsCSPBaseSrc();
@@ -291,80 +267,82 @@ class nsCSPReportURI : public nsCSPBaseS
nsCOMPtr<nsIURI> mReportURI;
};
/* =============== nsCSPDirective ============= */
class nsCSPDirective {
public:
nsCSPDirective();
- explicit nsCSPDirective(enum CSPDirective aDirective);
+ explicit nsCSPDirective(CSPDirective aDirective);
virtual ~nsCSPDirective();
bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected) const;
bool permits(nsIURI* aUri) const;
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
void toString(nsAString& outStr) const;
inline void addSrcs(const nsTArray<nsCSPBaseSrc*>& aSrcs)
{ mSrcs = aSrcs; }
bool restrictsContentType(nsContentPolicyType aContentType) const;
inline bool isDefaultDirective() const
- { return mDirective == CSP_DEFAULT_SRC; }
+ { return mDirective == nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE; }
- inline bool equals(enum CSPDirective aDirective) const
+ inline bool equals(CSPDirective aDirective) const
{ return (mDirective == aDirective); }
void getReportURIs(nsTArray<nsString> &outReportURIs) const;
private:
CSPDirective mDirective;
nsTArray<nsCSPBaseSrc*> mSrcs;
};
/* =============== nsCSPPolicy ================== */
class nsCSPPolicy {
public:
nsCSPPolicy();
virtual ~nsCSPPolicy();
- bool permits(nsContentPolicyType aContentType,
+ bool permits(CSPDirective aDirective,
nsIURI* aUri,
const nsAString& aNonce,
bool aWasRedirected,
+ bool aSpecific,
nsAString& outViolatedDirective) const;
- bool permitsBaseURI(nsIURI* aUri) const;
- bool permitsFormAction(nsIURI* aUri) const;
+ bool permits(CSPDirective aDir,
+ nsIURI* aUri,
+ bool aSpecific) const;
bool allows(nsContentPolicyType aContentType,
enum CSPKeyword aKeyword,
const nsAString& aHashOrNonce) const;
bool allows(nsContentPolicyType aContentType,
enum CSPKeyword aKeyword) const;
void toString(nsAString& outStr) const;
inline void addDirective(nsCSPDirective* aDir)
{ mDirectives.AppendElement(aDir); }
- bool directiveExists(enum CSPDirective aDir) const;
+ bool hasDirective(CSPDirective aDir) const;
inline void setReportOnlyFlag(bool aFlag)
{ mReportOnly = aFlag; }
inline bool getReportOnlyFlag() const
{ return mReportOnly; }
void getReportURIs(nsTArray<nsString> &outReportURIs) const;
void getDirectiveStringForContentType(nsContentPolicyType aContentType,
nsAString& outDirective) const;
- void getDirectiveAsString(enum CSPDirective aDir, nsAString& outDirective) const;
+ void getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const;
inline uint32_t getNumDirectives() const
{ return mDirectives.Length(); }
private:
nsTArray<nsCSPDirective*> mDirectives;
bool mReportOnly;
};
--- a/dom/vr/VRDevice.h
+++ b/dom/vr/VRDevice.h
@@ -217,17 +217,17 @@ class PositionSensorVRDevice : public VR
public:
virtual already_AddRefed<VRPositionState> GetState(double timeOffset) = 0;
virtual void ZeroSensor() = 0;
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
protected:
- PositionSensorVRDevice(nsISupports* aParent)
+ explicit PositionSensorVRDevice(nsISupports* aParent)
: VRDevice(aParent, VRDevice::PositionSensor)
{ }
virtual ~PositionSensorVRDevice() { }
};
} // namespace dom
} // namespace mozilla
--- a/dom/webidl/IDBObjectStore.webidl
+++ b/dom/webidl/IDBObjectStore.webidl
@@ -3,18 +3,17 @@
* 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/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBObjectStore
*/
dictionary IDBObjectStoreParameters {
- // TODO (DOMString or sequence<DOMString>)? keyPath = null;
- any keyPath = null;
+ (DOMString or sequence<DOMString>)? keyPath = null;
boolean autoIncrement = false;
};
interface IDBObjectStore {
readonly attribute DOMString name;
[Throws]
readonly attribute any keyPath;
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -21,16 +21,17 @@
#include "nsPIDOMWindow.h"
#include <algorithm>
#include "BackgroundChild.h"
#include "GeckoProfiler.h"
#include "jsfriendapi.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/TimeStamp.h"
#include "mozilla/dom/asmjscache/AsmJSCache.h"
#include "mozilla/dom/AtomList.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ErrorEventBinding.h"
#include "mozilla/dom/EventTargetBinding.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/ScriptSettings.h"
@@ -40,36 +41,32 @@
#include "nsContentUtils.h"
#include "nsCycleCollector.h"
#include "nsDOMJSUtils.h"
#include "nsIIPCBackgroundChildCreateCallback.h"
#include "nsISupportsImpl.h"
#include "nsLayoutStatics.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
-#include "nsThread.h"
#include "nsThreadUtils.h"
#include "nsXPCOM.h"
#include "nsXPCOMPrivate.h"
#include "OSFileConstants.h"
#include "xpcpublic.h"
#ifdef MOZ_NUWA_PROCESS
#include "ipc/Nuwa.h"
#endif
-#ifdef DEBUG
-#include "nsThreadManager.h"
-#endif
-
#include "Principal.h"
#include "ServiceWorker.h"
#include "SharedWorker.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
+#include "WorkerThread.h"
#ifdef ENABLE_TESTS
#include "BackgroundChildImpl.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "prrng.h"
#endif
using namespace mozilla;
@@ -85,20 +82,16 @@ using mozilla::Preferences;
#define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024
// The size of the generational GC nursery for workers, in bytes.
#define WORKER_DEFAULT_NURSERY_SIZE 1 * 1024 * 1024
// The size of the worker JS allocation threshold in MB. May be changed via pref.
#define WORKER_DEFAULT_ALLOCATION_THRESHOLD 30
-// The C stack size. We use the same stack size on all platforms for
-// consistency.
-#define WORKER_STACK_SIZE 256 * sizeof(size_t) * 1024
-
// Half the size of the actual C stack, to be safe.
#define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024
// The maximum number of threads to use for workers, overridable via pref.
#define MAX_WORKERS_PER_DOMAIN 10
static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
"We should allow at least one worker per domain.");
@@ -902,16 +895,68 @@ public:
nsCycleCollector_collect(nullptr);
}
}
private:
WorkerPrivate* mWorkerPrivate;
};
+#ifdef ENABLE_TESTS
+
+class TestPBackgroundCreateCallback MOZ_FINAL :
+ public nsIIPCBackgroundChildCreateCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ virtual void
+ ActorCreated(PBackgroundChild* aActor) MOZ_OVERRIDE
+ {
+ MOZ_RELEASE_ASSERT(aActor);
+ }
+
+ virtual void
+ ActorFailed() MOZ_OVERRIDE
+ {
+ MOZ_CRASH("TestPBackground() should not fail "
+ "GetOrCreateForCurrentThread()");
+ }
+
+private:
+ ~TestPBackgroundCreateCallback()
+ { }
+};
+
+NS_IMPL_ISUPPORTS(TestPBackgroundCreateCallback,
+ nsIIPCBackgroundChildCreateCallback);
+
+void
+TestPBackground()
+{
+ using namespace mozilla::ipc;
+
+ if (gTestPBackground) {
+ // Randomize value to validate workers are not cross-posting messages.
+ uint32_t testValue;
+ size_t randomSize = PR_GetRandomNoise(&testValue, sizeof(testValue));
+ MOZ_RELEASE_ASSERT(randomSize == sizeof(testValue));
+ nsCString testStr;
+ testStr.AppendInt(testValue);
+ testStr.AppendInt(reinterpret_cast<int64_t>(PR_GetCurrentThread()));
+ PBackgroundChild* existingBackgroundChild =
+ BackgroundChild::GetForCurrentThread();
+ MOZ_RELEASE_ASSERT(existingBackgroundChild);
+ bool ok = existingBackgroundChild->SendPBackgroundTestConstructor(testStr);
+ MOZ_RELEASE_ASSERT(ok);
+ }
+}
+
+#endif // ENABLE_TESTS
+
class WorkerBackgroundChildCallback MOZ_FINAL :
public nsIIPCBackgroundChildCreateCallback
{
bool* mDone;
public:
explicit WorkerBackgroundChildCallback(bool* aDone)
: mDone(aDone)
@@ -937,42 +982,42 @@ private:
{
*mDone = true;
}
};
class WorkerThreadPrimaryRunnable MOZ_FINAL : public nsRunnable
{
WorkerPrivate* mWorkerPrivate;
- nsRefPtr<RuntimeService::WorkerThread> mThread;
+ nsRefPtr<WorkerThread> mThread;
JSRuntime* mParentRuntime;
class FinishedRunnable MOZ_FINAL : public nsRunnable
{
- nsRefPtr<RuntimeService::WorkerThread> mThread;
+ nsRefPtr<WorkerThread> mThread;
public:
- explicit FinishedRunnable(already_AddRefed<RuntimeService::WorkerThread> aThread)
+ explicit FinishedRunnable(already_AddRefed<WorkerThread> aThread)
: mThread(aThread)
{
MOZ_ASSERT(mThread);
}
NS_DECL_ISUPPORTS_INHERITED
private:
~FinishedRunnable()
{ }
NS_DECL_NSIRUNNABLE
};
public:
WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate,
- RuntimeService::WorkerThread* aThread,
+ WorkerThread* aThread,
JSRuntime* aParentRuntime)
: mWorkerPrivate(aWorkerPrivate), mThread(aThread), mParentRuntime(aParentRuntime)
{
MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(aThread);
}
NS_DECL_ISUPPORTS_INHERITED
@@ -1073,139 +1118,16 @@ PlatformOverrideChanged(const char* /* a
RuntimeService* runtime = RuntimeService::GetService();
if (runtime) {
runtime->UpdatePlatformOverridePreference(override);
}
}
} /* anonymous namespace */
-class RuntimeService::WorkerThread MOZ_FINAL : public nsThread
-{
- class Observer MOZ_FINAL : public nsIThreadObserver
- {
- WorkerPrivate* mWorkerPrivate;
-
- public:
- explicit Observer(WorkerPrivate* aWorkerPrivate)
- : mWorkerPrivate(aWorkerPrivate)
- {
- MOZ_ASSERT(aWorkerPrivate);
- aWorkerPrivate->AssertIsOnWorkerThread();
- }
-
- NS_DECL_THREADSAFE_ISUPPORTS
-
- private:
- ~Observer()
- {
- mWorkerPrivate->AssertIsOnWorkerThread();
- }
-
- NS_DECL_NSITHREADOBSERVER
- };
-
- WorkerPrivate* mWorkerPrivate;
- nsRefPtr<Observer> mObserver;
-
-#ifdef DEBUG
- // Protected by nsThread::mLock.
- bool mAcceptingNonWorkerRunnables;
-#endif
-
-public:
- static already_AddRefed<WorkerThread>
- Create();
-
- void
- SetWorker(WorkerPrivate* aWorkerPrivate);
-
- NS_DECL_ISUPPORTS_INHERITED
-
- NS_IMETHOD
- Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) MOZ_OVERRIDE;
-
-#ifdef DEBUG
- bool
- IsAcceptingNonWorkerRunnables()
- {
- MutexAutoLock lock(mLock);
- return mAcceptingNonWorkerRunnables;
- }
-
- void
- SetAcceptingNonWorkerRunnables(bool aAcceptingNonWorkerRunnables)
- {
- MutexAutoLock lock(mLock);
- mAcceptingNonWorkerRunnables = aAcceptingNonWorkerRunnables;
- }
-#endif
-
-#ifdef ENABLE_TESTS
- class TestPBackgroundCreateCallback MOZ_FINAL :
- public nsIIPCBackgroundChildCreateCallback
- {
- public:
- virtual void ActorCreated(PBackgroundChild* actor) MOZ_OVERRIDE
- {
- MOZ_RELEASE_ASSERT(actor);
- }
-
- virtual void ActorFailed() MOZ_OVERRIDE
- {
- MOZ_CRASH("TestPBackground() should not fail GetOrCreateForCurrentThread()");
- }
-
- private:
- ~TestPBackgroundCreateCallback()
- { }
-
- public:
- NS_DECL_ISUPPORTS;
- };
-
- void
- TestPBackground()
- {
- using namespace mozilla::ipc;
- if (gTestPBackground) {
- // Randomize value to validate workers are not cross-posting messages.
- uint32_t testValue;
- size_t randomSize = PR_GetRandomNoise(&testValue, sizeof(testValue));
- MOZ_RELEASE_ASSERT(randomSize == sizeof(testValue));
- nsCString testStr;
- testStr.AppendInt(testValue);
- testStr.AppendInt(reinterpret_cast<int64_t>(PR_GetCurrentThread()));
- PBackgroundChild* existingBackgroundChild =
- BackgroundChild::GetForCurrentThread();
- MOZ_RELEASE_ASSERT(existingBackgroundChild);
- bool ok = existingBackgroundChild->SendPBackgroundTestConstructor(testStr);
- MOZ_RELEASE_ASSERT(ok);
- }
- }
-#endif // #ENABLE_TESTS
-
-private:
- WorkerThread()
- : nsThread(nsThread::NOT_MAIN_THREAD, WORKER_STACK_SIZE),
- mWorkerPrivate(nullptr)
-#ifdef DEBUG
- , mAcceptingNonWorkerRunnables(true)
-#endif
- { }
-
- ~WorkerThread()
- { }
-};
-
-#ifdef ENABLE_TESTS
-NS_IMPL_ISUPPORTS(RuntimeService::WorkerThread::TestPBackgroundCreateCallback,
- nsIIPCBackgroundChildCreateCallback);
-#endif
-
BEGIN_WORKERS_NAMESPACE
void
CancelWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
RuntimeService* runtime = RuntimeService::GetService();
if (runtime) {
@@ -1302,16 +1224,22 @@ IsCurrentThreadRunningChromeWorker()
JSContext*
GetCurrentThreadJSContext()
{
return GetCurrentThreadWorkerPrivate()->GetJSContext();
}
END_WORKERS_NAMESPACE
+struct RuntimeService::IdleThreadInfo
+{
+ nsRefPtr<WorkerThread> mThread;
+ mozilla::TimeStamp mExpirationTime;
+};
+
// This is only touched on the main thread. Initialized in Init() below.
JSSettings RuntimeService::sDefaultJSSettings;
bool RuntimeService::sDefaultPreferences[WORKERPREF_COUNT] = { false };
RuntimeService::RuntimeService()
: mMutex("RuntimeService::mMutex"), mObserved(false),
mShuttingDown(false), mNavigatorPropertiesLoaded(false)
{
@@ -1609,38 +1537,38 @@ RuntimeService::ScheduleWorker(JSContext
MutexAutoLock lock(mMutex);
if (!mIdleThreadArray.IsEmpty()) {
uint32_t index = mIdleThreadArray.Length() - 1;
mIdleThreadArray[index].mThread.swap(thread);
mIdleThreadArray.RemoveElementAt(index);
}
}
+ const WorkerThreadFriendKey friendKey;
+
if (!thread) {
- thread = WorkerThread::Create();
+ thread = WorkerThread::Create(friendKey);
if (!thread) {
UnregisterWorker(aCx, aWorkerPrivate);
JS_ReportError(aCx, "Could not create new thread!");
return false;
}
}
- MOZ_ASSERT(thread->IsAcceptingNonWorkerRunnables());
-
int32_t priority = aWorkerPrivate->IsChromeWorker() ?
nsISupportsPriority::PRIORITY_NORMAL :
nsISupportsPriority::PRIORITY_LOW;
if (NS_FAILED(thread->SetPriority(priority))) {
NS_WARNING("Could not set the thread's priority!");
}
nsCOMPtr<nsIRunnable> runnable =
new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread, JS_GetParentRuntime(aCx));
- if (NS_FAILED(thread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
+ if (NS_FAILED(thread->DispatchPrimaryRunnable(friendKey, runnable))) {
UnregisterWorker(aCx, aWorkerPrivate);
JS_ReportError(aCx, "Could not dispatch to thread!");
return false;
}
return true;
}
@@ -2382,20 +2310,16 @@ RuntimeService::ForgetSharedWorker(Worke
}
void
RuntimeService::NoteIdleThread(WorkerThread* aThread)
{
AssertIsOnMainThread();
MOZ_ASSERT(aThread);
-#ifdef DEBUG
- aThread->SetAcceptingNonWorkerRunnables(true);
-#endif
-
static TimeDuration timeout =
TimeDuration::FromSeconds(IDLE_THREAD_TIMEOUT_SEC);
TimeStamp expirationTime = TimeStamp::Now() + timeout;
bool shutdown;
if (mShuttingDown) {
shutdown = true;
@@ -2587,155 +2511,16 @@ RuntimeService::JSVersionChanged(const c
{
AssertIsOnMainThread();
bool useLatest = Preferences::GetBool(PREF_WORKERS_LATEST_JS_VERSION, false);
JS::CompartmentOptions& options = sDefaultJSSettings.content.compartmentOptions;
options.setVersion(useLatest ? JSVERSION_LATEST : JSVERSION_DEFAULT);
}
-// static
-already_AddRefed<RuntimeService::WorkerThread>
-RuntimeService::WorkerThread::Create()
-{
- MOZ_ASSERT(nsThreadManager::get());
-
- nsRefPtr<WorkerThread> thread = new WorkerThread();
- if (NS_FAILED(thread->Init())) {
- NS_WARNING("Failed to create new thread!");
- return nullptr;
- }
-
- NS_SetThreadName(thread, "DOM Worker");
-
- return thread.forget();
-}
-
-void
-RuntimeService::WorkerThread::SetWorker(WorkerPrivate* aWorkerPrivate)
-{
- MOZ_ASSERT(PR_GetCurrentThread() == mThread);
- MOZ_ASSERT_IF(aWorkerPrivate, !mWorkerPrivate);
- MOZ_ASSERT_IF(!aWorkerPrivate, mWorkerPrivate);
-
- // No need to lock here because mWorkerPrivate is only modified on mThread.
-
- if (mWorkerPrivate) {
- MOZ_ASSERT(mObserver);
-
- MOZ_ALWAYS_TRUE(NS_SUCCEEDED(RemoveObserver(mObserver)));
-
- mObserver = nullptr;
- mWorkerPrivate->SetThread(nullptr);
- }
-
- mWorkerPrivate = aWorkerPrivate;
-
- if (mWorkerPrivate) {
- mWorkerPrivate->SetThread(this);
-
- nsRefPtr<Observer> observer = new Observer(mWorkerPrivate);
-
- MOZ_ALWAYS_TRUE(NS_SUCCEEDED(AddObserver(observer)));
-
- mObserver.swap(observer);
- }
-}
-
-NS_IMPL_ISUPPORTS_INHERITED0(RuntimeService::WorkerThread, nsThread)
-
-NS_IMETHODIMP
-RuntimeService::WorkerThread::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags)
-{
- // May be called on any thread!
-
-#ifdef DEBUG
- if (PR_GetCurrentThread() == mThread) {
- MOZ_ASSERT(mWorkerPrivate);
- mWorkerPrivate->AssertIsOnWorkerThread();
- }
- else if (aRunnable && !IsAcceptingNonWorkerRunnables()) {
- // Only enforce cancelable runnables after we've started the worker loop.
- nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(aRunnable);
- MOZ_ASSERT(cancelable,
- "Should have been wrapped by the worker's event target!");
- }
-#endif
-
- // Workers only support asynchronous dispatch for now.
- if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
- return NS_ERROR_UNEXPECTED;