Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-01-16 16:23:41 +01:00
commit efe3acd08b
229 changed files with 5496 additions and 4522 deletions

View File

@ -139,6 +139,7 @@
@BINPATH@/components/chrome.xpt
@BINPATH@/components/commandhandler.xpt
@BINPATH@/components/commandlines.xpt
@BINPATH@/components/compartments.xpt
@BINPATH@/components/composer.xpt
@BINPATH@/components/content_events.xpt
@BINPATH@/components/content_html.xpt

View File

@ -4,7 +4,7 @@
///////////////////
//
// Whitelisting this test.
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
//
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Shader Editor is still waiting for a WebGL context to be created.");
@ -26,35 +26,41 @@ function ifWebGLSupported() {
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
let [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
ok(error,
"The new vertex shader source was compiled with errors.");
is(error.compile, "",
"The compilation status should be empty.");
isnot(error.link, "",
"The linkage status should not be empty.");
is(error.link.split("ERROR").length - 1, 2,
"The linkage status contains two errors.");
ok(error.link.contains("ERROR: 0:8: 'constructor'"),
"A constructor error is contained in the linkage status.");
ok(error.link.contains("ERROR: 0:8: 'assign'"),
"An assignment error is contained in the linkage status.");
// The implementation has the choice to defer all compile-time errors to link time.
let infoLog = (error.compile != "") ? error.compile : error.link;
isnot(infoLog, "",
"The one of the compile or link info logs should not be empty.");
is(infoLog.split("ERROR").length - 1, 2,
"The info log status contains two errors.");
ok(infoLog.contains("ERROR: 0:8: 'constructor'"),
"A constructor error is contained in the info log.");
ok(infoLog.contains("ERROR: 0:8: 'assign'"),
"An assignment error is contained in the info log.");
fsEditor.replaceText("vec4", { line: 2, ch: 14 }, { line: 2, ch: 18 });
[, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
ok(error,
"The new fragment shader source was compiled with errors.");
is(error.compile, "",
"The compilation status should be empty.");
isnot(error.link, "",
"The linkage status should not be empty.");
is(error.link.split("ERROR").length - 1, 1,
"The linkage status contains one error.");
ok(error.link.contains("ERROR: 0:6: 'constructor'"),
"A constructor error is contained in the linkage status.");
infoLog = (error.compile != "") ? error.compile : error.link;
isnot(infoLog, "",
"The one of the compile or link info logs should not be empty.");
is(infoLog.split("ERROR").length - 1, 1,
"The info log contains one error.");
ok(infoLog.contains("ERROR: 0:6: 'constructor'"),
"A constructor error is contained in the info log.");
yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);

View File

@ -23,16 +23,18 @@ function ifWebGLSupported() {
} catch (error) {
ok(error,
"The new vertex shader source was compiled with errors.");
is(error.compile, "",
"The compilation status should be empty.");
isnot(error.link, "",
"The linkage status should not be empty.");
is(error.link.split("ERROR").length - 1, 2,
"The linkage status contains two errors.");
ok(error.link.contains("ERROR: 0:8: 'constructor'"),
"A constructor error is contained in the linkage status.");
ok(error.link.contains("ERROR: 0:8: 'assign'"),
"An assignment error is contained in the linkage status.");
// The implementation has the choice to defer all compile-time errors to link time.
let infoLog = (error.compile != "") ? error.compile : error.link;
isnot(infoLog, "",
"The one of the compile or link info logs should not be empty.");
is(infoLog.split("ERROR").length - 1, 2,
"The info log contains two errors.");
ok(infoLog.contains("ERROR: 0:8: 'constructor'"),
"A constructor error is contained in the info log.");
ok(infoLog.contains("ERROR: 0:8: 'assign'"),
"An assignment error is contained in the info log.");
}
yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
@ -52,14 +54,16 @@ function ifWebGLSupported() {
} catch (error) {
ok(error,
"The new fragment shader source was compiled with errors.");
is(error.compile, "",
"The compilation status should be empty.");
isnot(error.link, "",
"The linkage status should not be empty.");
is(error.link.split("ERROR").length - 1, 1,
"The linkage status contains one error.");
ok(error.link.contains("ERROR: 0:6: 'constructor'"),
"A constructor error is contained in the linkage status.");
// The implementation has the choice to defer all compile-time errors to link time.
let infoLog = (error.compile != "") ? error.compile : error.link;
isnot(infoLog, "",
"The one of the compile or link info logs should not be empty.");
is(infoLog.split("ERROR").length - 1, 1,
"The info log contains one error.");
ok(infoLog.contains("ERROR: 0:6: 'constructor'"),
"A constructor error is contained in the info log.");
}
yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);

View File

@ -296,6 +296,7 @@ function SearchPref(event) {
}
}
let getAllPrefs; // Used by tests
function BuildUI() {
table = document.querySelector("table");
let trs = table.querySelectorAll("tr:not(#add-custom-preference)");
@ -307,7 +308,8 @@ function BuildUI() {
if (AppManager.connection &&
AppManager.connection.status == Connection.Status.CONNECTED &&
AppManager.preferenceFront) {
AppManager.preferenceFront.getAllPrefs().then(json => {
getAllPrefs = AppManager.preferenceFront.getAllPrefs();
getAllPrefs.then(json => {
let devicePrefs = Object.keys(json);
devicePrefs.sort();
for (let i = 0; i < devicePrefs.length; i++) {

View File

@ -292,7 +292,7 @@ let UI = {
this.unbusy();
}, (e) => {
let message;
if (e.error && e.message) {
if (e && e.error && e.message) {
// Some errors come from fronts that are not based on protocol.js.
// Errors are not translated to strings.
message = operationDescription + " (" + e.error + "): " + e.message;
@ -303,7 +303,9 @@ let UI = {
let operationCanceled = e && e.canceled;
if (!operationCanceled) {
UI.reportError("error_operationFail", message);
console.error(e);
if (e) {
console.error(e);
}
}
this.unbusy();
});
@ -439,8 +441,13 @@ let UI = {
connectToRuntime: function(runtime) {
let name = runtime.name;
let promise = AppManager.connectToRuntime(runtime);
promise.then(() => this.initConnectionTelemetry());
return this.busyUntil(promise, "connecting to runtime " + name);
promise.then(() => this.initConnectionTelemetry())
.catch(() => {
// Empty rejection handler to silence uncaught rejection warnings
// |busyUntil| will listen for rejections.
// Bug 1121100 may find a better way to silence these.
});
return this.busyUntil(promise, "Connecting to " + name);
},
updateRuntimeButton: function() {

View File

@ -93,12 +93,13 @@ let AppManager = exports.AppManager = {
},
onConnectionChanged: function() {
console.log("Connection status changed: " + this.connection.status);
if (this.connection.status == Connection.Status.DISCONNECTED) {
this.selectedRuntime = null;
}
if (this.connection.status != Connection.Status.CONNECTED) {
console.log("Connection status changed: " + this.connection.status);
if (this._appsFront) {
this._appsFront.off("install-progress", this.onInstallProgress);
this._appsFront.unwatchApps();
@ -354,18 +355,15 @@ let AppManager = exports.AppManager = {
} else {
deferred.reject();
}
}
};
this.connection.on(Connection.Events.CONNECTED, onConnectedOrDisconnected);
this.connection.on(Connection.Events.DISCONNECTED, onConnectedOrDisconnected);
try {
// Reset the connection's state to defaults
this.connection.resetOptions();
this.selectedRuntime.connect(this.connection).then(
() => {},
deferred.reject.bind(deferred));
deferred.resolve(this.selectedRuntime.connect(this.connection));
} catch(e) {
console.error(e);
deferred.reject();
deferred.reject(e);
}
}, deferred.reject);
@ -386,6 +384,10 @@ let AppManager = exports.AppManager = {
this.connection.once(Connection.Events.STATUS_CHANGED, () => {
this._telemetry.stopTimer(timerId);
});
}).catch(() => {
// Empty rejection handler to silence uncaught rejection warnings
// |connectToRuntime| caller should listen for rejections.
// Bug 1121100 may find a better way to silence these.
});
return deferred.promise;

View File

@ -401,7 +401,7 @@ DeprecatedUSBRuntime.prototype = {
},
connect: function(connection) {
if (!this.device) {
return promise.reject("Can't find device: " + this.name);
return promise.reject(new Error("Can't find device: " + this.name));
}
return this.device.connect().then((port) => {
connection.host = "localhost";
@ -445,7 +445,7 @@ WiFiRuntime.prototype = {
connect: function(connection) {
let service = discovery.getRemoteService("devtools", this.deviceName);
if (!service) {
return promise.reject("Can't find device: " + this.name);
return promise.reject(new Error("Can't find device: " + this.name));
}
connection.host = service.host;
connection.port = service.port;
@ -474,7 +474,7 @@ SimulatorRuntime.prototype = {
let port = ConnectionManager.getFreeTCPPort();
let simulator = Simulator.getByName(this.name);
if (!simulator || !simulator.launch) {
return promise.reject("Can't find simulator: " + this.name);
return promise.reject(new Error("Can't find simulator: " + this.name));
}
return simulator.launch({port: port}).then(() => {
connection.host = "localhost";
@ -520,7 +520,7 @@ let gRemoteRuntime = {
connect: function(connection) {
let win = Services.wm.getMostRecentWindow("devtools:webide");
if (!win) {
return promise.reject();
return promise.reject(new Error("No WebIDE window found"));
}
let ret = {value: connection.host + ":" + connection.port};
let title = Strings.GetStringFromName("remote_runtime_promptTitle");
@ -531,7 +531,7 @@ let gRemoteRuntime = {
return promise.reject({canceled: true});
}
if (!host || !port) {
return promise.reject();
return promise.reject(new Error("Invalid host or port"));
}
connection.host = host;
connection.port = port;

View File

@ -46,6 +46,7 @@
win.Cmds.showDevicePrefs();
is(deck.selectedPanel, prefIframe, "device preferences iframe selected");
yield prefIframe.contentWindow.getAllPrefs;
yield nextTick();
let doc = prefIframe.contentWindow.document;

View File

@ -49,7 +49,7 @@
win.Cmds.showRuntimeDetails();
is(deck.selectedPanel, infoIframe, "info iframe selected");
yield infoIframe.contentWindow.getRawPermissionsTablePromise;
yield infoIframe.contentWindow.getDescriptionPromise;
yield nextTick();
@ -72,7 +72,7 @@
win.Cmds.showPermissionsTable();
is(deck.selectedPanel, permIframe, "permission iframe selected");
yield infoIframe.contentWindow.getDescriptionPromise;
yield permIframe.contentWindow.getRawPermissionsTablePromise;
yield nextTick();

View File

@ -143,7 +143,7 @@
function connectToRuntime(win, type, index) {
return Task.spawn(function*() {
yield startConnection(win, type, index);
startConnection(win, type, index);
yield waitUntilConnected(win);
});
}

View File

@ -175,6 +175,7 @@
@RESPATH@/components/chrome.xpt
@RESPATH@/components/commandhandler.xpt
@RESPATH@/components/commandlines.xpt
@RESPATH@/components/compartments.xpt
@RESPATH@/components/composer.xpt
@RESPATH@/components/content_events.xpt
@RESPATH@/components/content_html.xpt

View File

@ -2111,13 +2111,8 @@ ia64*-hpux*)
# while; Intel recommends against using it.
MOZ_OPTIMIZE_FLAGS="-O2"
elif test "$GNU_CC" -o "$GNU_CXX"; then
case $GCC_VERSION in
4.5.*)
# -Os is broken on gcc 4.5.x we need to tweak it to get good results.
MOZ_OPTIMIZE_SIZE_TWEAK="-finline-limit=50"
esac
MOZ_PGO_OPTIMIZE_FLAGS="-O3"
MOZ_OPTIMIZE_FLAGS="-Os $MOZ_OPTIMIZE_SIZE_TWEAK"
MOZ_OPTIMIZE_FLAGS="-Os"
if test -z "$CLANG_CC"; then
MOZ_OPTIMIZE_FLAGS="-freorder-blocks $MOZ_OPTIMIZE_FLAGS"
fi
@ -2366,6 +2361,9 @@ ia64*-hpux*)
AC_DEFINE(_X86_)
;;
x86_64-*)
if test -z "$HAVE_64BIT_BUILD"; then
AC_MSG_ERROR([You are targeting 64-bit but using the 32-bit compiler.])
fi
if test -n "$_WIN32_MSVC"; then
DSO_LDOPTS="$DSO_LDOPTS -MACHINE:X64"
fi
@ -2627,7 +2625,7 @@ if test "$GNU_CC" -a "$OS_TARGET" != WINNT; then
;;
*)
case $GCC_VERSION in
4.4*|4.6*)
4.6*)
VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(MOZILLA_DIR)/config/gcc_hidden_dso_handle.h'
;;
*)
@ -6926,7 +6924,6 @@ AC_SUBST(MOZ_FRAMEPTR_FLAGS)
AC_SUBST(MOZ_OPTIMIZE_FLAGS)
AC_SUBST(MOZ_OPTIMIZE_LDFLAGS)
AC_SUBST(MOZ_ALLOW_HEAP_EXECUTE_FLAGS)
AC_SUBST(MOZ_OPTIMIZE_SIZE_TWEAK)
AC_SUBST(MOZ_PGO)
AC_SUBST(MOZ_PGO_OPTIMIZE_FLAGS)

View File

@ -722,5 +722,32 @@ DOMException::Create(nsresult aRv)
return inst.forget();
}
bool
DOMException::Sanitize(JSContext* aCx,
JS::MutableHandle<JS::Value> aSanitizedValue)
{
nsRefPtr<DOMException> retval = this;
if (mLocation && !mLocation->CallerSubsumes(aCx)) {
nsString message;
GetMessageMoz(message);
nsString name;
GetName(name);
retval = new dom::DOMException(nsresult(Result()),
NS_ConvertUTF16toUTF8(message),
NS_ConvertUTF16toUTF8(name),
Code());
// Now it's possible that the stack on retval still starts with
// stuff aCx is not supposed to touch; it depends on what's on the
// stack right this second. Walk past all of that.
while (retval->mLocation && !retval->mLocation->CallerSubsumes(aCx)) {
nsCOMPtr<nsIStackFrame> caller;
retval->mLocation->GetCaller(getter_AddRefs(caller));
retval->mLocation.swap(caller);
}
}
return ToJSValue(aCx, retval, aSanitizedValue);
}
} // namespace dom
} // namespace mozilla

View File

@ -156,6 +156,15 @@ public:
static already_AddRefed<DOMException>
Create(nsresult aRv);
// Sanitize() is a workaround for the fact that DOMExceptions can leak stack
// information for the first stackframe to callers that should not have access
// to it. To prevent this, we check whether aCx subsumes our first stackframe
// and if not hand out a JS::Value for a clone of ourselves. Otherwise we
// hand out a JS::Value for ourselves.
//
// If the return value is false, an exception was thrown on aCx.
bool Sanitize(JSContext* aCx, JS::MutableHandle<JS::Value> aSanitizedValue);
protected:
virtual ~DOMException() {}

View File

@ -208,8 +208,6 @@ NS_GetContentList(nsINode* aRootNode,
static const PLDHashTableOps hash_table_ops =
{
PL_DHashAllocTable,
PL_DHashFreeTable,
ContentListHashtableHashKey,
ContentListHashtableMatchEntry,
PL_DHashMoveEntryStub,
@ -320,8 +318,6 @@ GetFuncStringContentList(nsINode* aRootNode,
static const PLDHashTableOps hash_table_ops =
{
PL_DHashAllocTable,
PL_DHashFreeTable,
FuncStringContentListHashtableHashKey,
FuncStringContentListHashtableMatchEntry,
PL_DHashMoveEntryStub,

View File

@ -479,8 +479,6 @@ nsContentUtils::Init()
if (!sEventListenerManagersHash.ops) {
static const PLDHashTableOps hash_table_ops =
{
PL_DHashAllocTable,
PL_DHashFreeTable,
PL_DHashVoidPtrKeyStub,
PL_DHashMatchEntryStub,
PL_DHashMoveEntryStub,

View File

@ -3972,8 +3972,6 @@ nsDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc)
static const PLDHashTableOps hash_table_ops =
{
PL_DHashAllocTable,
PL_DHashFreeTable,
PL_DHashVoidPtrKeyStub,
PL_DHashMatchEntryStub,
PL_DHashMoveEntryStub,

View File

@ -320,8 +320,6 @@ nsScriptNameSpaceManager::Init()
{
static const PLDHashTableOps hash_table_ops =
{
PL_DHashAllocTable,
PL_DHashFreeTable,
GlobalNameHashHashKey,
GlobalNameHashMatchEntry,
PL_DHashMoveEntryStub,

View File

@ -234,20 +234,11 @@ ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx)
nsresult rv =
UNWRAP_OBJECT(DOMException, &mJSException.toObject(), domException);
if (NS_SUCCEEDED(rv)) {
// We actually have to create a new DOMException object, because the one we
// We may have to create a new DOMException object, because the one we
// have has a stack that includes the chrome code that threw it, and in
// particular has the wrong file/line/column information.
nsString message;
domException->GetMessageMoz(message);
nsString name;
domException->GetName(name);
nsRefPtr<dom::DOMException> newException =
new dom::DOMException(nsresult(domException->Result()),
NS_ConvertUTF16toUTF8(message),
NS_ConvertUTF16toUTF8(name),
domException->Code());
JS::Rooted<JS::Value> reflector(aCx);
if (!GetOrCreateDOMReflector(aCx, newException, &reflector)) {
if (!domException->Sanitize(aCx, &reflector)) {
// Well, that threw _an_ exception. Let's forget ours. We can just
// unroot and not change the value, since mJSException is completely
// ignored if mResult is not NS_ERROR_DOM_JS_EXCEPTION and we plan to
@ -301,6 +292,17 @@ ErrorResult::StealJSException(JSContext* cx,
value.set(mJSException);
js::RemoveRawValueRoot(cx, &mJSException);
mResult = NS_OK;
if (value.isObject()) {
// If it's a DOMException we may need to sanitize it.
dom::DOMException* domException;
nsresult rv =
UNWRAP_OBJECT(DOMException, &value.toObject(), domException);
if (NS_SUCCEEDED(rv) && !domException->Sanitize(cx, value)) {
JS_GetPendingException(cx, value);
JS_ClearPendingException(cx);
}
}
}
void

View File

@ -1278,8 +1278,7 @@ DOMInterfaces = {
'WebGLActiveInfo': {
'nativeType': 'mozilla::WebGLActiveInfo',
'headerFile': 'WebGLActiveInfo.h',
'wrapperCache': False
'headerFile': 'WebGLActiveInfo.h'
},
'WebGLBuffer': {
@ -1472,8 +1471,7 @@ DOMInterfaces = {
'WebGLUniformLocation': {
'nativeType': 'mozilla::WebGLUniformLocation',
'headerFile': 'WebGLUniformLocation.h',
'wrapperCache': False
'headerFile': 'WebGLUniformLocation.h'
},
'WebGLVertexArray': {

View File

@ -21,6 +21,8 @@
#include "WorkerPrivate.h"
#include "nsGlobalWindow.h"
#include "WorkerScope.h"
#include "jsapi.h"
#include "nsJSPrincipals.h"
namespace mozilla {
namespace dom {
@ -186,14 +188,36 @@ bool
CallbackObject::CallSetup::ShouldRethrowException(JS::Handle<JS::Value> aException)
{
if (mExceptionHandling == eRethrowExceptions) {
return true;
if (!mCompartment) {
// Caller didn't ask us to filter for only exceptions we subsume.
return true;
}
// On workers, we don't have nsIPrincipals to work with. But we also only
// have one compartment, so check whether mCompartment is the same as the
// current compartment of mCx.
if (mCompartment == js::GetContextCompartment(mCx)) {
return true;
}
MOZ_ASSERT(NS_IsMainThread());
// At this point mCx is in the compartment of our unwrapped callback, so
// just check whether the principal of mCompartment subsumes that of the
// current compartment/global of mCx.
nsIPrincipal* callerPrincipal =
nsJSPrincipals::get(JS_GetCompartmentPrincipals(mCompartment));
nsIPrincipal* calleePrincipal = nsContentUtils::SubjectPrincipal();
if (callerPrincipal->SubsumesConsideringDomain(calleePrincipal)) {
return true;
}
}
MOZ_ASSERT(mExceptionHandling == eRethrowContentExceptions);
MOZ_ASSERT(mCompartment);
// For eRethrowContentExceptions we only want to throw an exception if the
// object that was thrown is a DOMError or DOMException object in the caller
// compartment (which we stored in mCompartment).
// Now we only want to throw an exception to the caller if the object that was
// thrown is a DOMError or DOMException object in the caller compartment
// (which we stored in mCompartment).
if (!aException.isObject()) {
return false;

View File

@ -88,7 +88,10 @@ public:
// binding object for a DOMError or DOMException from the caller's scope,
// otherwise report it.
eRethrowContentExceptions,
// Throw any exception to the caller code.
// Throw exceptions to the caller code, unless the caller compartment is
// provided, the exception is not a DOMError or DOMException from the
// caller compartment, and the caller compartment does not subsume our
// unwrapped callback.
eRethrowExceptions
};
@ -168,6 +171,11 @@ protected:
public:
// If aExceptionHandling == eRethrowContentExceptions then aCompartment
// needs to be set to the compartment in which exceptions will be rethrown.
//
// If aExceptionHandling == eRethrowExceptions then aCompartment may be set
// to the compartment in which exceptions will be rethrown. In that case
// they will only be rethrown if that compartment's principal subsumes the
// principal of our (unwrapped) callback.
CallSetup(CallbackObject* aCallback, ErrorResult& aRv,
ExceptionHandling aExceptionHandling,
JSCompartment* aCompartment = nullptr,
@ -189,7 +197,7 @@ protected:
JSContext* mCx;
// Caller's compartment. This will only have a sensible value if
// mExceptionHandling == eRethrowContentExceptions.
// mExceptionHandling == eRethrowContentExceptions or eRethrowExceptions.
JSCompartment* mCompartment;
// And now members whose construction/destruction order we need to control.

View File

@ -13618,6 +13618,12 @@ class CGCallback(CGClass):
# CallSetup should re-throw exceptions on aRv.
args.append(Argument("ExceptionHandling", "aExceptionHandling",
"eReportExceptions"))
# And the argument for communicating when exceptions should really be
# rethrown. In particular, even when aExceptionHandling is
# eRethrowExceptions they won't get rethrown if aCompartment is provided
# and its principal doesn't subsume either the callback or the
# exception.
args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
# And now insert our template argument.
argsWithoutThis = list(args)
args.insert(0, Argument("const T&", "thisObjPtr"))
@ -13625,7 +13631,7 @@ class CGCallback(CGClass):
setupCall = fill(
"""
CallSetup s(this, aRv, aExceptionHandling);
CallSetup s(this, aRv, aExceptionHandling, aCompartment);
if (!s.GetContext()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return${errorReturn};
@ -13937,11 +13943,10 @@ class CallbackMember(CGNativeMember):
if not self.needThisHandling:
# Since we don't need this handling, we're the actual method that
# will be called, so we need an aRethrowExceptions argument.
if self.rethrowContentException:
args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
else:
if not self.rethrowContentException:
args.append(Argument("ExceptionHandling", "aExceptionHandling",
"eReportExceptions"))
args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
return args
# We want to allow the caller to pass in a "this" value, as
# well as a JSContext.
@ -13959,7 +13964,7 @@ class CallbackMember(CGNativeMember):
callSetup += ", eRethrowContentExceptions, aCompartment, /* aIsJSImplementedWebIDL = */ "
callSetup += toStringBool(isJSImplementedDescriptor(self.descriptorProvider))
else:
callSetup += ", aExceptionHandling"
callSetup += ", aExceptionHandling, aCompartment"
callSetup += ");\n"
return fill(
"""

View File

@ -97,7 +97,8 @@ public:
// StealJSException steals the JS Exception from the object. This method must
// be called only if IsJSException() returns true. This method also resets the
// ErrorCode() to NS_OK.
// ErrorCode() to NS_OK. The value will be ensured to be sanitized wrt to the
// current compartment of cx if it happens to be a DOMException.
void StealJSException(JSContext* cx, JS::MutableHandle<JS::Value> value);
void MOZ_ALWAYS_INLINE MightThrowJSException()

View File

@ -286,6 +286,7 @@ public:
NS_IMETHOD GetName(nsAString& aFunction) MOZ_OVERRIDE;
NS_IMETHOD GetCaller(nsIStackFrame** aCaller) MOZ_OVERRIDE;
NS_IMETHOD GetFormattedStack(nsAString& aStack) MOZ_OVERRIDE;
virtual bool CallerSubsumes(JSContext* aCx) MOZ_OVERRIDE;
protected:
virtual bool IsJSFrame() const MOZ_OVERRIDE {
@ -613,6 +614,37 @@ NS_IMETHODIMP StackFrame::ToString(nsACString& _retval)
return NS_OK;
}
/* virtual */ bool
StackFrame::CallerSubsumes(JSContext* aCx)
{
return true;
}
/* virtual */ bool
JSStackFrame::CallerSubsumes(JSContext* aCx)
{
if (!NS_IsMainThread()) {
return true;
}
if (!mStack) {
// No problem here, there's no data to leak.
return true;
}
nsIPrincipal* callerPrincipal = nsContentUtils::SubjectPrincipal();
JS::Rooted<JSObject*> unwrappedStack(aCx, js::CheckedUnwrap(mStack));
if (!unwrappedStack) {
// We can't leak data here either.
return true;
}
nsIPrincipal* stackPrincipal =
nsJSPrincipals::get(js::GetSavedFramePrincipals(unwrappedStack));
return callerPrincipal->SubsumesConsideringDomain(stackPrincipal);
}
/* static */ already_AddRefed<nsIStackFrame>
JSStackFrame::CreateStack(JSContext* aCx, int32_t aMaxDepth)
{

View File

@ -1,11 +1,12 @@
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const Cu = Components.utils;
const Ci = Components.interfaces;
"use strict";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -71,6 +72,74 @@ TestInterfaceJS.prototype = {
throw new this._win.DOMException("We are a DOMException",
"NotSupportedError");
},
testPromiseWithThrowingChromePromiseInit: function() {
return new this._win.Promise(function() {
noSuchMethodExistsYo1();
})
},
testPromiseWithThrowingContentPromiseInit: function(func) {
return new this._win.Promise(func);
},
testPromiseWithDOMExceptionThrowingPromiseInit: function() {
return new this._win.Promise(() => {
throw new this._win.DOMException("We are a second DOMException",
"NotFoundError");
})
},
testPromiseWithThrowingChromeThenFunction: function() {
return this._win.Promise.resolve(5).then(function() {
noSuchMethodExistsYo2();
});
},
testPromiseWithThrowingContentThenFunction: function(func) {
return this._win.Promise.resolve(10).then(func);
},
testPromiseWithDOMExceptionThrowingThenFunction: function() {
return this._win.Promise.resolve(5).then(() => {
throw new this._win.DOMException("We are a third DOMException",
"NetworkError");
});
},
testPromiseWithThrowingChromeThenable: function() {
// We need to produce a thing that has a "then" property in the page
// compartment, since we plan to call the page-provided resolve function.
var thenable = new this._win.Object();
Cu.waiveXrays(thenable).then = function() {
noSuchMethodExistsYo3()
}
return new this._win.Promise(function(resolve) {
resolve(thenable)
});
},
testPromiseWithThrowingContentThenable: function(thenable) {
// Waive Xrays on the thenable, because we're calling resolve() in the
// chrome compartment, so that's the compartment the "then" property get
// will happen in, and if we leave the Xray in place the function-valued
// property won't return the function.
return this._win.Promise.resolve(Cu.waiveXrays(thenable));
},
testPromiseWithDOMExceptionThrowingThenable: function() {
// We need to produce a thing that has a "then" property in the page
// compartment, since we plan to call the page-provided resolve function.
var thenable = new this._win.Object();
Cu.waiveXrays(thenable).then = () => {
throw new this._win.DOMException("We are a fourth DOMException",
"TypeMismatchError");
}
return new this._win.Promise(function(resolve) {
resolve(thenable)
});
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJS])

View File

@ -55,3 +55,5 @@ skip-if = debug == false
skip-if = debug == false
[test_exception_options_from_jsimplemented.html]
skip-if = debug == false
[test_promise_rejections_from_jsimplemented.html]
skip-if = debug == false

View File

@ -0,0 +1,115 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1107592
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1107592</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1107592 **/
SimpleTest.waitForExplicitFinish();
function checkExn(lineNumber, name, message, code, filename, testNumber, stack, exn) {
ise(exn.lineNumber, lineNumber,
"Should have the right line number in test " + testNumber);
ise(exn.name, name,
"Should have the right exception name in test " + testNumber);
ise("filename" in exn ? exn.filename : exn.fileName, filename,
"Should have the right file name in test " + testNumber);
ise(exn.message, message,
"Should have the right message in test " + testNumber);
ise(exn.code, code, "Should have the right .code in test " + testNumber);
if (message === "") {
ise(exn.name, "NS_ERROR_UNEXPECTED",
"Should have one of our synthetic exceptions in test " + testNumber);
}
ise(exn.stack, stack, "Should have the right stack in test " + testNumber);
}
function ensurePromiseFail(testNumber, value) {
ok(false, "Test " + testNumber + " should not have a fulfilled promise");
}
function doTest() {
var t = new TestInterfaceJS();
var ourFile = "http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html";
Promise.all([
t.testPromiseWithThrowingChromePromiseInit().then(
ensurePromiseFail.bind(null, 1),
checkExn.bind(null, 44, "NS_ERROR_UNEXPECTED", "", undefined,
ourFile, 1,
"doTest@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:44:6\n")),
t.testPromiseWithThrowingContentPromiseInit(function() {
thereIsNoSuchContentFunction1();
}).then(
ensurePromiseFail.bind(null, 2),
checkExn.bind(null, 50, "ReferenceError",
"thereIsNoSuchContentFunction1 is not defined",
undefined, ourFile, 2,
"doTest/<@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:50:11\ndoTest@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:49:7\n")),
t.testPromiseWithThrowingChromeThenFunction().then(
ensurePromiseFail.bind(null, 3),
checkExn.bind(null, 0, "NS_ERROR_UNEXPECTED", "", undefined, "", 3, "")),
t.testPromiseWithThrowingContentThenFunction(function() {
thereIsNoSuchContentFunction2();
}).then(
ensurePromiseFail.bind(null, 4),
checkExn.bind(null, 61, "ReferenceError",
"thereIsNoSuchContentFunction2 is not defined",
undefined, ourFile, 4,
"doTest/<@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:61:11\n")),
t.testPromiseWithThrowingChromeThenable().then(
ensurePromiseFail.bind(null, 5),
checkExn.bind(null, 0, "NS_ERROR_UNEXPECTED", "", undefined, "", 5, "")),
t.testPromiseWithThrowingContentThenable({
then: function() { thereIsNoSuchContentFunction3(); }
}).then(
ensurePromiseFail.bind(null, 6),
checkExn.bind(null, 72, "ReferenceError",
"thereIsNoSuchContentFunction3 is not defined",
undefined, ourFile, 6,
"doTest/<.then@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:72:32\n")),
t.testPromiseWithDOMExceptionThrowingPromiseInit().then(
ensurePromiseFail.bind(null, 7),
checkExn.bind(null, 79, "NotFoundError",
"We are a second DOMException",
DOMException.NOT_FOUND_ERR, ourFile, 7,
"doTest@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:79:6\n")),
t.testPromiseWithDOMExceptionThrowingThenFunction().then(
ensurePromiseFail.bind(null, 8),
checkExn.bind(null, 0, "NetworkError",
"We are a third DOMException",
DOMException.NETWORK_ERR, "", 8, "")),
t.testPromiseWithDOMExceptionThrowingThenable().then(
ensurePromiseFail.bind(null, 9),
checkExn.bind(null, 0, "TypeMismatchError",
"We are a fourth DOMException",
DOMException.TYPE_MISMATCH_ERR, "", 9, "")),
]).then(SimpleTest.finish,
function() {
ok(false, "One of our catch statements totally failed");
SimpleTest.finish();
});
}
SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
doTest);
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1107592">Mozilla Bug 1107592</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -152,6 +152,8 @@ WebGLContext::InitWebGL2()
auto xfBuffers = new WebGLRefPtr<WebGLBuffer>[mGLMaxTransformFeedbackSeparateAttribs];
mBoundTransformFeedbackBuffers.reset(xfBuffers);
mBypassShaderValidation = true;
return true;
}

View File

@ -212,14 +212,13 @@ WebGL2Context::TransformFeedbackVaryings(WebGLProgram* program,
tmpVaryings[n] = (GLchar*) ToNewCString(varyings[n]);
}
GLuint progname = program->GLName();
GLuint progname = program->mGLName;
MakeContextCurrent();
gl->fTransformFeedbackVaryings(progname, count, tmpVaryings, bufferMode);
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, tmpVaryings);
}
already_AddRefed<WebGLActiveInfo>
WebGL2Context::GetTransformFeedbackVarying(WebGLProgram* program, GLuint index)
{
@ -232,7 +231,7 @@ WebGL2Context::GetTransformFeedbackVarying(WebGLProgram* program, GLuint index)
MakeContextCurrent();
GLint len = 0;
GLuint progname = program->GLName();
GLuint progname = program->mGLName;
gl->fGetProgramiv(progname, LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &len);
if (!len)
return nullptr;
@ -245,10 +244,13 @@ WebGL2Context::GetTransformFeedbackVarying(WebGLProgram* program, GLuint index)
if (len == 0 || tfsize == 0 || tftype == 0)
return nullptr;
// TODO(djg): Reverse lookup of name
// nsCString reverseMappedName;
// prog->ReverveMapIdentifier(nsDependentCString(name), &reverseMappedName);
MOZ_CRASH("todo");
/*
// Reverse lookup of name
nsCString reverseMappedName;
prog->ReverveMapIdentifier(nsDependentCString(name), &reverseMappedName);
nsRefPtr<WebGLActiveInfo> result = new WebGLActiveInfo(tfsize, tftype, nsDependentCString(name.get()));
return result.forget();
*/
}

View File

@ -396,7 +396,7 @@ WebGL2Context::GetUniformIndices(WebGLProgram* program,
if (!uniformNames.Length())
return;
GLuint progname = program->GLName();
GLuint progname = program->mGLName;
size_t count = uniformNames.Length();
nsTArray<GLuint>& arr = retval.SetValue();
@ -431,7 +431,7 @@ WebGL2Context::GetActiveUniforms(WebGLProgram* program,
if (!count)
return;
GLuint progname = program->GLName();
GLuint progname = program->mGLName;
nsTArray<GLint>& arr = retval.SetValue();
arr.SetLength(count);
@ -450,17 +450,14 @@ WebGL2Context::GetUniformBlockIndex(WebGLProgram* program,
if (!ValidateObject("getUniformBlockIndex: program", program))
return 0;
if (!ValidateGLSLVariableName(uniformBlockName, "getUniformBlockIndex"))
return 0;
// Leave this unchecked for now.
NS_LossyConvertUTF16toASCII cname(uniformBlockName);
nsCString mappedName;
program->MapIdentifier(cname, &mappedName);
const NS_LossyConvertUTF16toASCII cname(uniformBlockName);
GLuint progname = program->GLName();
GLuint progname = program->mGLName;
MakeContextCurrent();
return gl->fGetUniformBlockIndex(progname, mappedName.get());
return gl->fGetUniformBlockIndex(progname, cname.BeginReading());
}
static bool
@ -501,7 +498,7 @@ WebGL2Context::GetActiveUniformBlockParameter(JSContext* cx, WebGLProgram* progr
if (!ValidateObject("getActiveUniformBlockParameter: program", program))
return;
GLuint progname = program->GLName();
GLuint progname = program->mGLName;
GLint param = 0;
MakeContextCurrent();
@ -551,7 +548,7 @@ WebGL2Context::GetActiveUniformBlockName(WebGLProgram* program, GLuint uniformBl
if (!ValidateObject("getActiveUniformBlockName: program", program))
return;
GLuint progname = program->GLName();
GLuint progname = program->mGLName;
GLchar nameBuffer[WEBGL_MAX_UNIFORM_BLOCK_NAME_LENGTH];
GLsizei length = 0;
@ -574,7 +571,7 @@ WebGL2Context::UniformBlockBinding(WebGLProgram* program, GLuint uniformBlockInd
if (!ValidateObject("uniformBlockBinding: program", program))
return;
GLuint progname = program->GLName();
GLuint progname = program->mGLName;
MakeContextCurrent();
gl->fUniformBlockBinding(progname, uniformBlockIndex, uniformBlockBinding);

View File

@ -6,16 +6,95 @@
#include "WebGLActiveInfo.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "WebGLContext.h"
#include "WebGLTexture.h"
namespace mozilla {
bool
WebGLActiveInfo::WrapObject(JSContext* aCx,
JS::MutableHandle<JSObject*> aReflector)
static uint8_t
ElemSizeFromType(GLenum elemType)
{
return dom::WebGLActiveInfoBinding::Wrap(aCx, this, aReflector);
switch (elemType) {
case LOCAL_GL_BOOL:
case LOCAL_GL_FLOAT:
case LOCAL_GL_INT:
case LOCAL_GL_INT_SAMPLER_2D:
case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
case LOCAL_GL_INT_SAMPLER_3D:
case LOCAL_GL_INT_SAMPLER_CUBE:
case LOCAL_GL_SAMPLER_2D:
case LOCAL_GL_SAMPLER_2D_ARRAY:
case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
case LOCAL_GL_SAMPLER_2D_SHADOW:
case LOCAL_GL_SAMPLER_CUBE:
case LOCAL_GL_SAMPLER_CUBE_SHADOW:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
return 1;
case LOCAL_GL_BOOL_VEC2:
case LOCAL_GL_FLOAT_VEC2:
case LOCAL_GL_INT_VEC2:
return 2;
case LOCAL_GL_BOOL_VEC3:
case LOCAL_GL_FLOAT_VEC3:
case LOCAL_GL_INT_VEC3:
return 3;
case LOCAL_GL_BOOL_VEC4:
case LOCAL_GL_FLOAT_MAT2:
case LOCAL_GL_FLOAT_VEC4:
case LOCAL_GL_INT_VEC4:
return 4;
case LOCAL_GL_FLOAT_MAT2x3:
case LOCAL_GL_FLOAT_MAT3x2:
return 6;
case LOCAL_GL_FLOAT_MAT2x4:
case LOCAL_GL_FLOAT_MAT4x2:
return 8;
case LOCAL_GL_FLOAT_MAT3:
return 9;
case LOCAL_GL_FLOAT_MAT3x4:
case LOCAL_GL_FLOAT_MAT4x3:
return 12;
case LOCAL_GL_FLOAT_MAT4:
return 16;
default:
MOZ_CRASH("Bad `elemType`.");
}
}
WebGLActiveInfo::WebGLActiveInfo(WebGLContext* webgl, GLint elemCount, GLenum elemType,
bool isArray, const nsACString& baseUserName,
const nsACString& baseMappedName)
: mWebGL(webgl)
, mElemCount(elemCount)
, mElemType(elemType)
, mBaseUserName(baseUserName)
, mIsArray(isArray)
, mElemSize(ElemSizeFromType(elemType))
, mBaseMappedName(baseMappedName)
{ }
////////////////////////////////////////////////////////////////////////////////
JSObject*
WebGLActiveInfo::WrapObject(JSContext* js)
{
return dom::WebGLActiveInfoBinding::Wrap(js, this);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLActiveInfo)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLActiveInfo, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLActiveInfo, Release)
} // namespace mozilla

View File

@ -6,48 +6,87 @@
#ifndef WEBGL_ACTIVE_INFO_H_
#define WEBGL_ACTIVE_INFO_H_
#include "GLDefs.h"
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS
#include "nsISupportsImpl.h" // NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING
#include "nsString.h"
#include "WebGLObjectModel.h"
#include "nsWrapperCache.h"
namespace mozilla {
class WebGLContext;
class WebGLActiveInfo MOZ_FINAL
: public nsWrapperCache
{
public:
WebGLActiveInfo(GLint size, GLenum type, const nsACString& name)
: mSize(size)
, mType(type)
, mName(NS_ConvertASCIItoUTF16(name))
{}
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLActiveInfo)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLActiveInfo)
virtual JSObject* WrapObject(JSContext* js) MOZ_OVERRIDE;
WebGLContext* GetParentObject() const {
return mWebGL;
}
WebGLContext* const mWebGL;
// ActiveInfo state:
const GLint mElemCount; // `size`
const GLenum mElemType; // `type`
const nsCString mBaseUserName; // `name`, but ASCII, and without any final "[0]".
// Not actually part of ActiveInfo:
const bool mIsArray;
const uint8_t mElemSize;
const nsCString mBaseMappedName; // Without any final "[0]".
WebGLActiveInfo(WebGLContext* webgl, GLint elemCount, GLenum elemType, bool isArray,
const nsACString& baseUserName, const nsACString& baseMappedName);
/* GLES 2.0.25, p33:
* This command will return as much information about active
* attributes as possible. If no information is available, length will
* be set to zero and name will be an empty string. This situation
* could arise if GetActiveAttrib is issued after a failed link.
*
* It's the same for GetActiveUniform.
*/
static WebGLActiveInfo* CreateInvalid(WebGLContext* webgl) {
return new WebGLActiveInfo(webgl);
}
// WebIDL attributes
GLint Size() const {
return mSize;
return mElemCount;
}
GLenum Type() const {
return mType;
return mElemType;
}
void GetName(nsString& retval) const {
retval = mName;
CopyASCIItoUTF16(mBaseUserName, retval);
if (mIsArray)
retval.AppendLiteral("[0]");
}
bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
NS_INLINE_DECL_REFCOUNTING(WebGLActiveInfo)
private:
// Private destructor, to discourage deletion outside of Release():
~WebGLActiveInfo()
{
}
explicit WebGLActiveInfo(WebGLContext* webgl)
: mWebGL(webgl)
, mElemCount(0)
, mElemType(0)
, mBaseUserName("")
, mIsArray(false)
, mElemSize(0)
, mBaseMappedName("")
{ }
GLint mSize;
GLenum mType;
nsString mName;
// Private destructor, to discourage deletion outside of Release():
~WebGLActiveInfo() { }
};
} // namespace mozilla

View File

@ -48,7 +48,7 @@ public:
WebGLContext* GetParentObject() const {
return Context();
};
}
virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;

View File

@ -202,6 +202,7 @@ WebGLContextOptions::WebGLContextOptions()
WebGLContext::WebGLContext()
: WebGLContextUnchecked(nullptr)
, mBypassShaderValidation(false)
, mNeedsFakeNoAlpha(false)
{
mGeneration = 0;
@ -215,8 +216,6 @@ WebGLContext::WebGLContext()
mPixelStorePremultiplyAlpha = false;
mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL;
mShaderValidation = true;
mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
mVertexAttrib0Vector[0] = 0;
@ -330,6 +329,7 @@ WebGLContext::DestroyResourcesAndContext()
mBoundTransformFeedbackBuffer = nullptr;
mBoundUniformBuffer = nullptr;
mCurrentProgram = nullptr;
mActiveProgramLinkInfo = nullptr;
mBoundDrawFramebuffer = nullptr;
mBoundReadFramebuffer = nullptr;
mActiveOcclusionQuery = nullptr;

View File

@ -19,6 +19,7 @@
#include "WebGLObjectModel.h"
#include "WebGLRenderbuffer.h"
#include "WebGLTexture.h"
#include "WebGLShaderValidator.h"
#include "WebGLStrongTypes.h"
#include <stdarg.h>
@ -96,6 +97,10 @@ namespace gfx {
class SourceSurface;
}
namespace webgl {
struct LinkedProgramInfo;
}
WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow);
@ -375,7 +380,9 @@ public:
void ClearStencil(GLint v);
void ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b, WebGLboolean a);
void CompileShader(WebGLShader* shader);
void CompressedTexImage2D(GLenum texImageTarget, GLint level,
void CompileShaderANGLE(WebGLShader* shader);
void CompileShaderBypass(WebGLShader* shader, const nsCString& shaderSource);
void CompressedTexImage2D(GLenum target, GLint level,
GLenum internalformat, GLsizei width,
GLsizei height, GLint border,
const dom::ArrayBufferView& view);
@ -837,8 +844,10 @@ public:
const float* data);
void UseProgram(WebGLProgram* prog);
bool ValidateAttribArraySetter(const char* name, uint32_t count,
uint32_t arrayLength);
bool ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName);
bool ValidateUniformSetter(WebGLUniformLocation* loc, uint8_t setterSize,
GLenum setterType, const char* info,
GLuint* out_rawLoc);
@ -1118,8 +1127,9 @@ protected:
GLenum mUnderlyingGLError;
GLenum GetAndFlushUnderlyingGLErrors();
// whether shader validation is supported
bool mShaderValidation;
bool mBypassShaderValidation;
webgl::ShaderValidator* CreateShaderValidator(GLenum shaderType) const;
// some GL constants
int32_t mGLMaxVertexAttribs;
@ -1144,6 +1154,10 @@ public:
return mGLMaxVertexAttribs;
}
GLuint GLMaxTextureUnits() const {
return mGLMaxTextureUnits;
}
bool IsFormatValidForFB(GLenum sizedFormat) const;
@ -1225,10 +1239,6 @@ protected:
WebGLintptr byteOffset, const char* info);
bool ValidateStencilParamsForDrawCall();
bool ValidateGLSLVariableName(const nsAString& name, const char* info);
bool ValidateGLSLCharacter(char16_t c);
bool ValidateGLSLString(const nsAString& string, const char* info);
bool ValidateCopyTexImage(GLenum internalFormat, WebGLTexImageFunc func,
WebGLTexDimensions dims);
@ -1275,6 +1285,10 @@ protected:
WebGLTexImageFunc func,
WebGLTexDimensions dims);
bool ValidateUniformLocationForProgram(WebGLUniformLocation* location,
WebGLProgram* program,
const char* funcName);
void Invalidate();
void DestroyResourcesAndContext();
@ -1403,6 +1417,7 @@ protected:
nsTArray<WebGLRefPtr<WebGLTexture> > mBound3DTextures;
WebGLRefPtr<WebGLProgram> mCurrentProgram;
RefPtr<const webgl::LinkedProgramInfo> mActiveProgramLinkInfo;
uint32_t mMaxFramebufferColorAttachments;
@ -1595,7 +1610,7 @@ WebGLContext::ValidateObjectAssumeNonNull(const char* info, ObjectType* object)
return false;
if (object->IsDeleted()) {
ErrorInvalidValue("%s: deleted object passed as argument", info);
ErrorInvalidValue("%s: Deleted object passed as argument.", info);
return false;
}

View File

@ -14,7 +14,6 @@
#include "WebGLRenderbuffer.h"
#include "WebGLShader.h"
#include "WebGLTexture.h"
#include "WebGLUniformInfo.h"
#include "WebGLVertexArray.h"
#include "WebGLVertexAttribData.h"
@ -410,11 +409,15 @@ void WebGLContext::Draw_cleanup()
bool
WebGLContext::ValidateBufferFetching(const char* info)
{
MOZ_ASSERT(mCurrentProgram);
// Note that mCurrentProgram->IsLinked() is NOT GUARANTEED.
MOZ_ASSERT(mActiveProgramLinkInfo);
#ifdef DEBUG
GLint currentProgram = 0;
MakeContextCurrent();
gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram);
MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->GLName(),
MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->mGLName,
"WebGL: current program doesn't agree with GL state");
#endif
@ -441,7 +444,7 @@ WebGLContext::ValidateBufferFetching(const char* info)
// If the attrib is not in use, then we don't have to validate
// it, just need to make sure that the binding is non-null.
if (!mCurrentProgram->IsAttribInUse(i))
if (!mActiveProgramLinkInfo->HasActiveAttrib(i))
continue;
// the base offset
@ -498,12 +501,13 @@ WebGLVertexAttrib0Status
WebGLContext::WhatDoesVertexAttrib0Need()
{
MOZ_ASSERT(mCurrentProgram);
MOZ_ASSERT(mActiveProgramLinkInfo);
// work around Mac OSX crash, see bug 631420
#ifdef XP_MACOSX
if (gl->WorkAroundDriverBugs() &&
mBoundVertexArray->IsAttribArrayEnabled(0) &&
!mCurrentProgram->IsAttribInUse(0))
!mActiveProgramLinkInfo->HasActiveAttrib(0))
{
return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
}
@ -515,7 +519,7 @@ WebGLContext::WhatDoesVertexAttrib0Need()
return WebGLVertexAttrib0Status::Default;
}
return mCurrentProgram->IsAttribInUse(0)
return mActiveProgramLinkInfo->HasActiveAttrib(0)
? WebGLVertexAttrib0Status::EmulatedInitializedArray
: WebGLVertexAttrib0Status::EmulatedUninitializedArray;
}

File diff suppressed because it is too large Load Diff

View File

@ -1043,7 +1043,7 @@ WebGLContext::AssertCachedBindings()
AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound);
}
GLuint bound = mCurrentProgram ? mCurrentProgram->GLName() : 0;
GLuint bound = mCurrentProgram ? mCurrentProgram->mGLName : 0;
AssertUintParamCorrect(gl, LOCAL_GL_CURRENT_PROGRAM, bound);
// Textures

View File

@ -22,6 +22,7 @@
#include "WebGLShader.h"
#include "WebGLTexture.h"
#include "WebGLUniformLocation.h"
#include "WebGLValidateStrings.h"
#include "WebGLVertexArray.h"
#include "WebGLVertexAttribData.h"
@ -367,50 +368,6 @@ WebGLContext::ValidateDrawModeEnum(GLenum mode, const char* info)
}
}
bool
WebGLContext::ValidateGLSLVariableName(const nsAString& name, const char* info)
{
if (name.IsEmpty())
return false;
const uint32_t maxSize = 256;
if (name.Length() > maxSize) {
ErrorInvalidValue("%s: Identifier is %d characters long, exceeds the"
" maximum allowed length of %d characters.", info,
name.Length(), maxSize);
return false;
}
if (!ValidateGLSLString(name, info))
return false;
nsString prefix1 = NS_LITERAL_STRING("webgl_");
nsString prefix2 = NS_LITERAL_STRING("_webgl_");
if (Substring(name, 0, prefix1.Length()).Equals(prefix1) ||
Substring(name, 0, prefix2.Length()).Equals(prefix2))
{
ErrorInvalidOperation("%s: String contains a reserved GLSL prefix.",
info);
return false;
}
return true;
}
bool WebGLContext::ValidateGLSLString(const nsAString& string, const char* info)
{
for (uint32_t i = 0; i < string.Length(); ++i) {
if (!ValidateGLSLCharacter(string.CharAt(i))) {
ErrorInvalidValue("%s: String contains the illegal character"
" '%d'.", info, string.CharAt(i));
return false;
}
}
return true;
}
/**
* Return true if the framebuffer attachment is valid. Attachment must
* be one of depth/stencil/depth_stencil/color attachment.
@ -1512,163 +1469,37 @@ WebGLContext::ValidateTexImage(TexImageTarget texImageTarget, GLint level,
}
bool
WebGLContext::ValidateUniformLocation(const char* info,
WebGLUniformLocation* loc)
WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName)
{
if (!ValidateObjectAllowNull(info, loc))
return false;
/* GLES 2.0.25, p38:
* If the value of location is -1, the Uniform* commands will silently
* ignore the data passed in, and the current uniform values will not be
* changed.
*/
if (!loc)
return false;
// The need to check specifically for !mCurrentProgram here is explained in
// bug 657556.
if (!ValidateObject(funcName, loc))
return false;
if (!mCurrentProgram) {
ErrorInvalidOperation("%s: No program is currently bound.", info);
ErrorInvalidOperation("%s: No program is currently bound.", funcName);
return false;
}
if (mCurrentProgram != loc->Program()) {
ErrorInvalidOperation("%s: This uniform location doesn't correspond to"
" the current program.", info);
return false;
}
if (mCurrentProgram->Generation() != loc->ProgramGeneration()) {
ErrorInvalidOperation("%s: This uniform location is obsolete since the"
" program has been relinked.", info);
return false;
}
return true;
return loc->ValidateForProgram(mCurrentProgram, this, funcName);
}
bool
WebGLContext::ValidateSamplerUniformSetter(const char* info,
WebGLUniformLocation* loc,
GLint value)
{
if (loc->Info().type != LOCAL_GL_SAMPLER_2D &&
loc->Info().type != LOCAL_GL_SAMPLER_CUBE)
{
return true;
}
if (value >= 0 && value < mGLMaxTextureUnits)
return true;
ErrorInvalidValue("%s: This uniform location is a sampler, but %d is not a"
" valid texture unit.", info, value);
return false;
}
bool
WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t cnt,
WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t setterElemSize,
uint32_t arrayLength)
{
if (IsContextLost())
return false;
if (arrayLength < cnt) {
ErrorInvalidOperation("%s: Array must be >= %d elements.", name, cnt);
return false;
}
return true;
}
static bool
IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType)
{
switch (uniformType) {
case LOCAL_GL_BOOL:
case LOCAL_GL_BOOL_VEC2:
case LOCAL_GL_BOOL_VEC3:
case LOCAL_GL_BOOL_VEC4:
return true; // GLfloat(0.0) sets a bool to false.
case LOCAL_GL_INT:
case LOCAL_GL_INT_SAMPLER_2D:
case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
case LOCAL_GL_INT_SAMPLER_3D:
case LOCAL_GL_INT_SAMPLER_CUBE:
case LOCAL_GL_INT_VEC2:
case LOCAL_GL_INT_VEC3:
case LOCAL_GL_INT_VEC4:
case LOCAL_GL_SAMPLER_2D:
case LOCAL_GL_SAMPLER_2D_ARRAY:
case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
case LOCAL_GL_SAMPLER_2D_SHADOW:
case LOCAL_GL_SAMPLER_CUBE:
case LOCAL_GL_SAMPLER_CUBE_SHADOW:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
return setterType == LOCAL_GL_INT;
case LOCAL_GL_FLOAT:
case LOCAL_GL_FLOAT_MAT2:
case LOCAL_GL_FLOAT_MAT2x3:
case LOCAL_GL_FLOAT_MAT2x4:
case LOCAL_GL_FLOAT_MAT3:
case LOCAL_GL_FLOAT_MAT3x2:
case LOCAL_GL_FLOAT_MAT3x4:
case LOCAL_GL_FLOAT_MAT4:
case LOCAL_GL_FLOAT_MAT4x2:
case LOCAL_GL_FLOAT_MAT4x3:
case LOCAL_GL_FLOAT_VEC2:
case LOCAL_GL_FLOAT_VEC3:
case LOCAL_GL_FLOAT_VEC4:
return setterType == LOCAL_GL_FLOAT;
default:
MOZ_ASSERT(false); // should never get here
return false;
}
}
static bool
CheckUniformSizeAndType(WebGLContext& webgl, WebGLUniformLocation* loc,
uint8_t setterElemSize, GLenum setterType,
const char* info)
{
if (setterElemSize != loc->ElementSize()) {
webgl.ErrorInvalidOperation("%s: Bad uniform size: %i", info,
loc->ElementSize());
return false;
}
if (!IsUniformSetterTypeValid(setterType, loc->Info().type)) {
webgl.ErrorInvalidOperation("%s: Bad uniform type: %i", info,
loc->Info().type);
return false;
}
return true;
}
static bool
CheckUniformArrayLength(WebGLContext& webgl, WebGLUniformLocation* loc,
uint8_t setterElemSize, size_t setterArraySize,
const char* info)
{
if (setterArraySize == 0 ||
setterArraySize % setterElemSize)
{
webgl.ErrorInvalidValue("%s: expected an array of length a multiple of"
" %d, got an array of length %d.", info,
setterElemSize, setterArraySize);
return false;
}
if (!loc->Info().isArray &&
setterArraySize != setterElemSize)
{
webgl.ErrorInvalidOperation("%s: expected an array of length exactly %d"
" (since this uniform is not an array"
" uniform), got an array of length %d.",
info, setterElemSize, setterArraySize);
if (arrayLength < setterElemSize) {
ErrorInvalidOperation("%s: Array must have >= %d elements.", name,
setterElemSize);
return false;
}
@ -1678,18 +1509,18 @@ CheckUniformArrayLength(WebGLContext& webgl, WebGLUniformLocation* loc,
bool
WebGLContext::ValidateUniformSetter(WebGLUniformLocation* loc,
uint8_t setterElemSize, GLenum setterType,
const char* info, GLuint* out_rawLoc)
const char* funcName, GLuint* out_rawLoc)
{
if (IsContextLost())
return false;
if (!ValidateUniformLocation(info, loc))
if (!ValidateUniformLocation(loc, funcName))
return false;
if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info))
if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName))
return false;
*out_rawLoc = loc->Location();
*out_rawLoc = loc->mLoc;
return true;
}
@ -1698,27 +1529,24 @@ WebGLContext::ValidateUniformArraySetter(WebGLUniformLocation* loc,
uint8_t setterElemSize,
GLenum setterType,
size_t setterArraySize,
const char* info,
const char* funcName,
GLuint* const out_rawLoc,
GLsizei* const out_numElementsToUpload)
{
if (IsContextLost())
return false;
if (!ValidateUniformLocation(info, loc))
if (!ValidateUniformLocation(loc, funcName))
return false;
if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info))
if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName))
return false;
if (!CheckUniformArrayLength(*this, loc, setterElemSize, setterArraySize,
info))
{
if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, this, funcName))
return false;
}
*out_rawLoc = loc->Location();
*out_numElementsToUpload = std::min((size_t)loc->Info().arraySize,
*out_rawLoc = loc->mLoc;
*out_numElementsToUpload = std::min((size_t)loc->mActiveInfo->mElemCount,
setterArraySize / setterElemSize);
return true;
}
@ -1729,7 +1557,7 @@ WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
GLenum setterType,
size_t setterArraySize,
bool setterTranspose,
const char* info,
const char* funcName,
GLuint* const out_rawLoc,
GLsizei* const out_numElementsToUpload)
{
@ -1738,25 +1566,22 @@ WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
if (IsContextLost())
return false;
if (!ValidateUniformLocation(info, loc))
if (!ValidateUniformLocation(loc, funcName))
return false;
if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info))
if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName))
return false;
if (!CheckUniformArrayLength(*this, loc, setterElemSize, setterArraySize,
info))
{
if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, this, funcName))
return false;
}
if (setterTranspose) {
ErrorInvalidValue("%s: `transpose` must be false.", info);
ErrorInvalidValue("%s: `transpose` must be false.", funcName);
return false;
}
*out_rawLoc = loc->Location();
*out_numElementsToUpload = std::min((size_t)loc->Info().arraySize,
*out_rawLoc = loc->mLoc;
*out_numElementsToUpload = std::min((size_t)loc->mActiveInfo->mElemCount,
setterArraySize / setterElemSize);
return true;
}
@ -2095,15 +1920,13 @@ WebGLContext::InitAndValidateGL()
// Check the shader validator pref
NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
mShaderValidation = Preferences::GetBool("webgl.shader_validator",
mShaderValidation);
mBypassShaderValidation = Preferences::GetBool("webgl.bypass-shader-validation",
mBypassShaderValidation);
// initialize shader translator
if (mShaderValidation) {
if (!ShInitialize()) {
GenerateWarning("GLSL translator initialization failed!");
return false;
}
if (!ShInitialize()) {
GenerateWarning("GLSL translator initialization failed!");
return false;
}
// Mesa can only be detected with the GL_VERSION string, of the form

View File

@ -13,7 +13,6 @@
#include "WebGLRenderbuffer.h"
#include "WebGLShader.h"
#include "WebGLTexture.h"
#include "WebGLUniformInfo.h"
#include "WebGLVertexArray.h"
#include "WebGLVertexAttribData.h"

View File

@ -7,364 +7,691 @@
#include "GLContext.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "MurmurHash3.h"
#include "WebGLContext.h"
#include "WebGLShader.h"
#include "WebGLUniformLocation.h"
#include "WebGLValidateStrings.h"
namespace mozilla {
/** Takes an ASCII string like "foo[i]", turns it into "foo" and returns "[i]"
* in bracketPart.
*
* \param string input/output: The string to split, becomes the string without
* the bracket part.
* \param bracketPart output: Gets the bracket part.
*
* Notice that if there are multiple brackets like "foo[i].bar[j]", only the
* last bracket is split.
*/
/* If `name`: "foo[3]"
* Then returns true, with
* `out_baseName`: "foo"
* `out_isArray`: true
* `out_index`: 3
*
* If `name`: "foo"
* Then returns true, with
* `out_baseName`: "foo"
* `out_isArray`: false
* `out_index`: <not written>
*/
static bool
SplitLastSquareBracket(nsACString& string, nsCString& bracketPart)
ParseName(const nsCString& name, nsCString* const out_baseName,
bool* const out_isArray, size_t* const out_arrayIndex)
{
MOZ_ASSERT(bracketPart.IsEmpty(),
"SplitLastSquareBracket must be called with empty bracketPart"
" string.");
int32_t indexEnd = name.RFind("]");
if (indexEnd == -1 ||
(uint32_t)indexEnd != name.Length() - 1)
{
*out_baseName = name;
*out_isArray = false;
return true;
}
if (string.IsEmpty())
int32_t indexOpenBracket = name.RFind("[");
if (indexOpenBracket == -1)
return false;
char* string_start = string.BeginWriting();
char* s = string_start + string.Length() - 1;
if (*s != ']')
uint32_t indexStart = indexOpenBracket + 1;
uint32_t indexLen = indexEnd - indexStart;
if (indexLen == 0)
return false;
while (*s != '[' && s != string_start)
s--;
const nsAutoCString indexStr(Substring(name, indexStart, indexLen));
if (*s != '[')
nsresult errorcode;
int32_t indexNum = indexStr.ToInteger(&errorcode);
if (NS_FAILED(errorcode))
return false;
bracketPart.Assign(s);
*s = 0;
string.EndWriting();
string.SetLength(s - string_start);
if (indexNum < 0)
return false;
*out_baseName = StringHead(name, indexOpenBracket);
*out_isArray = true;
*out_arrayIndex = indexNum;
return true;
}
JSObject*
WebGLProgram::WrapObject(JSContext* cx) {
return dom::WebGLProgramBinding::Wrap(cx, this);
static void
AddActiveInfo(WebGLContext* webgl, GLint elemCount, GLenum elemType, bool isArray,
const nsACString& baseUserName, const nsACString& baseMappedName,
std::vector<nsRefPtr<WebGLActiveInfo>>* activeInfoList,
std::map<nsCString, const WebGLActiveInfo*>* infoLocMap)
{
nsRefPtr<WebGLActiveInfo> info = new WebGLActiveInfo(webgl, elemCount, elemType,
isArray, baseUserName,
baseMappedName);
activeInfoList->push_back(info);
infoLocMap->insert(std::make_pair(info->mBaseUserName, info.get()));
}
//#define DUMP_SHADERVAR_MAPPINGS
static TemporaryRef<const webgl::LinkedProgramInfo>
QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl)
{
RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
GLuint maxAttribLenWithNull = 0;
gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
(GLint*)&maxAttribLenWithNull);
if (maxAttribLenWithNull < 1)
maxAttribLenWithNull = 1;
GLuint maxUniformLenWithNull = 0;
gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH,
(GLint*)&maxUniformLenWithNull);
if (maxUniformLenWithNull < 1)
maxUniformLenWithNull = 1;
#ifdef DUMP_SHADERVAR_MAPPINGS
printf_stderr("maxAttribLenWithNull: %d\n", maxAttribLenWithNull);
printf_stderr("maxUniformLenWithNull: %d\n", maxUniformLenWithNull);
#endif
// Attribs
GLuint numActiveAttribs = 0;
gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES,
(GLint*)&numActiveAttribs);
for (GLuint i = 0; i < numActiveAttribs; i++) {
nsAutoCString mappedName;
mappedName.SetLength(maxAttribLenWithNull - 1);
GLsizei lengthWithoutNull = 0;
GLint elemCount = 0; // `size`
GLenum elemType = 0; // `type`
gl->fGetActiveAttrib(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
&elemCount, &elemType, mappedName.BeginWriting());
mappedName.SetLength(lengthWithoutNull);
// Collect ActiveInfos:
// Attribs can't be arrays, so we can skip some of the mess we have in the Uniform
// path.
nsDependentCString userName;
if (!prog->FindAttribUserNameByMappedName(mappedName, &userName))
userName.Rebind(mappedName, 0);
#ifdef DUMP_SHADERVAR_MAPPINGS
printf_stderr("[attrib %i] %s/%s\n", i, mappedName.BeginReading(),
userName.BeginReading());
printf_stderr(" lengthWithoutNull: %d\n", lengthWithoutNull);
#endif
const bool isArray = false;
AddActiveInfo(prog->Context(), elemCount, elemType, isArray, userName, mappedName,
&info->activeAttribs, &info->attribMap);
// Collect active locations:
GLint loc = gl->fGetAttribLocation(prog->mGLName, mappedName.BeginReading());
if (loc == -1)
MOZ_CRASH("Active attrib has no location.");
info->activeAttribLocs.insert(loc);
}
// Uniforms
const bool needsCheckForArrays = true;
GLuint numActiveUniforms = 0;
gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORMS,
(GLint*)&numActiveUniforms);
for (GLuint i = 0; i < numActiveUniforms; i++) {
nsAutoCString mappedName;
mappedName.SetLength(maxUniformLenWithNull - 1);
GLsizei lengthWithoutNull = 0;
GLint elemCount = 0; // `size`
GLenum elemType = 0; // `type`
gl->fGetActiveUniform(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
&elemCount, &elemType, mappedName.BeginWriting());
mappedName.SetLength(lengthWithoutNull);
nsAutoCString baseMappedName;
bool isArray;
size_t arrayIndex;
if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
MOZ_CRASH("Failed to parse `mappedName` received from driver.");
// Note that for good drivers, `isArray` should already be correct.
// However, if FindUniform succeeds, it will be validator-guaranteed correct.
nsAutoCString baseUserName;
if (!prog->FindUniformByMappedName(baseMappedName, &baseUserName, &isArray)) {
baseUserName = baseMappedName;
if (needsCheckForArrays && !isArray) {
// By GLES 3, GetUniformLocation("foo[0]") should return -1 if `foo` is
// not an array. Our current linux Try slaves return the location of `foo`
// anyways, though.
std::string mappedName = baseMappedName.BeginReading();
mappedName += "[0]";
GLint loc = gl->fGetUniformLocation(prog->mGLName, mappedName.c_str());
if (loc != -1)
isArray = true;
}
}
#ifdef DUMP_SHADERVAR_MAPPINGS
printf_stderr("[uniform %i] %s/%i/%s/%s\n", i, mappedName.BeginReading(),
(int)isArray, baseMappedName.BeginReading(),
baseUserName.BeginReading());
printf_stderr(" lengthWithoutNull: %d\n", lengthWithoutNull);
printf_stderr(" isArray: %d\n", (int)isArray);
#endif
AddActiveInfo(prog->Context(), elemCount, elemType, isArray, baseUserName,
baseMappedName, &info->activeUniforms, &info->uniformMap);
}
return info.forget();
}
////////////////////////////////////////////////////////////////////////////////
webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* aProg)
: prog(aProg)
{ }
////////////////////////////////////////////////////////////////////////////////
// WebGLProgram
static GLuint
CreateProgram(gl::GLContext* gl)
{
gl->MakeCurrent();
return gl->fCreateProgram();
}
WebGLProgram::WebGLProgram(WebGLContext* webgl)
: WebGLContextBoundObject(webgl)
, mLinkStatus(false)
, mGeneration(0)
, mIdentifierMap(new CStringMap)
, mIdentifierReverseMap(new CStringMap)
, mUniformInfoMap(new CStringToUniformInfoMap)
, mAttribMaxNameLength(0)
, mGLName(CreateProgram(webgl->GL()))
{
mContext->MakeContextCurrent();
mGLName = mContext->gl->fCreateProgram();
mContext->mPrograms.insertBack(this);
}
void
WebGLProgram::Delete()
{
DetachShaders();
mContext->MakeContextCurrent();
mContext->gl->fDeleteProgram(mGLName);
gl::GLContext* gl = mContext->GL();
gl->MakeCurrent();
gl->fDeleteProgram(mGLName);
mVertShader = nullptr;
mFragShader = nullptr;
mMostRecentLinkInfo = nullptr;
LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms);
}
bool
////////////////////////////////////////////////////////////////////////////////
// GL funcs
void
WebGLProgram::AttachShader(WebGLShader* shader)
{
if (ContainsShader(shader))
return false;
WebGLRefPtr<WebGLShader>* shaderSlot;
switch (shader->mType) {
case LOCAL_GL_VERTEX_SHADER:
shaderSlot = &mVertShader;
break;
case LOCAL_GL_FRAGMENT_SHADER:
shaderSlot = &mFragShader;
break;
default:
mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
return;
}
mAttachedShaders.AppendElement(shader);
if (*shaderSlot) {
if (shader == *shaderSlot) {
mContext->ErrorInvalidOperation("attachShader: `shader` is already attached.");
} else {
mContext->ErrorInvalidOperation("attachShader: Only one of each type of"
" shader may be attached to a program.");
}
return;
}
*shaderSlot = shader;
mContext->MakeContextCurrent();
mContext->gl->fAttachShader(GLName(), shader->GLName());
return true;
mContext->gl->fAttachShader(mGLName, shader->mGLName);
}
bool
void
WebGLProgram::BindAttribLocation(GLuint loc, const nsAString& name)
{
if (!ValidateGLSLVariableName(name, mContext, "bindAttribLocation"))
return;
if (loc >= mContext->MaxVertexAttribs()) {
mContext->ErrorInvalidValue("bindAttribLocation: `location` must be less than"
" MAX_VERTEX_ATTRIBS.");
return;
}
if (StringBeginsWith(name, NS_LITERAL_STRING("gl_"))) {
mContext->ErrorInvalidOperation("bindAttribLocation: Can't set the location of a"
" name that starts with 'gl_'.");
return;
}
NS_LossyConvertUTF16toASCII asciiName(name);
auto res = mBoundAttribLocs.insert(std::pair<nsCString, GLuint>(asciiName, loc));
const bool wasInserted = res.second;
if (!wasInserted) {
auto itr = res.first;
itr->second = loc;
}
}
void
WebGLProgram::DetachShader(WebGLShader* shader)
{
if (!mAttachedShaders.RemoveElement(shader))
return false;
MOZ_ASSERT(shader);
WebGLRefPtr<WebGLShader>* shaderSlot;
switch (shader->mType) {
case LOCAL_GL_VERTEX_SHADER:
shaderSlot = &mVertShader;
break;
case LOCAL_GL_FRAGMENT_SHADER:
shaderSlot = &mFragShader;
break;
default:
mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
return;
}
if (*shaderSlot != shader) {
mContext->ErrorInvalidOperation("detachShader: `shader` is not attached.");
return;
}
*shaderSlot = nullptr;
mContext->MakeContextCurrent();
mContext->gl->fDetachShader(GLName(), shader->GLName());
return true;
mContext->gl->fDetachShader(mGLName, shader->mGLName);
}
bool
WebGLProgram::HasAttachedShaderOfType(GLenum shaderType)
already_AddRefed<WebGLActiveInfo>
WebGLProgram::GetActiveAttrib(GLuint index) const
{
for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) {
if (mAttachedShaders[i] && mAttachedShaders[i]->ShaderType() == shaderType)
return true;
if (!mMostRecentLinkInfo) {
nsRefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
return ret.forget();
}
return false;
const auto& activeList = mMostRecentLinkInfo->activeAttribs;
if (index >= activeList.size()) {
mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
index, "ACTIVE_ATTRIBS", activeList.size());
return nullptr;
}
nsRefPtr<WebGLActiveInfo> ret = activeList[index];
return ret.forget();
}
bool
WebGLProgram::HasBadShaderAttached()
already_AddRefed<WebGLActiveInfo>
WebGLProgram::GetActiveUniform(GLuint index) const
{
for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) {
if (mAttachedShaders[i] && !mAttachedShaders[i]->CompileStatus())
return true;
if (!mMostRecentLinkInfo) {
nsRefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
return ret.forget();
}
return false;
}
const auto& activeList = mMostRecentLinkInfo->activeUniforms;
size_t
WebGLProgram::UpperBoundNumSamplerUniforms()
{
size_t numSamplerUniforms = 0;
for (size_t i = 0; i < mAttachedShaders.Length(); ++i) {
const WebGLShader* shader = mAttachedShaders[i];
if (!shader)
continue;
for (size_t j = 0; j < shader->mUniformInfos.Length(); ++j) {
WebGLUniformInfo u = shader->mUniformInfos[j];
if (u.type == LOCAL_GL_SAMPLER_2D ||
u.type == LOCAL_GL_SAMPLER_CUBE)
{
numSamplerUniforms += u.arraySize;
}
}
if (index >= activeList.size()) {
mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
index, "ACTIVE_UNIFORMS", activeList.size());
return nullptr;
}
return numSamplerUniforms;
nsRefPtr<WebGLActiveInfo> ret = activeList[index];
return ret.forget();
}
void
WebGLProgram::MapIdentifier(const nsACString& name,
nsCString* const out_mappedName)
WebGLProgram::GetAttachedShaders(nsTArray<nsRefPtr<WebGLShader>>* const out) const
{
MOZ_ASSERT(mIdentifierMap);
out->TruncateLength(0);
nsCString mutableName(name);
nsCString bracketPart;
bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
if (hadBracketPart)
mutableName.AppendLiteral("[0]");
if (mVertShader)
out->AppendElement(mVertShader);
if (mIdentifierMap->Get(mutableName, out_mappedName)) {
if (hadBracketPart) {
nsCString mappedBracketPart;
bool mappedHadBracketPart = SplitLastSquareBracket(*out_mappedName,
mappedBracketPart);
if (mappedHadBracketPart)
out_mappedName->Append(bracketPart);
}
return;
if (mFragShader)
out->AppendElement(mFragShader);
}
GLint
WebGLProgram::GetAttribLocation(const nsAString& userName_wide) const
{
if (!ValidateGLSLVariableName(userName_wide, mContext, "getAttribLocation"))
return -1;
if (!IsLinked()) {
mContext->ErrorInvalidOperation("getAttribLocation: `program` must be linked.");
return -1;
}
// Not found? We might be in the situation we have a uniform array name and
// the GL's glGetActiveUniform returned its name without [0], as is allowed
// by desktop GL but not in ES. Let's then try with [0].
mutableName.AppendLiteral("[0]");
if (mIdentifierMap->Get(mutableName, out_mappedName))
return;
const NS_LossyConvertUTF16toASCII userName(userName_wide);
/* Not found? Return name unchanged. This case happens e.g. on bad user
* input, or when we're not using identifier mapping, or if we didn't store
* an identifier in the map because e.g. its mapping is trivial. (as happens
* for short identifiers)
*/
out_mappedName->Assign(name);
const WebGLActiveInfo* info;
if (!LinkInfo()->FindAttrib(userName, &info))
return -1;
const nsCString& mappedName = info->mBaseMappedName;
gl::GLContext* gl = mContext->GL();
gl->MakeCurrent();
return gl->fGetAttribLocation(mGLName, mappedName.BeginReading());
}
void
WebGLProgram::ReverseMapIdentifier(const nsACString& name,
nsCString* const out_reverseMappedName)
WebGLProgram::GetProgramInfoLog(nsAString* const out) const
{
MOZ_ASSERT(mIdentifierReverseMap);
nsCString mutableName(name);
nsCString bracketPart;
bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
if (hadBracketPart)
mutableName.AppendLiteral("[0]");
if (mIdentifierReverseMap->Get(mutableName, out_reverseMappedName)) {
if (hadBracketPart) {
nsCString reverseMappedBracketPart;
bool reverseMappedHadBracketPart = SplitLastSquareBracket(*out_reverseMappedName,
reverseMappedBracketPart);
if (reverseMappedHadBracketPart)
out_reverseMappedName->Append(bracketPart);
}
return;
}
// Not found? We might be in the situation we have a uniform array name and
// the GL's glGetActiveUniform returned its name without [0], as is allowed
// by desktop GL but not in ES. Let's then try with [0].
mutableName.AppendLiteral("[0]");
if (mIdentifierReverseMap->Get(mutableName, out_reverseMappedName))
return;
/* Not found? Return name unchanged. This case happens e.g. on bad user
* input, or when we're not using identifier mapping, or if we didn't store
* an identifier in the map because e.g. its mapping is trivial. (as happens
* for short identifiers)
*/
out_reverseMappedName->Assign(name);
CopyASCIItoUTF16(mLinkLog, *out);
}
WebGLUniformInfo
WebGLProgram::GetUniformInfoForMappedIdentifier(const nsACString& name)
static GLint
GetProgramiv(gl::GLContext* gl, GLuint program, GLenum pname)
{
MOZ_ASSERT(mUniformInfoMap);
GLint ret = 0;
gl->fGetProgramiv(program, pname, &ret);
return ret;
}
nsCString mutableName(name);
nsCString bracketPart;
bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
// If there is a bracket, we're either an array or an entry in an array.
if (hadBracketPart)
mutableName.AppendLiteral("[0]");
JS::Value
WebGLProgram::GetProgramParameter(GLenum pname) const
{
gl::GLContext* gl = mContext->gl;
gl->MakeCurrent();
WebGLUniformInfo info;
mUniformInfoMap->Get(mutableName, &info);
// We don't check if that Get failed, as if it did, it left info with
// default values.
if (mContext->IsWebGL2()) {
switch (pname) {
case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
return JS::Int32Value(GetProgramiv(gl, mGLName, pname));
}
}
return info;
switch (pname) {
case LOCAL_GL_ATTACHED_SHADERS:
case LOCAL_GL_ACTIVE_UNIFORMS:
case LOCAL_GL_ACTIVE_ATTRIBUTES:
return JS::Int32Value(GetProgramiv(gl, mGLName, pname));
case LOCAL_GL_DELETE_STATUS:
return JS::BooleanValue(IsDeleteRequested());
case LOCAL_GL_LINK_STATUS:
return JS::BooleanValue(IsLinked());
case LOCAL_GL_VALIDATE_STATUS:
#ifdef XP_MACOSX
// See comment in ValidateProgram.
if (gl->WorkAroundDriverBugs())
return JS::BooleanValue(true);
#endif
return JS::BooleanValue(bool(GetProgramiv(gl, mGLName, pname)));
default:
mContext->ErrorInvalidEnumInfo("getProgramParameter: `pname`",
pname);
return JS::NullValue();
}
}
already_AddRefed<WebGLUniformLocation>
WebGLProgram::GetUniformLocation(const nsAString& userName_wide) const
{
if (!ValidateGLSLVariableName(userName_wide, mContext, "getUniformLocation"))
return nullptr;
if (!IsLinked()) {
mContext->ErrorInvalidOperation("getUniformLocation: `program` must be linked.");
return nullptr;
}
const NS_LossyConvertUTF16toASCII userName(userName_wide);
nsDependentCString baseUserName;
bool isArray;
size_t arrayIndex;
if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
return nullptr;
const WebGLActiveInfo* activeInfo;
if (!LinkInfo()->FindUniform(baseUserName, &activeInfo))
return nullptr;
const nsCString& baseMappedName = activeInfo->mBaseMappedName;
nsAutoCString mappedName(baseMappedName);
if (isArray) {
mappedName.AppendLiteral("[");
mappedName.AppendInt(uint32_t(arrayIndex));
mappedName.AppendLiteral("]");
}
gl::GLContext* gl = mContext->GL();
gl->MakeCurrent();
GLint loc = gl->fGetUniformLocation(mGLName, mappedName.BeginReading());
if (loc == -1)
return nullptr;
nsRefPtr<WebGLUniformLocation> locObj = new WebGLUniformLocation(mContext, LinkInfo(),
loc, activeInfo);
return locObj.forget();
}
bool
WebGLProgram::UpdateInfo()
WebGLProgram::LinkProgram()
{
mAttribMaxNameLength = 0;
for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
mAttribMaxNameLength = std::max(mAttribMaxNameLength,
mAttachedShaders[i]->mAttribMaxNameLength);
}
mContext->InvalidateBufferFetching(); // we do it early in this function
// as some of the validation below changes program state
GLint attribCount;
mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &attribCount);
mLinkLog.Truncate();
mMostRecentLinkInfo = nullptr;
if (!mAttribsInUse.SetLength(mContext->mGLMaxVertexAttribs)) {
mContext->ErrorOutOfMemory("updateInfo: Out of memory to allocate %d"
" attribs.", mContext->mGLMaxVertexAttribs);
if (!mVertShader || !mVertShader->IsCompiled()) {
mLinkLog.AssignLiteral("Must have a compiled vertex shader attached.");
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
return false;
}
for (size_t i = 0; i < mAttribsInUse.Length(); i++)
mAttribsInUse[i] = false;
if (!mFragShader || !mFragShader->IsCompiled()) {
mLinkLog.AssignLiteral("Must have an compiled fragment shader attached.");
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
return false;
}
nsAutoArrayPtr<char> nameBuf(new char[mAttribMaxNameLength]);
if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog)) {
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
return false;
}
for (int i = 0; i < attribCount; ++i) {
GLint attrnamelen;
GLint attrsize;
GLenum attrtype;
mContext->gl->fGetActiveAttrib(mGLName, i, mAttribMaxNameLength,
&attrnamelen, &attrsize, &attrtype,
nameBuf);
if (attrnamelen > 0) {
GLint loc = mContext->gl->fGetAttribLocation(mGLName, nameBuf);
MOZ_ASSERT(loc >= 0, "Major oops in managing the attributes of a"
" WebGL program.");
if (loc < mContext->mGLMaxVertexAttribs) {
mAttribsInUse[loc] = true;
} else {
mContext->GenerateWarning("Program exceeds MAX_VERTEX_ATTRIBS.");
return false;
}
gl::GLContext* gl = mContext->gl;
gl->MakeCurrent();
// Bug 777028: Mesa can't handle more than 16 samplers per program,
// counting each array entry.
size_t numSamplerUniforms_upperBound = mVertShader->CalcNumSamplerUniforms() +
mFragShader->CalcNumSamplerUniforms();
if (gl->WorkAroundDriverBugs() &&
mContext->mIsMesa &&
numSamplerUniforms_upperBound > 16)
{
mLinkLog.AssignLiteral("Programs with more than 16 samplers are disallowed on"
" Mesa drivers to avoid crashing.");
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
return false;
}
// Bind the attrib locations.
// This can't be done trivially, because we have to deal with mapped attrib names.
for (auto itr = mBoundAttribLocs.begin(); itr != mBoundAttribLocs.end(); ++itr) {
const nsCString& name = itr->first;
GLuint index = itr->second;
mVertShader->BindAttribLocation(mGLName, name, index);
}
if (LinkAndUpdate())
return true;
// Failed link.
if (mContext->ShouldGenerateWarnings()) {
// report shader/program infoLogs as warnings.
// note that shader compilation errors can be deferred to linkProgram,
// which is why we can't do anything in compileShader. In practice we could
// report in compileShader the translation errors generated by ANGLE,
// but it seems saner to keep a single way of obtaining shader infologs.
if (!mLinkLog.IsEmpty()) {
mContext->GenerateWarning("linkProgram: Failed to link, leaving the following"
" log:\n%s\n",
mLinkLog.BeginReading());
}
}
// nsAutoPtr will delete old version first
mIdentifierMap = new CStringMap;
mIdentifierReverseMap = new CStringMap;
mUniformInfoMap = new CStringToUniformInfoMap;
for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
// Loop through ATTRIBUTES
for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) {
const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j];
return false;
}
// FORWARD MAPPING
mIdentifierMap->Put(attrib.original, attrib.mapped);
// REVERSE MAPPING
mIdentifierReverseMap->Put(attrib.mapped, attrib.original);
}
// Loop through UNIFORMS
for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) {
// Add the uniforms name mapping to mIdentifier[Reverse]Map
const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j];
// FOWARD MAPPING
mIdentifierMap->Put(uniform.original, uniform.mapped);
// REVERSE MAPPING
mIdentifierReverseMap->Put(uniform.mapped, uniform.original);
// Add uniform info to mUniformInfoMap
const WebGLUniformInfo& info = mAttachedShaders[i]->mUniformInfos[j];
mUniformInfoMap->Put(uniform.mapped, info);
}
bool
WebGLProgram::UseProgram() const
{
if (!mMostRecentLinkInfo) {
mContext->ErrorInvalidOperation("useProgram: Program has not been successfully"
" linked.");
return false;
}
mActiveAttribMap.clear();
mContext->MakeContextCurrent();
GLint numActiveAttrs = 0;
mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &numActiveAttrs);
// Spec says the maximum attrib name length is 256 chars, so this is
// sufficient to hold any attrib name.
char attrName[257];
GLint dummySize;
GLenum dummyType;
for (GLint i = 0; i < numActiveAttrs; i++) {
mContext->gl->fGetActiveAttrib(mGLName, i, 257, nullptr, &dummySize,
&dummyType, attrName);
GLint attrLoc = mContext->gl->fGetAttribLocation(mGLName, attrName);
MOZ_ASSERT(attrLoc >= 0);
mActiveAttribMap.insert(std::make_pair(attrLoc, nsCString(attrName)));
}
mContext->InvalidateBufferFetching();
mContext->gl->fUseProgram(mGLName);
return true;
}
/*static*/ uint64_t
WebGLProgram::IdentifierHashFunction(const char* ident, size_t size)
void
WebGLProgram::ValidateProgram() const
{
uint64_t outhash[2];
// NB: we use the x86 function everywhere, even though it's suboptimal perf
// on x64. They return different results; not sure if that's a requirement.
MurmurHash3_x86_128(ident, size, 0, &outhash[0]);
return outhash[0];
mContext->MakeContextCurrent();
gl::GLContext* gl = mContext->gl;
#ifdef XP_MACOSX
// See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed
// with Mac OS 10.6.7.
if (gl->WorkAroundDriverBugs()) {
mContext->GenerateWarning("validateProgram: Implemented as a no-op on"
" Mac to work around crashes.");
return;
}
#endif
gl->fValidateProgram(mGLName);
}
/*static*/ void
WebGLProgram::HashMapIdentifier(const nsACString& name,
nsCString* const out_hashedName)
////////////////////////////////////////////////////////////////////////////////
bool
WebGLProgram::LinkAndUpdate()
{
uint64_t hash = IdentifierHashFunction(name.BeginReading(), name.Length());
out_hashedName->Truncate();
// This MUST MATCH HASHED_NAME_PREFIX from
// angle/src/compiler/translator/HashNames.h .
out_hashedName->AppendPrintf("webgl_%llx", hash);
mMostRecentLinkInfo = nullptr;
gl::GLContext* gl = mContext->gl;
gl->fLinkProgram(mGLName);
// Grab the program log.
GLuint logLenWithNull = 0;
gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&logLenWithNull);
if (logLenWithNull > 1) {
mLinkLog.SetLength(logLenWithNull - 1);
gl->fGetProgramInfoLog(mGLName, logLenWithNull, nullptr, mLinkLog.BeginWriting());
} else {
mLinkLog.SetLength(0);
}
GLint ok = 0;
gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
if (!ok)
return false;
mMostRecentLinkInfo = QueryProgramInfo(this, gl);
MOZ_ASSERT(mMostRecentLinkInfo);
if (!mMostRecentLinkInfo)
mLinkLog.AssignLiteral("Failed to gather program info.");
return mMostRecentLinkInfo;
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mAttachedShaders)
bool
WebGLProgram::FindAttribUserNameByMappedName(const nsACString& mappedName,
nsDependentCString* const out_userName) const
{
if (mVertShader->FindAttribUserNameByMappedName(mappedName, out_userName))
return true;
return false;
}
bool
WebGLProgram::FindUniformByMappedName(const nsACString& mappedName,
nsCString* const out_userName,
bool* const out_isArray) const
{
if (mVertShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
return true;
if (mFragShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
JSObject*
WebGLProgram::WrapObject(JSContext* js)
{
return dom::WebGLProgramBinding::Wrap(js, this);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mVertShader, mFragShader)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release)

View File

@ -9,19 +9,74 @@
#include <map>
#include "mozilla/CheckedInt.h"
#include "mozilla/LinkedList.h"
#include "nsString.h"
#include "nsWrapperCache.h"
#include <set>
#include <vector>
#include "WebGLObjectModel.h"
#include "WebGLShader.h"
#include "WebGLUniformInfo.h"
namespace mozilla {
class WebGLActiveInfo;
class WebGLProgram;
class WebGLUniformLocation;
namespace webgl {
struct LinkedProgramInfo MOZ_FINAL
: public RefCounted<LinkedProgramInfo>
, public SupportsWeakPtr<LinkedProgramInfo>
{
MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
WebGLProgram* const prog;
std::vector<nsRefPtr<WebGLActiveInfo>> activeAttribs;
std::vector<nsRefPtr<WebGLActiveInfo>> activeUniforms;
// Needed for Get{Attrib,Uniform}Location. The keys for these are non-mapped
// user-facing `GLActiveInfo::name`s, without any final "[0]".
std::map<nsCString, const WebGLActiveInfo*> attribMap;
std::map<nsCString, const WebGLActiveInfo*> uniformMap;
// Needed for draw call validation.
std::set<GLuint> activeAttribLocs;
explicit LinkedProgramInfo(WebGLProgram* aProg);
bool FindAttrib(const nsCString& baseUserName,
const WebGLActiveInfo** const out_activeInfo) const
{
auto itr = attribMap.find(baseUserName);
if (itr == attribMap.end())
return false;
*out_activeInfo = itr->second;
return true;
}
bool FindUniform(const nsCString& baseUserName,
const WebGLActiveInfo** const out_activeInfo) const
{
auto itr = uniformMap.find(baseUserName);
if (itr == uniformMap.end())
return false;
*out_activeInfo = itr->second;
return true;
}
bool HasActiveAttrib(GLuint loc) const {
auto itr = activeAttribLocs.find(loc);
return itr != activeAttribLocs.end();
}
};
} // namespace webgl
class WebGLShader;
struct WebGLUniformInfo;
typedef nsDataHashtable<nsCStringHashKey, nsCString> CStringMap;
typedef nsDataHashtable<nsCStringHashKey,
WebGLUniformInfo> CStringToUniformInfoMap;
class WebGLProgram MOZ_FINAL
: public nsWrapperCache
@ -30,107 +85,64 @@ class WebGLProgram MOZ_FINAL
, public WebGLContextBoundObject
{
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgram)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLProgram)
explicit WebGLProgram(WebGLContext* webgl);
void Delete();
void DetachShaders() {
mAttachedShaders.Clear();
// GL funcs
void AttachShader(WebGLShader* shader);
void BindAttribLocation(GLuint index, const nsAString& name);
void DetachShader(WebGLShader* shader);
already_AddRefed<WebGLActiveInfo> GetActiveAttrib(GLuint index) const;
already_AddRefed<WebGLActiveInfo> GetActiveUniform(GLuint index) const;
void GetAttachedShaders(nsTArray<nsRefPtr<WebGLShader>>* const out) const;
GLint GetAttribLocation(const nsAString& name) const;
void GetProgramInfoLog(nsAString* const out) const;
JS::Value GetProgramParameter(GLenum pname) const;
already_AddRefed<WebGLUniformLocation> GetUniformLocation(const nsAString& name) const;
bool LinkProgram();
bool UseProgram() const;
void ValidateProgram() const;
////////////////
bool FindAttribUserNameByMappedName(const nsACString& mappedName,
nsDependentCString* const out_userName) const;
bool FindUniformByMappedName(const nsACString& mappedName,
nsCString* const out_userName,
bool* const out_isArray) const;
bool IsLinked() const { return mMostRecentLinkInfo; }
const webgl::LinkedProgramInfo* LinkInfo() const {
return mMostRecentLinkInfo.get();
}
GLuint GLName() { return mGLName; }
const nsTArray<WebGLRefPtr<WebGLShader> >& AttachedShaders() const {
return mAttachedShaders;
}
bool LinkStatus() { return mLinkStatus; }
uint32_t Generation() const { return mGeneration.value(); }
void SetLinkStatus(bool val) { mLinkStatus = val; }
bool ContainsShader(WebGLShader* shader) {
return mAttachedShaders.Contains(shader);
}
// return true if the shader wasn't already attached
bool AttachShader(WebGLShader* shader);
// return true if the shader was found and removed
bool DetachShader(WebGLShader* shader);
bool HasAttachedShaderOfType(GLenum shaderType);
bool HasBothShaderTypesAttached() {
return HasAttachedShaderOfType(LOCAL_GL_VERTEX_SHADER) &&
HasAttachedShaderOfType(LOCAL_GL_FRAGMENT_SHADER);
}
bool HasBadShaderAttached();
size_t UpperBoundNumSamplerUniforms();
bool NextGeneration() {
if (!(mGeneration + 1).isValid())
return false; // must exit without changing mGeneration
++mGeneration;
return true;
}
// Called only after LinkProgram
bool UpdateInfo();
// Getters for cached program info
bool IsAttribInUse(uint32_t i) const { return mAttribsInUse[i]; }
// Maps identifier |name| to the mapped identifier |*mappedName|
// Both are ASCII strings.
void MapIdentifier(const nsACString& name, nsCString* out_mappedName);
// Un-maps mapped identifier |name| to the original identifier
// |*reverseMappedName|.
// Both are ASCII strings.
void ReverseMapIdentifier(const nsACString& name,
nsCString* out_reverseMappedName);
/* Returns the uniform array size (or 1 if the uniform is not an array) of
* the uniform with given mapped identifier.
*
* Note: The input string |name| is the mapped identifier, not the original
* identifier.
*/
WebGLUniformInfo GetUniformInfoForMappedIdentifier(const nsACString& name);
WebGLContext* GetParentObject() const {
return Context();
}
virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
virtual JSObject* WrapObject(JSContext* js) MOZ_OVERRIDE;
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgram)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLProgram)
// public post-link data
std::map<GLint, nsCString> mActiveAttribMap;
static uint64_t IdentifierHashFunction(const char* ident, size_t size);
static void HashMapIdentifier(const nsACString& name,
nsCString* const out_hashedName);
protected:
private:
~WebGLProgram() {
DeleteOnce();
}
GLuint mGLName;
bool mLinkStatus;
// attached shaders of the program object
nsTArray<WebGLRefPtr<WebGLShader> > mAttachedShaders;
CheckedUint32 mGeneration;
bool LinkAndUpdate();
// post-link data
FallibleTArray<bool> mAttribsInUse;
nsAutoPtr<CStringMap> mIdentifierMap, mIdentifierReverseMap;
nsAutoPtr<CStringToUniformInfoMap> mUniformInfoMap;
int mAttribMaxNameLength;
public:
const GLuint mGLName;
private:
WebGLRefPtr<WebGLShader> mVertShader;
WebGLRefPtr<WebGLShader> mFragShader;
std::map<nsCString, GLuint> mBoundAttribLocs;
nsCString mLinkLog;
RefPtr<const webgl::LinkedProgramInfo> mMostRecentLinkInfo;
};
} // namespace mozilla

View File

@ -9,51 +9,374 @@
#include "GLContext.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "mozilla/MemoryReporting.h"
#include "nsPrintfCString.h"
#include "nsString.h"
#include "WebGLContext.h"
#include "WebGLObjectModel.h"
#include "WebGLShaderValidator.h"
#include "WebGLValidateStrings.h"
namespace mozilla {
JSObject*
WebGLShader::WrapObject(JSContext* cx) {
return dom::WebGLShaderBinding::Wrap(cx, this);
// On success, writes to out_validator and out_translatedSource.
// On failure, writes to out_translationLog.
static bool
Translate(const nsACString& source, webgl::ShaderValidator* validator,
nsACString* const out_translationLog, nsACString* const out_translatedSource)
{
if (!validator->ValidateAndTranslate(source.BeginReading())) {
validator->GetInfoLog(out_translationLog);
return false;
}
// Success
validator->GetOutput(out_translatedSource);
return true;
}
template<size_t N>
static bool
SubstringStartsWith(const std::string& testStr, size_t offset, const char (& refStr)[N])
{
for (size_t i = 0; i < N-1; i++) {
if (testStr[offset + i] != refStr[i])
return false;
}
return true;
}
/* On success, writes to out_translatedSource.
* On failure, writes to out_translationLog.
*
* Requirements:
* #version is either omitted, `#version 100`, or `version 300 es`.
*/
static bool
TranslateWithoutValidation(const nsACString& sourceNS, bool isWebGL2,
nsACString* const out_translationLog,
nsACString* const out_translatedSource)
{
std::string source = sourceNS.BeginReading();
size_t versionStrStart = source.find("#version");
size_t versionStrLen;
uint32_t glesslVersion;
if (versionStrStart != std::string::npos) {
static const char versionStr100[] = "#version 100\n";
static const char versionStr300es[] = "#version 300 es\n";
if (isWebGL2 && SubstringStartsWith(source, versionStrStart, versionStr300es)) {
glesslVersion = 300;
versionStrLen = strlen(versionStr300es);
} else if (SubstringStartsWith(source, versionStrStart, versionStr100)) {
glesslVersion = 100;
versionStrLen = strlen(versionStr100);
} else {
nsPrintfCString error("#version, if declared, must be %s.",
isWebGL2 ? "`100` or `300 es`"
: "`100`");
*out_translationLog = error;
return false;
}
} else {
versionStrStart = 0;
versionStrLen = 0;
glesslVersion = 100;
}
std::string reversionedSource = source;
reversionedSource.erase(versionStrStart, versionStrLen);
switch (glesslVersion) {
case 100:
break;
case 300:
reversionedSource.insert(versionStrStart, "#version 330\n");
break;
default:
MOZ_CRASH("Bad `glesslVersion`.");
}
out_translatedSource->Assign(reversionedSource.c_str(),
reversionedSource.length());
return true;
}
static void
GetCompilationStatusAndLog(gl::GLContext* gl, GLuint shader, bool* const out_success,
nsACString* const out_log)
{
GLint compileStatus = LOCAL_GL_FALSE;
gl->fGetShaderiv(shader, LOCAL_GL_COMPILE_STATUS, &compileStatus);
// It's simpler if we always get the log.
GLint lenWithNull = 0;
gl->fGetShaderiv(shader, LOCAL_GL_INFO_LOG_LENGTH, &lenWithNull);
if (lenWithNull > 1) {
// SetLength takes the length without the null.
out_log->SetLength(lenWithNull - 1);
gl->fGetShaderInfoLog(shader, lenWithNull, nullptr, out_log->BeginWriting());
} else {
out_log->SetLength(0);
}
*out_success = (compileStatus == LOCAL_GL_TRUE);
}
////////////////////////////////////////////////////////////////////////////////
static GLuint
CreateShader(gl::GLContext* gl, GLenum type)
{
gl->MakeCurrent();
return gl->fCreateShader(type);
}
WebGLShader::WebGLShader(WebGLContext* webgl, GLenum type)
: WebGLContextBoundObject(webgl)
, mGLName(CreateShader(webgl->GL(), type))
, mType(type)
, mNeedsTranslation(true)
, mAttribMaxNameLength(0)
, mCompileStatus(false)
{
mContext->MakeContextCurrent();
mGLName = mContext->gl->fCreateShader(mType);
mContext->mShaders.insertBack(this);
}
void
WebGLShader::Delete()
WebGLShader::~WebGLShader()
{
mSource.Truncate();
mTranslationLog.Truncate();
mContext->MakeContextCurrent();
mContext->gl->fDeleteShader(mGLName);
LinkedListElement<WebGLShader>::removeFrom(mContext->mShaders);
DeleteOnce();
}
void
WebGLShader::ShaderSource(const nsAString& source)
{
StripComments stripComments(source);
const nsAString& cleanSource = Substring(stripComments.result().Elements(),
stripComments.length());
if (!ValidateGLSLString(cleanSource, mContext, "shaderSource"))
return;
// We checked that the source stripped of comments is in the
// 7-bit ASCII range, so we can skip the NS_IsAscii() check.
NS_LossyConvertUTF16toASCII sourceCString(cleanSource);
if (mContext->gl->WorkAroundDriverBugs()) {
const size_t maxSourceLength = 0x3ffff;
if (sourceCString.Length() > maxSourceLength) {
mContext->ErrorInvalidValue("shaderSource: Source has more than %d"
" characters. (Driver workaround)",
maxSourceLength);
return;
}
}
// HACK - dump shader source
{
/*
printf_stderr("//-*- glsl -*-\n");
// Wow - Roll Your Own For Each Lines because printf_stderr has a hard-coded internal size, so long strings are truncated.
const nsString& src = shader->Source();
int32_t start = 0;
int32_t end = src.Find("\n", false, start, -1);
while (end > -1) {
printf_stderr("%s\n", NS_ConvertUTF16toUTF8(nsDependentSubstring(src, start, end - start)).get());
start = end + 1;
end = src.Find("\n", false, start, -1);
}
printf_stderr("//\n");
*/
}
// HACK
mSource = source;
mCleanSource = sourceCString;
}
void
WebGLShader::CompileShader()
{
mValidator = nullptr;
mTranslationSuccessful = false;
mCompilationSuccessful = false;
gl::GLContext* gl = mContext->gl;
mValidator.reset(mContext->CreateShaderValidator(mType));
bool success;
if (mValidator) {
success = Translate(mCleanSource, mValidator.get(), &mValidationLog,
&mTranslatedSource);
} else {
success = TranslateWithoutValidation(mCleanSource, mContext->IsWebGL2(),
&mValidationLog, &mTranslatedSource);
}
if (!success)
return;
mTranslationSuccessful = true;
gl->MakeCurrent();
const char* const parts[] = {
mTranslatedSource.BeginReading()
};
gl->fShaderSource(mGLName, ArrayLength(parts), parts, nullptr);
gl->fCompileShader(mGLName);
GetCompilationStatusAndLog(gl, mGLName, &mCompilationSuccessful, &mCompilationLog);
}
void
WebGLShader::GetShaderInfoLog(nsAString* out) const
{
const nsCString& log = !mTranslationSuccessful ? mValidationLog
: mCompilationLog;
CopyASCIItoUTF16(log, *out);
}
JS::Value
WebGLShader::GetShaderParameter(GLenum pname) const
{
switch (pname) {
case LOCAL_GL_SHADER_TYPE:
return JS::NumberValue(mType);
case LOCAL_GL_DELETE_STATUS:
return JS::BooleanValue(IsDeleteRequested());
case LOCAL_GL_COMPILE_STATUS:
return JS::BooleanValue(mCompilationSuccessful);
default:
mContext->ErrorInvalidEnumInfo("getShaderParameter: `pname`", pname);
return JS::NullValue();
}
}
void
WebGLShader::GetShaderSource(nsAString* out) const
{
out->SetIsVoid(false);
*out = mSource;
}
void
WebGLShader::GetShaderTranslatedSource(nsAString* out) const
{
if (!mCompilationSuccessful) {
mContext->ErrorInvalidOperation("getShaderTranslatedSource: Shader has"
" not been successfully compiled.");
return;
}
out->SetIsVoid(false);
CopyASCIItoUTF16(mTranslatedSource, *out);
}
////////////////////////////////////////////////////////////////////////////////
bool
WebGLShader::CanLinkTo(const WebGLShader* prev, nsCString* const out_log) const
{
if (!mValidator)
return true;
return mValidator->CanLinkTo(prev->mValidator.get(), out_log);
}
size_t
WebGLShader::CalcNumSamplerUniforms() const
{
if (mValidator)
return mValidator->CalcNumSamplerUniforms();
// TODO
return 0;
}
void
WebGLShader::BindAttribLocation(GLuint prog, const nsCString& userName,
GLuint index) const
{
std::string userNameStr(userName.BeginReading());
const std::string* mappedNameStr = &userNameStr;
if (mValidator)
mValidator->FindAttribMappedNameByUserName(userNameStr, &mappedNameStr);
mContext->gl->fBindAttribLocation(prog, index, mappedNameStr->c_str());
}
bool
WebGLShader::FindAttribUserNameByMappedName(const nsACString& mappedName,
nsDependentCString* const out_userName) const
{
if (!mValidator)
return false;
const std::string mappedNameStr(mappedName.BeginReading());
const std::string* userNameStr;
if (!mValidator->FindAttribUserNameByMappedName(mappedNameStr, &userNameStr))
return false;
out_userName->Rebind(userNameStr->c_str());
return true;
}
bool
WebGLShader::FindUniformByMappedName(const nsACString& mappedName,
nsCString* const out_userName,
bool* const out_isArray) const
{
if (!mValidator)
return false;
const std::string mappedNameStr(mappedName.BeginReading(), mappedName.Length());
std::string userNameStr;
if (!mValidator->FindUniformByMappedName(mappedNameStr, &userNameStr, out_isArray))
return false;
*out_userName = userNameStr.c_str();
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Boilerplate
JSObject*
WebGLShader::WrapObject(JSContext* js)
{
return dom::WebGLShaderBinding::Wrap(js, this);
}
size_t
WebGLShader::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
{
size_t validatorSize = mValidator ? mallocSizeOf(mValidator.get())
: 0;
return mallocSizeOf(this) +
mSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
mTranslationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf);
mCleanSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
validatorSize +
mValidationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
mTranslatedSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
mCompilationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf);
}
void
WebGLShader::SetTranslationSuccess()
WebGLShader::Delete()
{
mTranslationLog.SetIsVoid(true);
mNeedsTranslation = false;
gl::GLContext* gl = mContext->GL();
gl->MakeCurrent();
gl->fDeleteShader(mGLName);
LinkedListElement<WebGLShader>::removeFrom(mContext->mShaders);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLShader)

View File

@ -6,25 +6,17 @@
#ifndef WEBGL_SHADER_H_
#define WEBGL_SHADER_H_
#include "GLDefs.h"
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "nsWrapperCache.h"
#include "WebGLObjectModel.h"
#include "WebGLUniformInfo.h"
namespace mozilla {
struct WebGLMappedIdentifier
{
// ASCII strings
nsCString original;
nsCString mapped;
WebGLMappedIdentifier(const nsACString& o, const nsACString& m)
: original(o)
, mapped(m)
{}
};
namespace webgl {
class ShaderValidator;
} // namespace webgl
class WebGLShader MOZ_FINAL
: public nsWrapperCache
@ -38,67 +30,57 @@ class WebGLShader MOZ_FINAL
public:
WebGLShader(WebGLContext* webgl, GLenum type);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
protected:
~WebGLShader();
GLuint GLName() { return mGLName; }
sh::GLenum ShaderType() { return mType; }
public:
// GL funcs
void CompileShader();
JS::Value GetShaderParameter(GLenum pname) const;
void GetShaderInfoLog(nsAString* out) const;
void GetShaderSource(nsAString* out) const;
void GetShaderTranslatedSource(nsAString* out) const;
void ShaderSource(const nsAString& source);
void SetSource(const nsAString& src) {
// TODO: Do some quick gzip here maybe? Getting this will be very rare,
// and we have to keep it forever.
mSource.Assign(src);
}
const nsString& Source() const { return mSource; }
void SetNeedsTranslation() { mNeedsTranslation = true; }
bool NeedsTranslation() const { return mNeedsTranslation; }
void SetCompileStatus (bool status) {
mCompileStatus = status;
// Util funcs
bool CanLinkTo(const WebGLShader* prev, nsCString* const out_log) const;
size_t CalcNumSamplerUniforms() const;
void BindAttribLocation(GLuint prog, const nsCString& userName, GLuint index) const;
bool FindAttribUserNameByMappedName(const nsACString& mappedName,
nsDependentCString* const out_userName) const;
bool FindUniformByMappedName(const nsACString& mappedName,
nsCString* const out_userName,
bool* const out_isArray) const;
bool IsCompiled() const {
return mTranslationSuccessful && mCompilationSuccessful;
}
// Other funcs
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
void Delete();
bool CompileStatus() const {
return mCompileStatus;
}
WebGLContext* GetParentObject() const { return Context(); }
void SetTranslationSuccess();
void SetTranslationFailure(const nsCString& msg) {
mTranslationLog.Assign(msg);
}
const nsCString& TranslationLog() const { return mTranslationLog; }
const nsString& TranslatedSource() const { return mTranslatedSource; }
WebGLContext* GetParentObject() const {
return Context();
}
virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
virtual JSObject* WrapObject(JSContext* js) MOZ_OVERRIDE;
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLShader)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLShader)
public:
const GLuint mGLName;
const GLenum mType;
protected:
~WebGLShader() {
DeleteOnce();
}
GLuint mGLName;
GLenum mType;
nsString mSource;
nsString mTranslatedSource;
nsCString mTranslationLog; // The translation log should contain only ASCII characters
bool mNeedsTranslation;
nsTArray<WebGLMappedIdentifier> mAttributes;
nsTArray<WebGLMappedIdentifier> mUniforms;
nsTArray<WebGLUniformInfo> mUniformInfos;
int mAttribMaxNameLength;
bool mCompileStatus;
nsCString mCleanSource;
UniquePtr<webgl::ShaderValidator> mValidator;
nsCString mValidationLog;
bool mTranslationSuccessful;
nsCString mTranslatedSource;
bool mCompilationSuccessful;
nsCString mCompilationLog;
};
} // namespace mozilla

View File

@ -0,0 +1,349 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "WebGLShaderValidator.h"
#include "angle/ShaderLang.h"
#include "GLContext.h"
#include "MurmurHash3.h"
#include "nsPrintfCString.h"
#include <string>
#include <vector>
#include "WebGLContext.h"
namespace mozilla {
namespace webgl {
uint64_t
IdentifierHashFunc(const char* name, size_t len)
{
// NB: we use the x86 function everywhere, even though it's suboptimal perf
// on x64. They return different results; not sure if that's a requirement.
uint64_t hash[2];
MurmurHash3_x86_128(name, len, 0, hash);
return hash[0];
}
static int
ChooseValidatorCompileOptions(const ShBuiltInResources& resources,
const mozilla::gl::GLContext* gl)
{
int options = SH_VARIABLES |
SH_ENFORCE_PACKING_RESTRICTIONS |
SH_INIT_VARYINGS_WITHOUT_STATIC_USE |
SH_OBJECT_CODE |
SH_LIMIT_CALL_STACK_DEPTH;
if (resources.MaxExpressionComplexity > 0) {
options |= 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
// compiler in 10.6 to crash
options |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
#endif
#ifdef XP_MACOSX
if (gl->WorkAroundDriverBugs()) {
// Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
// https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
options |= SH_UNFOLD_SHORT_CIRCUIT;
// Work around bug 665578 and bug 769810
if (gl->Vendor() == gl::GLVendor::ATI) {
options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
}
// Work around bug 735560
if (gl->Vendor() == gl::GLVendor::Intel) {
options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
}
// Work around bug 636926
if (gl->Vendor() == gl::GLVendor::NVIDIA) {
options |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
}
}
#endif
return options;
}
} // namespace webgl
////////////////////////////////////////
webgl::ShaderValidator*
WebGLContext::CreateShaderValidator(GLenum shaderType) const
{
if (mBypassShaderValidation)
return nullptr;
ShShaderSpec spec = SH_WEBGL_SPEC;
ShShaderOutput outputLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT
: SH_GLSL_OUTPUT;
ShBuiltInResources resources;
memset(&resources, 0, sizeof(resources));
ShInitBuiltInResources(&resources);
resources.HashFunction = webgl::IdentifierHashFunc;
resources.MaxVertexAttribs = mGLMaxVertexAttribs;
resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors;
resources.MaxVaryingVectors = mGLMaxVaryingVectors;
resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits;
resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits;
resources.MaxTextureImageUnits = mGLMaxTextureImageUnits;
resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
resources.MaxDrawBuffers = mGLMaxDrawBuffers;
if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth))
resources.EXT_frag_depth = 1;
if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
resources.OES_standard_derivatives = 1;
if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
resources.EXT_draw_buffers = 1;
if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod))
resources.EXT_shader_texture_lod = 1;
// Tell ANGLE to allow highp in frag shaders. (unless disabled)
// If underlying GLES doesn't have highp in frag shaders, it should complain anyways.
resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1;
if (gl->WorkAroundDriverBugs()) {
#ifdef XP_MACOSX
if (gl->Vendor() == gl::GLVendor::NVIDIA) {
// Work around bug 890432
resources.MaxExpressionComplexity = 1000;
}
#endif
}
int compileOptions = webgl::ChooseValidatorCompileOptions(resources, gl);
return webgl::ShaderValidator::Create(shaderType, spec, outputLanguage, resources,
compileOptions);
}
////////////////////////////////////////
namespace webgl {
/*static*/ ShaderValidator*
ShaderValidator::Create(GLenum shaderType, ShShaderSpec spec,
ShShaderOutput outputLanguage,
const ShBuiltInResources& resources, int compileOptions)
{
ShHandle handle = ShConstructCompiler(shaderType, spec, outputLanguage, &resources);
if (!handle)
return nullptr;
return new ShaderValidator(handle, compileOptions);
}
ShaderValidator::~ShaderValidator()
{
ShDestruct(mHandle);
}
bool
ShaderValidator::ValidateAndTranslate(const char* source)
{
MOZ_ASSERT(!mHasRun);
mHasRun = true;
const char* const parts[] = {
source
};
return ShCompile(mHandle, parts, ArrayLength(parts), mCompileOptions);
}
void
ShaderValidator::GetInfo(ShShaderInfo pname, size_t* params) const
{
MOZ_ASSERT(mHasRun);
ShGetInfo(mHandle, pname, params);
}
void
ShaderValidator::GetInfoLog(nsACString* out) const
{
MOZ_ASSERT(mHasRun);
size_t lenWithNull = 0;
GetInfo(SH_INFO_LOG_LENGTH, &lenWithNull);
MOZ_ASSERT(lenWithNull >= 1);
// SetLength allocates len+1, for the null-term.
out->SetLength(lenWithNull - 1);
ShGetInfoLog(mHandle, out->BeginWriting());
}
void
ShaderValidator::GetOutput(nsACString* out) const
{
MOZ_ASSERT(mHasRun);
size_t lenWithNull = 0;
GetInfo(SH_OBJECT_CODE_LENGTH, &lenWithNull);
MOZ_ASSERT(lenWithNull >= 1);
// SetLength allocates len+1, for the null-term.
out->SetLength(lenWithNull - 1);
ShGetObjectCode(mHandle, out->BeginWriting());
}
template<size_t N>
static bool
StartsWith(const std::string& haystack, const char (&needle)[N])
{
return haystack.compare(0, N - 1, needle) == 0;
}
bool
ShaderValidator::CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const
{
{
const std::vector<sh::Uniform>& vertList = *ShGetUniforms(prev->mHandle);
const std::vector<sh::Uniform>& fragList = *ShGetUniforms(mHandle);
for (auto itrFrag = fragList.begin(); itrFrag != fragList.end(); ++itrFrag) {
for (auto itrVert = vertList.begin(); itrVert != vertList.end(); ++itrVert) {
if (itrVert->name != itrFrag->name)
continue;
if (!itrVert->isSameUniformAtLinkTime(*itrFrag)) {
nsPrintfCString error("Uniform `%s`is not linkable between"
" attached shaders.",
itrFrag->name.c_str());
*out_log = error;
return false;
}
break;
}
}
}
{
const std::vector<sh::Varying>& vertList = *ShGetVaryings(prev->mHandle);
const std::vector<sh::Varying>& fragList = *ShGetVaryings(mHandle);
for (auto itrFrag = fragList.begin(); itrFrag != fragList.end(); ++itrFrag) {
static const char prefix[] = "gl_";
if (StartsWith(itrFrag->name, prefix))
continue;
bool definedInVertShader = false;
for (auto itrVert = vertList.begin(); itrVert != vertList.end(); ++itrVert) {
if (itrVert->name != itrFrag->name)
continue;
if (!itrVert->isSameVaryingAtLinkTime(*itrFrag)) {
nsPrintfCString error("Varying `%s`is not linkable between"
" attached shaders.",
itrFrag->name.c_str());
*out_log = error;
return false;
}
definedInVertShader = true;
break;
}
if (!definedInVertShader && itrFrag->staticUse) {
nsPrintfCString error("Varying `%s` has static-use in the frag"
" shader, but is undeclared in the vert"
" shader.", itrFrag->name.c_str());
*out_log = error;
return false;
}
}
}
return true;
}
size_t
ShaderValidator::CalcNumSamplerUniforms() const
{
size_t accum = 0;
const std::vector<sh::Uniform>& uniforms = *ShGetUniforms(mHandle);
for (auto itr = uniforms.begin(); itr != uniforms.end(); ++itr) {
GLenum type = itr->type;
if (type == LOCAL_GL_SAMPLER_2D ||
type == LOCAL_GL_SAMPLER_CUBE)
{
accum += itr->arraySize;
}
}
return accum;
}
// Attribs cannot be structs or arrays, and neither can vertex inputs in ES3.
// Therefore, attrib names are always simple.
bool
ShaderValidator::FindAttribUserNameByMappedName(const std::string& mappedName,
const std::string** const out_userName) const
{
const std::vector<sh::Attribute>& attribs = *ShGetAttributes(mHandle);
for (auto itr = attribs.begin(); itr != attribs.end(); ++itr) {
if (itr->mappedName == mappedName) {
*out_userName = &(itr->name);
return true;
}
}
return false;
}
bool
ShaderValidator::FindAttribMappedNameByUserName(const std::string& userName,
const std::string** const out_mappedName) const
{
const std::vector<sh::Attribute>& attribs = *ShGetAttributes(mHandle);
for (auto itr = attribs.begin(); itr != attribs.end(); ++itr) {
if (itr->name == userName) {
*out_mappedName = &(itr->mappedName);
return true;
}
}
return false;
}
// This must handle names like "foo.bar[0]".
bool
ShaderValidator::FindUniformByMappedName(const std::string& mappedName,
std::string* const out_userName,
bool* const out_isArray) const
{
const std::vector<sh::Uniform>& uniforms = *ShGetUniforms(mHandle);
for (auto itr = uniforms.begin(); itr != uniforms.end(); ++itr) {
const sh::ShaderVariable* found;
if (!itr->findInfoByMappedName(mappedName, &found, out_userName))
continue;
*out_isArray = found->isArray();
return true;
}
return false;
}
} // namespace webgl
} // namespace mozilla

View File

@ -0,0 +1,60 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 WEBGL_SHADER_VALIDATOR_H_
#define WEBGL_SHADER_VALIDATOR_H_
#include "angle/ShaderLang.h"
#include "GLDefs.h"
#include "nsString.h"
#include <string>
namespace mozilla {
namespace webgl {
class ShaderValidator MOZ_FINAL
{
const ShHandle mHandle;
const int mCompileOptions;
bool mHasRun;
public:
static ShaderValidator* Create(GLenum shaderType, ShShaderSpec spec,
ShShaderOutput outputLanguage,
const ShBuiltInResources& resources,
int compileOptions);
private:
ShaderValidator(ShHandle handle, int compileOptions)
: mHandle(handle)
, mCompileOptions(compileOptions)
, mHasRun(false)
{ }
public:
~ShaderValidator();
bool ValidateAndTranslate(const char* source);
void GetInfo(ShShaderInfo pname, size_t* params) const;
void GetInfoLog(nsACString* out) const;
void GetOutput(nsACString* out) const;
bool CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const;
size_t CalcNumSamplerUniforms() const;
bool FindAttribUserNameByMappedName(const std::string& mappedName,
const std::string** const out_userName) const;
bool FindAttribMappedNameByUserName(const std::string& userName,
const std::string** const out_mappedName) const;
bool FindUniformByMappedName(const std::string& mappedName,
std::string* const out_userName,
bool* const out_isArray) const;
};
} // namespace webgl
} // namespace mozilla
#endif // WEBGL_SHADER_VALIDATOR_H_

View File

@ -1,82 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 WEBGLUNIFORMINFO_H_
#define WEBGLUNIFORMINFO_H_
#include "GLDefs.h"
#include "angle/ShaderLang.h"
namespace mozilla {
struct WebGLUniformInfo {
uint32_t arraySize;
bool isArray;
sh::GLenum type;
explicit WebGLUniformInfo(uint32_t s = 0, bool a = false, sh::GLenum t = LOCAL_GL_NONE)
: arraySize(s), isArray(a), type(t) {}
int ElementSize() const {
switch (type) {
case LOCAL_GL_FLOAT:
case LOCAL_GL_INT:
case LOCAL_GL_UNSIGNED_INT:
case LOCAL_GL_BOOL:
case LOCAL_GL_SAMPLER_2D:
case LOCAL_GL_SAMPLER_3D:
case LOCAL_GL_SAMPLER_CUBE:
case LOCAL_GL_SAMPLER_2D_SHADOW:
case LOCAL_GL_SAMPLER_2D_ARRAY:
case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
case LOCAL_GL_SAMPLER_CUBE_SHADOW:
case LOCAL_GL_INT_SAMPLER_2D:
case LOCAL_GL_INT_SAMPLER_3D:
case LOCAL_GL_INT_SAMPLER_CUBE:
case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
return 1;
case LOCAL_GL_FLOAT_VEC2:
case LOCAL_GL_INT_VEC2:
case LOCAL_GL_UNSIGNED_INT_VEC2:
case LOCAL_GL_BOOL_VEC2:
return 2;
case LOCAL_GL_FLOAT_VEC3:
case LOCAL_GL_INT_VEC3:
case LOCAL_GL_UNSIGNED_INT_VEC3:
case LOCAL_GL_BOOL_VEC3:
return 3;
case LOCAL_GL_FLOAT_VEC4:
case LOCAL_GL_INT_VEC4:
case LOCAL_GL_UNSIGNED_INT_VEC4:
case LOCAL_GL_BOOL_VEC4:
case LOCAL_GL_FLOAT_MAT2:
return 4;
case LOCAL_GL_FLOAT_MAT2x3:
case LOCAL_GL_FLOAT_MAT3x2:
return 6;
case LOCAL_GL_FLOAT_MAT2x4:
case LOCAL_GL_FLOAT_MAT4x2:
return 8;
case LOCAL_GL_FLOAT_MAT3:
return 9;
case LOCAL_GL_FLOAT_MAT3x4:
case LOCAL_GL_FLOAT_MAT4x3:
return 12;
case LOCAL_GL_FLOAT_MAT4:
return 16;
default:
MOZ_ASSERT(false); // should never get here
return 0;
}
}
};
} // namespace mozilla
#endif

View File

@ -5,34 +5,272 @@
#include "WebGLUniformLocation.h"
#include "GLContext.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "WebGLActiveInfo.h"
#include "WebGLContext.h"
#include "WebGLProgram.h"
#include "WebGLShader.h"
namespace mozilla {
WebGLUniformLocation::WebGLUniformLocation(WebGLContext* webgl,
const webgl::LinkedProgramInfo* linkInfo,
GLuint loc, const WebGLActiveInfo* activeInfo)
: WebGLContextBoundObject(webgl)
, mLinkInfo(linkInfo)
, mLoc(loc)
, mActiveInfo(activeInfo)
{ }
WebGLUniformLocation::~WebGLUniformLocation()
{ }
bool
WebGLUniformLocation::WrapObject(JSContext* aCx,
JS::MutableHandle<JSObject*> aReflector)
WebGLUniformLocation::ValidateForProgram(WebGLProgram* prog, WebGLContext* webgl,
const char* funcName) const
{
return dom::WebGLUniformLocationBinding::Wrap(aCx, this, aReflector);
// Check the weak-pointer.
if (!mLinkInfo) {
webgl->ErrorInvalidOperation("%s: This uniform location is obsolete because its"
" program has been successfully relinked.",
funcName);
return false;
}
if (mLinkInfo->prog != prog) {
webgl->ErrorInvalidOperation("%s: This uniform location corresponds to a"
" different program.", funcName);
return false;
}
return true;
}
WebGLUniformLocation::WebGLUniformLocation(WebGLContext* context,
WebGLProgram* program,
GLint location,
const WebGLUniformInfo& info)
: WebGLContextBoundObject(context)
, mProgram(program)
, mProgramGeneration(program->Generation())
, mLocation(location)
, mInfo(info)
static bool
IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType)
{
mElementSize = info.ElementSize();
switch (uniformType) {
case LOCAL_GL_BOOL:
case LOCAL_GL_BOOL_VEC2:
case LOCAL_GL_BOOL_VEC3:
case LOCAL_GL_BOOL_VEC4:
return setterType == LOCAL_GL_INT ||
setterType == LOCAL_GL_FLOAT; // GLfloat(0.0) sets a bool to false.
case LOCAL_GL_INT:
case LOCAL_GL_INT_SAMPLER_2D:
case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
case LOCAL_GL_INT_SAMPLER_3D:
case LOCAL_GL_INT_SAMPLER_CUBE:
case LOCAL_GL_INT_VEC2:
case LOCAL_GL_INT_VEC3:
case LOCAL_GL_INT_VEC4:
case LOCAL_GL_SAMPLER_2D:
case LOCAL_GL_SAMPLER_2D_ARRAY:
case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
case LOCAL_GL_SAMPLER_2D_SHADOW:
case LOCAL_GL_SAMPLER_CUBE:
case LOCAL_GL_SAMPLER_CUBE_SHADOW:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
return setterType == LOCAL_GL_INT;
case LOCAL_GL_FLOAT:
case LOCAL_GL_FLOAT_MAT2:
case LOCAL_GL_FLOAT_MAT2x3:
case LOCAL_GL_FLOAT_MAT2x4:
case LOCAL_GL_FLOAT_MAT3:
case LOCAL_GL_FLOAT_MAT3x2:
case LOCAL_GL_FLOAT_MAT3x4:
case LOCAL_GL_FLOAT_MAT4:
case LOCAL_GL_FLOAT_MAT4x2:
case LOCAL_GL_FLOAT_MAT4x3:
case LOCAL_GL_FLOAT_VEC2:
case LOCAL_GL_FLOAT_VEC3:
case LOCAL_GL_FLOAT_VEC4:
return setterType == LOCAL_GL_FLOAT;
default:
MOZ_CRASH("Bad `uniformType`.");
}
}
NS_IMPL_CYCLE_COLLECTION(WebGLUniformLocation, mProgram)
bool
WebGLUniformLocation::ValidateSizeAndType(uint8_t setterElemSize, GLenum setterType,
WebGLContext* webgl, const char* funcName) const
{
MOZ_ASSERT(mLinkInfo);
if (setterElemSize != mActiveInfo->mElemSize) {
webgl->ErrorInvalidOperation("%s: Bad uniform size: %i", funcName,
mActiveInfo->mElemSize);
return false;
}
if (!IsUniformSetterTypeValid(setterType, mActiveInfo->mElemType)) {
webgl->ErrorInvalidOperation("%s: Bad uniform type: %i", funcName,
mActiveInfo->mElemSize);
return false;
}
return true;
}
bool
WebGLUniformLocation::ValidateArrayLength(uint8_t setterElemSize, size_t setterArraySize,
WebGLContext* webgl, const char* funcName) const
{
MOZ_ASSERT(mLinkInfo);
if (setterArraySize == 0 ||
setterArraySize % setterElemSize)
{
webgl->ErrorInvalidValue("%s: expected an array of length a multiple of"
" %d, got an array of length %d.",
funcName, setterElemSize, setterArraySize);
return false;
}
/* GLES 2.0.25, Section 2.10, p38
* When loading `N` elements starting at an arbitrary position `k` in a uniform
* declared as an array, elements `k` through `k + N - 1` in the array will be
* replaced with the new values. Values for any array element that exceeds the
* highest array element index used, as reported by `GetActiveUniform`, will be
* ignored by GL.
*/
if (!mActiveInfo->mIsArray &&
setterArraySize != setterElemSize)
{
webgl->ErrorInvalidOperation("%s: expected an array of length exactly %d"
" (since this uniform is not an array"
" uniform), got an array of length %d.",
funcName, setterElemSize, setterArraySize);
return false;
}
return true;
}
bool
WebGLUniformLocation::ValidateSamplerSetter(GLint value, WebGLContext* webgl,
const char* funcName) const
{
MOZ_ASSERT(mLinkInfo);
if (mActiveInfo->mElemType != LOCAL_GL_SAMPLER_2D &&
mActiveInfo->mElemType != LOCAL_GL_SAMPLER_CUBE)
{
return true;
}
if (value >= 0 && value < (GLint)webgl->GLMaxTextureUnits())
return true;
webgl->ErrorInvalidValue("%s: This uniform location is a sampler, but %d is not a"
" valid texture unit.",
funcName, value);
return false;
}
JS::Value
WebGLUniformLocation::GetUniform(JSContext* js, WebGLContext* webgl) const
{
MOZ_ASSERT(mLinkInfo);
uint8_t elemSize = mActiveInfo->mElemSize;
static const uint8_t kMaxElemSize = 16;
MOZ_ASSERT(elemSize <= kMaxElemSize);
GLuint prog = mLinkInfo->prog->mGLName;
gl::GLContext* gl = webgl->GL();
gl->MakeCurrent();
switch (mActiveInfo->mElemType) {
case LOCAL_GL_INT:
case LOCAL_GL_INT_VEC2:
case LOCAL_GL_INT_VEC3:
case LOCAL_GL_INT_VEC4:
case LOCAL_GL_SAMPLER_2D:
case LOCAL_GL_SAMPLER_CUBE:
{
GLint buffer[kMaxElemSize] = {0};
gl->fGetUniformiv(prog, mLoc, buffer);
if (elemSize == 1)
return JS::Int32Value(buffer[0]);
JSObject* obj = dom::Int32Array::Create(js, webgl, elemSize, buffer);
if (!obj) {
webgl->ErrorOutOfMemory("getUniform: out of memory");
return JS::NullValue();
}
return JS::ObjectOrNullValue(obj);
}
case LOCAL_GL_BOOL:
case LOCAL_GL_BOOL_VEC2:
case LOCAL_GL_BOOL_VEC3:
case LOCAL_GL_BOOL_VEC4:
{
GLint buffer[kMaxElemSize] = {0};
gl->fGetUniformiv(prog, mLoc, buffer);
if (elemSize == 1)
return JS::BooleanValue(buffer[0]);
bool boolBuffer[kMaxElemSize];
for (uint8_t i = 0; i < kMaxElemSize; i++)
boolBuffer[i] = buffer[i];
JS::RootedValue val(js);
// Be careful: we don't want to convert all of |uv|!
if (!dom::ToJSValue(js, boolBuffer, elemSize, &val)) {
webgl->ErrorOutOfMemory("getUniform: out of memory");
return JS::NullValue();
}
return val;
}
case LOCAL_GL_FLOAT:
case LOCAL_GL_FLOAT_VEC2:
case LOCAL_GL_FLOAT_VEC3:
case LOCAL_GL_FLOAT_VEC4:
case LOCAL_GL_FLOAT_MAT2:
case LOCAL_GL_FLOAT_MAT3:
case LOCAL_GL_FLOAT_MAT4:
{
GLfloat buffer[16] = {0.0f};
gl->fGetUniformfv(prog, mLoc, buffer);
if (elemSize == 1)
return JS::DoubleValue(buffer[0]);
JSObject* obj = dom::Float32Array::Create(js, webgl, elemSize, buffer);
if (!obj) {
webgl->ErrorOutOfMemory("getUniform: out of memory");
return JS::NullValue();
}
return JS::ObjectOrNullValue(obj);
}
default:
MOZ_CRASH("Invalid elemType.");
}
}
////////////////////////////////////////////////////////////////////////////////
JSObject*
WebGLUniformLocation::WrapObject(JSContext* js)
{
return dom::WebGLUniformLocationBinding::Wrap(js, this);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLUniformLocation)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLUniformLocation, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLUniformLocation, Release)

View File

@ -6,48 +6,63 @@
#ifndef WEBGL_UNIFORM_LOCATION_H_
#define WEBGL_UNIFORM_LOCATION_H_
#include "GLDefs.h"
#include "mozilla/WeakPtr.h"
#include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS
#include "nsISupportsImpl.h" // NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING
#include "nsWrapperCache.h"
#include "WebGLObjectModel.h"
#include "WebGLUniformInfo.h"
struct JSContext;
namespace mozilla {
class WebGLActiveInfo;
class WebGLContext;
class WebGLProgram;
namespace webgl {
struct LinkedProgramInfo;
}
class WebGLUniformLocation MOZ_FINAL
: public WebGLContextBoundObject
: public nsWrapperCache
, public WebGLContextBoundObject
{
public:
WebGLUniformLocation(WebGLContext* context, WebGLProgram* program,
GLint location, const WebGLUniformInfo& info);
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLUniformLocation)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLUniformLocation)
// needed for certain helper functions like ValidateObject.
// WebGLUniformLocation's can't be 'Deleted' in the WebGL sense.
virtual JSObject* WrapObject(JSContext* js) MOZ_OVERRIDE;
WebGLContext* GetParentObject() const {
return mContext;
}
const WeakPtr<const webgl::LinkedProgramInfo> mLinkInfo;
const GLuint mLoc;
const WebGLActiveInfo* const mActiveInfo;
WebGLUniformLocation(WebGLContext* webgl, const webgl::LinkedProgramInfo* linkInfo,
GLuint loc, const WebGLActiveInfo* activeInfo);
bool ValidateForProgram(WebGLProgram* prog, WebGLContext* webgl,
const char* funcName) const;
bool ValidateSamplerSetter(GLint value, WebGLContext* webgl,
const char* funcName) const;
bool ValidateSizeAndType(uint8_t setterElemSize, GLenum setterType,
WebGLContext* webgl, const char* funcName) const;
bool ValidateArrayLength(uint8_t setterElemSize, size_t setterArraySize,
WebGLContext* webgl, const char* funcName) const;
JS::Value GetUniform(JSContext* js, WebGLContext* webgl) const;
// Needed for certain helper functions like ValidateObject.
// `WebGLUniformLocation`s can't be 'Deleted' in the WebGL sense.
bool IsDeleted() const { return false; }
const WebGLUniformInfo& Info() const { return mInfo; }
WebGLProgram* Program() const { return mProgram; }
GLint Location() const { return mLocation; }
uint32_t ProgramGeneration() const { return mProgramGeneration; }
int ElementSize() const { return mElementSize; }
bool WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLUniformLocation)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(WebGLUniformLocation)
protected:
~WebGLUniformLocation() { }
// nsRefPtr, not WebGLRefPtr, so that we don't prevent the program from being explicitly deleted.
// we just want to avoid having a dangling pointer.
nsRefPtr<WebGLProgram> mProgram;
uint32_t mProgramGeneration;
GLint mLocation;
WebGLUniformInfo mInfo;
int mElementSize;
friend class WebGLProgram;
~WebGLUniformLocation();
};
} // namespace mozilla

View File

@ -0,0 +1,169 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "WebGLValidateStrings.h"
#include "nsString.h"
#include "WebGLContext.h"
namespace mozilla {
// The following code was taken from the WebKit WebGL implementation,
// which can be found here:
// http://trac.webkit.org/browser/trunk/Source/WebCore/html/canvas/WebGLRenderingContext.cpp?rev=93625#L121
// Note that some modifications were done to adapt it to Mozilla.
/****** BEGIN CODE TAKEN FROM WEBKIT ******/
bool IsValidGLSLCharacter(char16_t c)
{
// Printing characters are valid except " $ ` @ \ ' DEL.
if (c >= 32 && c <= 126 &&
c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' && c != '\'')
{
return true;
}
// Horizontal tab, line feed, vertical tab, form feed, carriage return
// are also valid.
if (c >= 9 && c <= 13) {
return true;
}
return false;
}
void StripComments::process(char16_t c)
{
if (isNewline(c)) {
// No matter what state we are in, pass through newlines
// so we preserve line numbers.
emit(c);
if (m_parseState != InMultiLineComment)
m_parseState = BeginningOfLine;
return;
}
char16_t temp = 0;
switch (m_parseState) {
case BeginningOfLine:
// If it's an ASCII space.
if (c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9))) {
emit(c);
break;
}
if (c == '#') {
m_parseState = InPreprocessorDirective;
emit(c);
break;
}
// Transition to normal state and re-handle character.
m_parseState = MiddleOfLine;
process(c);
break;
case MiddleOfLine:
if (c == '/' && peek(temp)) {
if (temp == '/') {
m_parseState = InSingleLineComment;
emit(' ');
advance();
break;
}
if (temp == '*') {
m_parseState = InMultiLineComment;
// Emit the comment start in case the user has
// an unclosed comment and we want to later
// signal an error.
emit('/');
emit('*');
advance();
break;
}
}
emit(c);
break;
case InPreprocessorDirective:
// No matter what the character is, just pass it
// through. Do not parse comments in this state. This
// might not be the right thing to do long term, but it
// should handle the #error preprocessor directive.
emit(c);
break;
case InSingleLineComment:
// The newline code at the top of this function takes care
// of resetting our state when we get out of the
// single-line comment. Swallow all other characters.
break;
case InMultiLineComment:
if (c == '*' && peek(temp) && temp == '/') {
emit('*');
emit('/');
m_parseState = MiddleOfLine;
advance();
break;
}
// Swallow all other characters. Unclear whether we may
// want or need to just emit a space per character to try
// to preserve column numbers for debugging purposes.
break;
}
}
/****** END CODE TAKEN FROM WEBKIT ******/
bool
ValidateGLSLString(const nsAString& string, WebGLContext* webgl, const char* funcName)
{
for (size_t i = 0; i < string.Length(); ++i) {
if (!IsValidGLSLCharacter(string.CharAt(i))) {
webgl->ErrorInvalidValue("%s: String contains the illegal character '%d'",
funcName, string.CharAt(i));
return false;
}
}
return true;
}
bool
ValidateGLSLVariableName(const nsAString& name, WebGLContext* webgl, const char* funcName)
{
if (name.IsEmpty())
return false;
const uint32_t maxSize = 256;
if (name.Length() > maxSize) {
webgl->ErrorInvalidValue("%s: Identifier is %d characters long, exceeds the"
" maximum allowed length of %d characters.",
funcName, name.Length(), maxSize);
return false;
}
if (!ValidateGLSLString(name, webgl, funcName))
return false;
nsString prefix1 = NS_LITERAL_STRING("webgl_");
nsString prefix2 = NS_LITERAL_STRING("_webgl_");
if (Substring(name, 0, prefix1.Length()).Equals(prefix1) ||
Substring(name, 0, prefix2.Length()).Equals(prefix2))
{
webgl->ErrorInvalidOperation("%s: String contains a reserved GLSL prefix.",
funcName);
return false;
}
return true;
}
} // namespace mozilla

View File

@ -27,226 +27,130 @@
#ifndef WEBGL_VALIDATE_STRINGS_H_
#define WEBGL_VALIDATE_STRINGS_H_
#include "WebGLContext.h"
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla {
class WebGLContext;
// The following code was taken from the WebKit WebGL implementation,
// which can be found here:
// http://trac.webkit.org/browser/trunk/Source/WebCore/html/canvas/WebGLRenderingContext.cpp?rev=93625#L121
// Note that some modifications were done to adapt it to Mozilla.
/****** BEGIN CODE TAKEN FROM WEBKIT ******/
bool WebGLContext::ValidateGLSLCharacter(char16_t c)
// Strips comments from shader text. This allows non-ASCII characters
// to be used in comments without potentially breaking OpenGL
// implementations not expecting characters outside the GLSL ES set.
class StripComments {
public:
explicit StripComments(const nsAString& str)
: m_parseState(BeginningOfLine)
, m_end(str.EndReading())
, m_current(str.BeginReading())
, m_position(0)
{
// Printing characters are valid except " $ ` @ \ ' DEL.
if (c >= 32 && c <= 126 &&
c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' && c != '\'')
{
return true;
}
// Horizontal tab, line feed, vertical tab, form feed, carriage return
// are also valid.
if (c >= 9 && c <= 13) {
return true;
}
return false;
m_result.SetLength(str.Length());
parse();
}
// Strips comments from shader text. This allows non-ASCII characters
// to be used in comments without potentially breaking OpenGL
// implementations not expecting characters outside the GLSL ES set.
class StripComments {
public:
explicit StripComments(const nsAString& str)
: m_parseState(BeginningOfLine)
, m_end(str.EndReading())
, m_current(str.BeginReading())
, m_position(0)
{
m_result.SetLength(str.Length());
parse();
const nsTArray<char16_t>& result()
{
return m_result;
}
size_t length()
{
return m_position;
}
private:
bool hasMoreCharacters()
{
return (m_current < m_end);
}
void parse()
{
while (hasMoreCharacters()) {
process(current());
// process() might advance the position.
if (hasMoreCharacters())
advance();
}
}
const nsTArray<char16_t>& result()
{
return m_result;
}
void process(char16_t);
size_t length()
{
return m_position;
}
bool peek(char16_t& character)
{
if (m_current + 1 >= m_end)
return false;
character = *(m_current + 1);
return true;
}
private:
bool hasMoreCharacters()
{
return (m_current < m_end);
}
char16_t current()
{
//ASSERT(m_position < m_length);
return *m_current;
}
void parse()
{
while (hasMoreCharacters()) {
process(current());
// process() might advance the position.
if (hasMoreCharacters())
advance();
}
}
void advance()
{
++m_current;
}
void process(char16_t);
bool isNewline(char16_t character)
{
// Don't attempt to canonicalize newline related characters.
return (character == '\n' || character == '\r');
}
bool peek(char16_t& character)
{
if (m_current + 1 >= m_end)
return false;
character = *(m_current + 1);
return true;
}
void emit(char16_t character)
{
m_result[m_position++] = character;
}
char16_t current()
{
//ASSERT(m_position < m_length);
return *m_current;
}
enum ParseState {
// Have not seen an ASCII non-whitespace character yet on
// this line. Possible that we might see a preprocessor
// directive.
BeginningOfLine,
void advance()
{
++m_current;
}
// Have seen at least one ASCII non-whitespace character
// on this line.
MiddleOfLine,
bool isNewline(char16_t character)
{
// Don't attempt to canonicalize newline related characters.
return (character == '\n' || character == '\r');
}
// Handling a preprocessor directive. Passes through all
// characters up to the end of the line. Disables comment
// processing.
InPreprocessorDirective,
void emit(char16_t character)
{
m_result[m_position++] = character;
}
// Handling a single-line comment. The comment text is
// replaced with a single space.
InSingleLineComment,
enum ParseState {
// Have not seen an ASCII non-whitespace character yet on
// this line. Possible that we might see a preprocessor
// directive.
BeginningOfLine,
// Have seen at least one ASCII non-whitespace character
// on this line.
MiddleOfLine,
// Handling a preprocessor directive. Passes through all
// characters up to the end of the line. Disables comment
// processing.
InPreprocessorDirective,
// Handling a single-line comment. The comment text is
// replaced with a single space.
InSingleLineComment,
// Handling a multi-line comment. Newlines are passed
// through to preserve line numbers.
InMultiLineComment
};
ParseState m_parseState;
const char16_t* m_end;
const char16_t* m_current;
size_t m_position;
nsTArray<char16_t> m_result;
// Handling a multi-line comment. Newlines are passed
// through to preserve line numbers.
InMultiLineComment
};
void StripComments::process(char16_t c)
{
if (isNewline(c)) {
// No matter what state we are in, pass through newlines
// so we preserve line numbers.
emit(c);
if (m_parseState != InMultiLineComment)
m_parseState = BeginningOfLine;
return;
}
char16_t temp = 0;
switch (m_parseState) {
case BeginningOfLine:
// If it's an ASCII space.
if (c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9))) {
emit(c);
break;
}
if (c == '#') {
m_parseState = InPreprocessorDirective;
emit(c);
break;
}
// Transition to normal state and re-handle character.
m_parseState = MiddleOfLine;
process(c);
break;
case MiddleOfLine:
if (c == '/' && peek(temp)) {
if (temp == '/') {
m_parseState = InSingleLineComment;
emit(' ');
advance();
break;
}
if (temp == '*') {
m_parseState = InMultiLineComment;
// Emit the comment start in case the user has
// an unclosed comment and we want to later
// signal an error.
emit('/');
emit('*');
advance();
break;
}
}
emit(c);
break;
case InPreprocessorDirective:
// No matter what the character is, just pass it
// through. Do not parse comments in this state. This
// might not be the right thing to do long term, but it
// should handle the #error preprocessor directive.
emit(c);
break;
case InSingleLineComment:
// The newline code at the top of this function takes care
// of resetting our state when we get out of the
// single-line comment. Swallow all other characters.
break;
case InMultiLineComment:
if (c == '*' && peek(temp) && temp == '/') {
emit('*');
emit('/');
m_parseState = MiddleOfLine;
advance();
break;
}
// Swallow all other characters. Unclear whether we may
// want or need to just emit a space per character to try
// to preserve column numbers for debugging purposes.
break;
}
}
ParseState m_parseState;
const char16_t* m_end;
const char16_t* m_current;
size_t m_position;
nsTArray<char16_t> m_result;
};
/****** END CODE TAKEN FROM WEBKIT ******/
bool ValidateGLSLString(const nsAString& string, WebGLContext* webgl,
const char* funcName);
bool ValidateGLSLVariableName(const nsAString& name, WebGLContext* webgl,
const char* funcName);
} // namespace mozilla
#endif // WEBGL_VALIDATE_STRINGS_H_

View File

@ -108,11 +108,13 @@ UNIFIED_SOURCES += [
'WebGLSampler.cpp',
'WebGLShader.cpp',
'WebGLShaderPrecisionFormat.cpp',
'WebGLShaderValidator.cpp',
'WebGLSync.cpp',
'WebGLTexelConversions.cpp',
'WebGLTexture.cpp',
'WebGLTransformFeedback.cpp',
'WebGLUniformLocation.cpp',
'WebGLValidateStrings.cpp',
'WebGLVertexArray.cpp',
'WebGLVertexArrayFake.cpp',
'WebGLVertexArrayGL.cpp',

View File

@ -68,15 +68,13 @@ function shouldBeEmptyString(command) {
}
for (var i = 0; i < arguments.length; ++i) {
var func, func2, func3;
var func, func2;
if (arguments[i].throws) {
func = shouldThrow;
func2 = shouldThrow;
func3 = shouldThrow;
} else {
func = shouldBeUndefined;
func2 = shouldBeNull;
func3 = shouldBeEmptyString;
}
argument = arguments[i].value;
func("context.compileShader(argument)");
@ -98,15 +96,14 @@ for (var i = 0; i < arguments.length; ++i) {
func("context.uniform2iv(argument, new Int32Array([0, 0]))");
func("context.uniformMatrix2fv(argument, false, new Float32Array([0.0, 0.0, 0.0, 0.0]))");
func2("context.getProgramInfoLog(argument)");
func2("context.getProgramParameter(argument, 0)");
func2("context.getShaderInfoLog(argument)");
func2("context.getShaderParameter(argument, 0)");
func2("context.getShaderSource(argument)");
func2("context.getUniform(argument, loc)");
func2("context.getUniform(program, argument)");
func2("context.getUniformLocation(argument, 'u_modelViewProjMatrix')");
func3("context.getProgramInfoLog(argument)");
func3("context.getShaderInfoLog(argument)");
func3("context.getShaderSource(argument)");
}
successfullyParsed = true;

View File

@ -129,6 +129,7 @@ function GetExpectedTestFailSet() {
failSet['conformance/extensions/oes-texture-float.html'] = true;
failSet['conformance/glsl/functions/glsl-function-sin.html'] = true;
failSet['conformance/misc/object-deletion-behaviour.html'] = true;
failSet['conformance/programs/program-test.html'] = true;
failSet['conformance/textures/tex-image-and-sub-image-2d-with-video.html'] = true;
failSet['conformance/textures/texture-mips.html'] = true;
failSet['conformance/textures/texture-npot.html'] = true;

View File

@ -57,6 +57,7 @@ var gTestIndex = 0;
const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
const isOSXYosemite = navigator.userAgent.indexOf("Mac OS X 10.10") != -1;
function nextTest() {
if (isWinXP) {
@ -66,7 +67,7 @@ function nextTest() {
}
if (testWindow) {
testWindow.close();
if (isOSXLion || isOSXMtnLion) {
if (isOSXLion || isOSXMtnLion || isOSXYosemite) {
// On OS X Lion, tests cause problems. Timeouts are a bad way to get around
// the problem and may lead to future [orange], but they are the only option
// at this point.
@ -98,7 +99,7 @@ function runNextTest() {
}
}
if (isOSXLion || isOSXMtnLion) {
if (isOSXLion || isOSXMtnLion || isOSXYosemite) {
todo(false, "Can't reliably run full-screen tests on OS X (bug 900453 comment 18 & bug 802504)");
} else {
try {

View File

@ -582,6 +582,8 @@ MediaDecoderStateMachine::NeedToDecodeVideo()
AssertCurrentThreadInMonitor();
return IsVideoDecoding() &&
((mState == DECODER_STATE_SEEKING && mDecodeToSeekTarget) ||
(mState == DECODER_STATE_DECODING_FIRSTFRAME &&
IsVideoDecoding() && VideoQueue().GetSize() == 0) ||
(!mMinimizePreroll && !HaveEnoughDecodedVideo()));
}
@ -681,6 +683,8 @@ MediaDecoderStateMachine::NeedToDecodeAudio()
return IsAudioDecoding() &&
((mState == DECODER_STATE_SEEKING && mDecodeToSeekTarget) ||
(mState == DECODER_STATE_DECODING_FIRSTFRAME &&
IsAudioDecoding() && AudioQueue().GetSize() == 0) ||
(!mMinimizePreroll &&
!HaveEnoughDecodedAudio(mAmpleAudioThresholdUsecs * mPlaybackRate) &&
(mState != DECODER_STATE_SEEKING || mDecodeToSeekTarget)));

View File

@ -133,7 +133,7 @@ public:
MediaByteRange(int64_t aStart, int64_t aEnd)
: mStart(aStart), mEnd(aEnd)
{
NS_ASSERTION(mStart < mEnd, "Range should end after start!");
NS_ASSERTION(mStart <= mEnd, "Range should end after start!");
}
explicit MediaByteRange(TimestampedMediaByteRange& aByteRange);

View File

@ -268,4 +268,10 @@ MediaSourceDecoder::SetCDMProxy(CDMProxy* aProxy)
}
#endif
bool
MediaSourceDecoder::IsActiveReader(MediaDecoderReader* aReader)
{
return mReader->IsActiveReader(aReader);
}
} // namespace mozilla

View File

@ -74,6 +74,10 @@ public:
MediaSourceReader* GetReader() { return mReader; }
// Returns true if aReader is a currently active audio or video
// reader in this decoders MediaSourceReader.
bool IsActiveReader(MediaDecoderReader* aReader);
private:
// The owning MediaSource holds a strong reference to this decoder, and
// calls Attach/DetachMediaSource on this decoder to set and clear

View File

@ -908,4 +908,11 @@ MediaSourceReader::SetCDMProxy(CDMProxy* aProxy)
}
#endif
bool
MediaSourceReader::IsActiveReader(MediaDecoderReader* aReader)
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
return aReader == mVideoReader.get() || aReader == mAudioReader.get();
}
} // namespace mozilla

View File

@ -138,6 +138,9 @@ public:
(!mVideoReader || mVideoReader->IsAsync());
}
// Returns true if aReader is a currently active audio or video
bool IsActiveReader(MediaDecoderReader* aReader);
private:
// Switch the current audio/video reader to the reader that
// contains aTarget (or up to aError after target). Both

View File

@ -105,19 +105,36 @@ public:
// Tries to evict at least aSizeToEvict from the queue up until
// aOffset. Returns amount evicted.
uint32_t Evict(uint64_t aOffset, uint32_t aSizeToEvict) {
SBR_DEBUG("ResourceQueue(%p)::Evict(aOffset=%llu, aSizeToEvict=%u)",
this, aOffset, aSizeToEvict);
return EvictBefore(std::min(aOffset, (uint64_t)aSizeToEvict));
}
uint32_t EvictBefore(uint64_t aOffset) {
SBR_DEBUG("ResourceQueue(%p)::EvictBefore(%llu)", this, aOffset);
uint32_t evicted = 0;
while (ResourceItem* item = ResourceAt(0)) {
if (item->mData.Length() + mOffset > aOffset) {
SBR_DEBUG("ResourceQueue(%p)::EvictBefore item=%p length=%d offset=%llu",
this, item, item->mData.Length(), mOffset);
if (item->mData.Length() + mOffset >= aOffset) {
break;
}
mOffset += item->mData.Length();
evicted += item->mData.Length();
SBR_DEBUGV("ResourceQueue(%p)::Evict(%llu, %u) removed chunk length=%u",
this, aOffset, aSizeToEvict, item->mData.Length());
delete PopFront();
if (aSizeToEvict && evicted >= aSizeToEvict) {
break;
}
}
return evicted;
}
uint32_t EvictAll() {
SBR_DEBUG("ResourceQueue(%p)::EvictAll()", this);
uint32_t evicted = 0;
while (ResourceItem* item = ResourceAt(0)) {
SBR_DEBUG("ResourceQueue(%p)::EvictAll item=%p length=%d offset=%llu",
this, item, item->mData.Length(), mOffset);
mOffset += item->mData.Length();
evicted += item->mData.Length();
delete PopFront();
}
return evicted;
}

View File

@ -387,14 +387,17 @@ SourceBuffer::PrepareAppend(ErrorResult& aRv)
// TODO: Make the eviction threshold smaller for audio-only streams.
// TODO: Drive evictions off memory pressure notifications.
// TODO: Consider a global eviction threshold rather than per TrackBuffer.
bool evicted = mTrackBuffer->EvictData(mEvictionThreshold);
double newBufferStartTime = 0.0;
bool evicted =
mTrackBuffer->EvictData(mMediaSource->GetDecoder()->GetCurrentTime(),
mEvictionThreshold, &newBufferStartTime);
if (evicted) {
MSE_DEBUG("SourceBuffer(%p)::AppendData Evict; current buffered start=%f",
this, GetBufferedStart());
// We notify that we've evicted from the time range 0 through to
// the current start point.
mMediaSource->NotifyEvicted(0.0, GetBufferedStart());
mMediaSource->NotifyEvicted(0.0, newBufferStartTime);
}
// TODO: Test buffer full flag.

View File

@ -172,11 +172,12 @@ SourceBufferResource::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCo
}
uint32_t
SourceBufferResource::EvictData(uint32_t aThreshold)
SourceBufferResource::EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold)
{
SBR_DEBUG("SourceBufferResource(%p)::EvictData(aThreshold=%u)", this, aThreshold);
SBR_DEBUG("SourceBufferResource(%p)::EvictData(aPlaybackOffset=%llu,"
"aThreshold=%u)", this, aPlaybackOffset, aThreshold);
ReentrantMonitorAutoEnter mon(mMonitor);
return mInputBuffer.Evict(mOffset, aThreshold);
return mInputBuffer.Evict(aPlaybackOffset, aThreshold);
}
void
@ -186,10 +187,18 @@ SourceBufferResource::EvictBefore(uint64_t aOffset)
ReentrantMonitorAutoEnter mon(mMonitor);
// If aOffset is past the current playback offset we don't evict.
if (aOffset < mOffset) {
mInputBuffer.Evict(aOffset, 0);
mInputBuffer.EvictBefore(aOffset);
}
}
uint32_t
SourceBufferResource::EvictAll()
{
SBR_DEBUG("SourceBufferResource(%p)::EvictAll()", this);
ReentrantMonitorAutoEnter mon(mMonitor);
return mInputBuffer.EvictAll();
}
void
SourceBufferResource::AppendData(const uint8_t* aData, uint32_t aLength)
{

View File

@ -115,11 +115,14 @@ public:
void Ended();
// Remove data from resource if it holds more than the threshold
// number of bytes. Returns amount evicted.
uint32_t EvictData(uint32_t aThreshold);
uint32_t EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold);
// Remove data from resource before the given offset.
void EvictBefore(uint64_t aOffset);
// Remove all data from the resource
uint32_t EvictAll();
// Returns the amount of data currently retained by this resource.
int64_t GetSize() {
ReentrantMonitorAutoEnter mon(mMonitor);

View File

@ -33,6 +33,11 @@ extern PRLogModuleInfo* GetMediaSourceAPILog();
#define MSE_API(...)
#endif
// Time in seconds to substract from the current time when deciding the
// time point to evict data before in a decoder. This is used to help
// precent evicting the current playback point.
#define MSE_EVICT_THRESHOLD_TIME 2.0
namespace mozilla {
TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType)
@ -240,11 +245,17 @@ public:
};
bool
TrackBuffer::EvictData(uint32_t aThreshold)
TrackBuffer::EvictData(double aPlaybackTime,
uint32_t aThreshold,
double* aBufferStartTime)
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
if (!mCurrentDecoder) {
return false;
}
int64_t totalSize = 0;
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
totalSize += mDecoders[i]->GetResource()->GetSize();
@ -255,46 +266,85 @@ TrackBuffer::EvictData(uint32_t aThreshold)
return false;
}
// Get a list of initialized decoders, sorted by their start times.
// Get a list of initialized decoders
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 -= decoders[i]->GetResource()->EvictData(toEvict);
if (!decoders[i]->GetResource()->GetSize() &&
decoders[i] != mCurrentDecoder) {
RemoveDecoder(decoders[i]);
bool pastCurrentDecoder = true;
for (; i < decoders.Length() && toEvict > 0; ++i) {
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
decoders[i]->GetBuffered(buffered);
bool onCurrent = decoders[i] == mCurrentDecoder;
if (onCurrent) {
pastCurrentDecoder = false;
}
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);
MSE_DEBUG("TrackBuffer(%p)::EvictData decoder=%u/%u threshold=%u "
"toEvict=%lld current=%s pastCurrent=%s",
this, i, decoders.Length(), aThreshold, toEvict,
onCurrent ? "true" : "false",
pastCurrentDecoder ? "true" : "false");
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;
if (pastCurrentDecoder
&& !mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
// Remove data from older decoders than the current one.
// Don't remove data if it is currently active.
MSE_DEBUG("TrackBuffer(%p)::EvictData evicting all before start "
"bufferedStart=%f bufferedEnd=%f aPlaybackTime=%f size=%lld",
this, buffered->GetStartTime(), buffered->GetEndTime(),
aPlaybackTime, decoders[i]->GetResource()->GetSize());
toEvict -= decoders[i]->GetResource()->EvictAll();
} else {
// To ensure we don't evict data past the current playback position
// we apply a threshold of a few seconds back and evict data up to
// that point.
if (aPlaybackTime > MSE_EVICT_THRESHOLD_TIME) {
double time = aPlaybackTime - MSE_EVICT_THRESHOLD_TIME;
int64_t playbackOffset = decoders[i]->ConvertToByteOffset(time);
MSE_DEBUG("TrackBuffer(%p)::EvictData evicting some bufferedEnd=%f"
"aPlaybackTime=%f time=%f, playbackOffset=%lld size=%lld",
this, buffered->GetEndTime(), aPlaybackTime, time,
playbackOffset, decoders[i]->GetResource()->GetSize());
if (playbackOffset > 0) {
toEvict -= decoders[i]->GetResource()->EvictData(playbackOffset,
toEvict);
}
}
}
}
return toEvict < (totalSize - aThreshold);
// Remove decoders that have no data in them
for (i = 0; i < decoders.Length(); ++i) {
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
decoders[i]->GetBuffered(buffered);
MSE_DEBUG("TrackBuffer(%p):EvictData maybe remove empty decoders=%d "
"size=%lld start=%f end=%f",
this, i, decoders[i]->GetResource()->GetSize(),
buffered->GetStartTime(), buffered->GetEndTime());
if (decoders[i] == mCurrentDecoder
|| mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
continue;
}
if (decoders[i]->GetResource()->GetSize() == 0 ||
buffered->GetStartTime() < 0.0 ||
buffered->GetEndTime() < 0.0) {
MSE_DEBUG("TrackBuffer(%p):EvictData remove empty decoders=%d", this, i);
RemoveDecoder(decoders[i]);
}
}
bool evicted = toEvict < (totalSize - aThreshold);
if (evicted) {
nsRefPtr<TimeRanges> ranges = new TimeRanges();
mCurrentDecoder->GetBuffered(ranges);
*aBufferStartTime = std::max(0.0, ranges->GetStartTime());
}
return evicted;
}
void
@ -307,10 +357,6 @@ TrackBuffer::EvictBefore(double aTime)
if (endOffset > 0) {
MSE_DEBUG("TrackBuffer(%p)::EvictBefore decoder=%u offset=%lld", this, i, endOffset);
mInitializedDecoders[i]->GetResource()->EvictBefore(endOffset);
if (!mInitializedDecoders[i]->GetResource()->GetSize() &&
mInitializedDecoders[i] != mCurrentDecoder) {
RemoveDecoder(mInitializedDecoders[i]);
}
}
}
}
@ -646,6 +692,9 @@ TrackBuffer::RemoveDecoder(SourceBufferDecoder* aDecoder)
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
// There should be no other references to the decoder. Assert that
// we aren't using it in the MediaSourceReader.
MOZ_ASSERT(!mParentDecoder->IsActiveReader(aDecoder->GetReader()));
mInitializedDecoders.RemoveElement(aDecoder);
mDecoders.RemoveElement(aDecoder);

View File

@ -40,7 +40,17 @@ public:
// NotifyDataArrived on the decoder to keep buffered range computation up
// to date. Returns false if the append failed.
bool AppendData(const uint8_t* aData, uint32_t aLength, int64_t aTimestampOffset /* microseconds */);
bool EvictData(uint32_t aThreshold);
// Evicts data held in the current decoders SourceBufferResource from the
// start of the buffer through to aPlaybackTime. aThreshold is used to
// bound the data being evicted. It will not evict more than aThreshold
// bytes. aBufferStartTime contains the new start time of the current
// decoders buffered data after the eviction. Returns true if data was
// evicted.
bool EvictData(double aPlaybackTime, uint32_t aThreshold, double* aBufferStartTime);
// Evicts data held in all the decoders SourceBufferResource from the start
// of the buffer through to aTime.
void EvictBefore(double aTime);
// Returns the highest end time of all of the buffered ranges in the

View File

@ -1488,7 +1488,8 @@ function PeerConnectionWrapper(label, configuration, h264) {
this.dataChannels = [ ];
this.onAddStreamFired = false;
this.onAddStreamAudioCounter = 0;
this.onAddStreamVideoCounter = 0;
this.addStreamCallbacks = {};
this._local_ice_candidates = [];
@ -1534,15 +1535,15 @@ function PeerConnectionWrapper(label, configuration, h264) {
*/
this._pc.onaddstream = function (event) {
info(self + ": 'onaddstream' event fired for " + JSON.stringify(event.stream));
// TODO: remove this once Bugs 998552 and 998546 are closed
self.onAddStreamFired = true;
var type = '';
if (event.stream.getAudioTracks().length > 0) {
type = 'audio';
self.onAddStreamAudioCounter += event.stream.getAudioTracks().length;
}
if (event.stream.getVideoTracks().length > 0) {
type += 'video';
self.onAddStreamVideoCounter += event.stream.getVideoTracks().length;
}
self.attachMedia(event.stream, type, 'remote');
@ -2251,12 +2252,8 @@ PeerConnectionWrapper.prototype = {
*/
checkMediaTracks : function PCW_checkMediaTracks(constraintsRemote, onSuccess) {
var self = this;
var addStreamTimeout = null;
function _checkMediaTracks(constraintsRemote, onSuccess) {
if (addStreamTimeout !== null) {
clearTimeout(addStreamTimeout);
}
var localConstraintAudioTracks =
self.countAudioTracksInMediaConstraint(self.constraints);
@ -2293,23 +2290,35 @@ PeerConnectionWrapper.prototype = {
self.countAudioTracksInMediaConstraint(constraintsRemote) +
self.countVideoTracksInMediaConstraint(constraintsRemote);
// TODO: remove this once Bugs 998552 and 998546 are closed
if ((self.onAddStreamFired) || (expectedRemoteTracks == 0)) {
_checkMediaTracks(constraintsRemote, onSuccess);
} else {
info(self + " checkMediaTracks() got called before onAddStream fired");
// we rely on the outer mochitest timeout to catch the case where
// TODO: this whole counting of streams should be replaced with comparing
// media stream objects IDs and what we got in the SDP (bug 1089798)
function _compareReceivedAndExpectedTracks(constraintsRemote, onSuccess) {
var receivedRemoteTracks =
self.onAddStreamAudioCounter + self.onAddStreamVideoCounter;
if (receivedRemoteTracks === expectedRemoteTracks) {
_checkMediaTracks(constraintsRemote, onSuccess);
} else if (receivedRemoteTracks > expectedRemoteTracks) {
ok(false, "Received more streams " + receivedRemoteTracks +
" then expected " + expectedRemoteTracks);
_checkMediaTracks(constraintsRemote, onSuccess);
} else {
info("Still waiting for more remote streams to arrive (" +
receivedRemoteTracks + " vs " + expectedRemoteTracks + ")");
}
}
if (expectedRemoteTracks > (self.onAddStreamAudioCounter +
self.onAddStreamVideoCounter)) {
// This installs a callback handler for every time onaddstrem fires.
// We rely on the outer mochitest timeout to catch the case where
// onaddstream never fires
self.addStreamCallbacks.checkMediaTracks = function() {
_checkMediaTracks(constraintsRemote, onSuccess);
_compareReceivedAndExpectedTracks(constraintsRemote, onSuccess);
};
addStreamTimeout = setTimeout(function () {
ok(self.onAddStreamFired, self + " checkMediaTracks() timed out waiting for onaddstream event to fire");
if (!self.onAddStreamFired) {
onSuccess();
}
}, 60000);
}
_compareReceivedAndExpectedTracks(constraintsRemote, onSuccess);
},
verifySdp : function PCW_verifySdp(desc, expectedType, offerConstraintsList,

View File

@ -205,12 +205,28 @@ protected:
JS::Rooted<JSObject*> rootedThenable(cx, mThenable);
mThen->Call(rootedThenable, resolveFunc, rejectFunc, rv,
CallbackObject::eRethrowExceptions);
CallbackObject::eRethrowExceptions,
mPromise->Compartment());
rv.WouldReportJSException();
if (rv.IsJSException()) {
if (rv.Failed()) {
JS::Rooted<JS::Value> exn(cx);
rv.StealJSException(cx, &exn);
if (rv.IsJSException()) {
// Enter the compartment of mPromise before stealing the JS exception,
// since the StealJSException call will use the current compartment for
// a security check that determines how much of the stack we're allowed
// to see and we'll be exposing that stack to consumers of mPromise.
JSAutoCompartment ac(cx, mPromise->GlobalJSObject());
rv.StealJSException(cx, &exn);
} else {
// Convert the ErrorResult to a JS exception object that we can reject
// ourselves with. This will be exactly the exception that would get
// thrown from a binding method whose ErrorResult ended up with
// whatever is on "rv" right now.
JSAutoCompartment ac(cx, mPromise->GlobalJSObject());
DebugOnly<bool> conversionResult = ToJSValue(cx, rv, &exn);
MOZ_ASSERT(conversionResult);
}
bool couldMarkAsCalled = MarkAsCalledIfNotCalledBefore(cx, resolveFunc);
@ -562,12 +578,20 @@ Promise::CallInitFunction(const GlobalObject& aGlobal,
return;
}
aInit.Call(resolveFunc, rejectFunc, aRv, CallbackObject::eRethrowExceptions);
aInit.Call(resolveFunc, rejectFunc, aRv, CallbackObject::eRethrowExceptions,
Compartment());
aRv.WouldReportJSException();
if (aRv.IsJSException()) {
JS::Rooted<JS::Value> value(cx);
aRv.StealJSException(cx, &value);
{ // scope for ac
// Enter the compartment of our global before stealing the JS exception,
// since the StealJSException call will use the current compartment for
// a security check that determines how much of the stack we're allowed
// to see, and we'll be exposing that stack to consumers of this promise.
JSAutoCompartment ac(cx, GlobalJSObject());
aRv.StealJSException(cx, &value);
}
// we want the same behavior as this JS implementation:
// function Promise(arg) { try { arg(a, b); } catch (e) { this.reject(e); }}
@ -578,6 +602,12 @@ Promise::CallInitFunction(const GlobalObject& aGlobal,
MaybeRejectInternal(cx, value);
}
// Else aRv is an error. We _could_ reject ourselves with that error, but
// we're just going to propagate aRv out to the binding code, which will then
// throw us away and create a new promise rejected with the error on aRv. So
// there's no need to worry about rejecting ourselves here; the bindings
// will do the right thing.
}
/* static */ already_AddRefed<Promise>
@ -936,6 +966,18 @@ Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
AppendCallbacks(resolveCb, rejectCb);
}
JSObject*
Promise::GlobalJSObject() const
{
return mGlobal->GetGlobalJSObject();
}
JSCompartment*
Promise::Compartment() const
{
return js::GetObjectCompartment(GlobalJSObject());
}
void
Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
PromiseCallback* aRejectCallback)

View File

@ -19,6 +19,7 @@
#include "nsWrapperCache.h"
#include "nsAutoPtr.h"
#include "js/TypeDecls.h"
#include "jspubtd.h"
#include "mozilla/dom/workers/bindings/WorkerFeature.h"
@ -176,6 +177,10 @@ public:
void AppendNativeHandler(PromiseNativeHandler* aRunnable);
JSObject* GlobalJSObject() const;
JSCompartment* Compartment() const;
protected:
// Do NOT call this unless you're Promise::Create. I wish we could enforce
// that from inside this class too, somehow.

View File

@ -205,18 +205,37 @@ WrapperPromiseCallback::Call(JSContext* aCx,
// PromiseReactionTask step 6
JS::Rooted<JS::Value> retValue(aCx);
mCallback->Call(value, &retValue, rv, CallbackObject::eRethrowExceptions);
mCallback->Call(value, &retValue, rv, CallbackObject::eRethrowExceptions,
mNextPromise->Compartment());
rv.WouldReportJSException();
// PromiseReactionTask step 7
if (rv.Failed() && rv.IsJSException()) {
if (rv.Failed()) {
JS::Rooted<JS::Value> value(aCx);
rv.StealJSException(aCx, &value);
if (rv.IsJSException()) {
{ // scope for ac
// Enter the compartment of mNextPromise before stealing the JS
// exception, since the StealJSException call will use the current
// compartment for a security check that determines how much of the
// stack we're allowed to see and we'll be exposing that stack to
// consumers of mPromise.
JSAutoCompartment ac(aCx, mNextPromise->GlobalJSObject());
rv.StealJSException(aCx, &value);
}
if (!JS_WrapValue(aCx, &value)) {
NS_WARNING("Failed to wrap value into the right compartment.");
return;
if (!JS_WrapValue(aCx, &value)) {
NS_WARNING("Failed to wrap value into the right compartment.");
return;
}
} else {
// Convert the ErrorResult to a JS exception object that we can reject
// ourselves with. This will be exactly the exception that would get
// thrown from a binding method whose ErrorResult ended up with whatever
// is on "rv" right now.
JSAutoCompartment ac(aCx, mNextPromise->GlobalJSObject());
DebugOnly<bool> conversionResult = ToJSValue(aCx, rv, &value);
MOZ_ASSERT(conversionResult);
}
mNextPromise->RejectInternal(aCx, value);

View File

@ -70,6 +70,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
const isOSXYosemite = navigator.userAgent.indexOf("Mac OS X 10.10") != -1;
const isWin8 = navigator.userAgent.indexOf("Windows NT 6.2") != -1;
function finish() {
@ -90,8 +91,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
finish();
return;
}
if (isOSXLion || isOSXMtnLion) {
todo(false, "Can't reliably run full-screen tests on OS X Lion or Mountain Lion, see bug 744125");
if (isOSXLion || isOSXMtnLion || isOSXYosemite) {
todo(false, "Can't reliably run full-screen tests on OS X Lion or Mountain Lion or Yosemite, see bug 744125");
finish();
return;
}

View File

@ -55,4 +55,15 @@ interface TestInterfaceJS {
[Throws]
void testThrowDOMException();
// Tests for promise-rejection behavior
Promise<void> testPromiseWithThrowingChromePromiseInit();
Promise<void> testPromiseWithThrowingContentPromiseInit(PromiseInit func);
Promise<void> testPromiseWithDOMExceptionThrowingPromiseInit();
Promise<void> testPromiseWithThrowingChromeThenFunction();
Promise<void> testPromiseWithThrowingContentThenFunction(AnyCallback func);
Promise<void> testPromiseWithDOMExceptionThrowingThenFunction();
Promise<void> testPromiseWithThrowingChromeThenable();
Promise<void> testPromiseWithThrowingContentThenable(object thenable);
Promise<void> testPromiseWithDOMExceptionThrowingThenable();
};

View File

@ -760,8 +760,6 @@ XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
}
static const PLDHashTableOps gOps = {
PL_DHashAllocTable,
PL_DHashFreeTable,
PL_DHashVoidPtrKeyStub,
PL_DHashMatchEntryStub,
PL_DHashMoveEntryStub,

View File

@ -16,8 +16,6 @@ using namespace mozilla;
const PLDHashTableOps nsCommandParams::sHashOps =
{
PL_DHashAllocTable,
PL_DHashFreeTable,
HashKey,
HashMatchEntry,
HashMoveEntry,

View File

@ -902,7 +902,11 @@ mozInlineSpellChecker::SpellCheckAfterEditorChange(
nsresult
mozInlineSpellChecker::SpellCheckRange(nsIDOMRange* aRange)
{
NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
if (!mSpellCheck) {
NS_WARN_IF_FALSE(mPendingSpellCheck,
"Trying to spellcheck, but checking seems to be disabled");
return NS_ERROR_NOT_INITIALIZED;
}
mozInlineSpellStatus status(this);
nsRange* range = static_cast<nsRange*>(aRange);

View File

@ -2015,7 +2015,7 @@ public:
AFTER_GL_CALL;
}
void fShaderSource(GLuint shader, GLsizei count, const GLchar** strings, const GLint* lengths) {
void fShaderSource(GLuint shader, GLsizei count, const GLchar* const* strings, const GLint* lengths) {
BEFORE_GL_CALL;
mSymbols.fShaderSource(shader, count, strings, lengths);
AFTER_GL_CALL;

View File

@ -308,7 +308,7 @@ struct GLContextSymbols
PFNGETSHADERPRECISIONFORMAT fGetShaderPrecisionFormat;
typedef void (GLAPIENTRY * PFNGLGETSHADERSOURCEPROC) (GLint obj, GLsizei maxLength, GLsizei* length, GLchar* source);
PFNGLGETSHADERSOURCEPROC fGetShaderSource;
typedef void (GLAPIENTRY * PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar** strings, const GLint* lengths);
typedef void (GLAPIENTRY * PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* const* strings, const GLint* lengths);
PFNGLSHADERSOURCEPROC fShaderSource;
typedef void (GLAPIENTRY * PFNGLBINDFRAMEBUFFER) (GLenum target, GLuint framebuffer);

View File

@ -622,8 +622,6 @@ public:
: mWriteNeeded(false)
{
mOps = (PLDHashTableOps) {
PL_DHashAllocTable,
PL_DHashFreeTable,
StringHash,
HashMatchEntry,
MoveEntry,

View File

@ -237,16 +237,15 @@ private:
DECL_GFX_PREF(Live, "image.high_quality_downscaling.enabled", ImageHQDownscalingEnabled, bool, false);
DECL_GFX_PREF(Live, "image.high_quality_downscaling.min_factor", ImageHQDownscalingMinFactor, uint32_t, 1000);
DECL_GFX_PREF(Live, "image.high_quality_upscaling.max_size", ImageHQUpscalingMaxSize, uint32_t, 20971520);
DECL_GFX_PREF(Live, "image.mem.decode_bytes_at_a_time", ImageMemDecodeBytesAtATime, uint32_t, 200000);
DECL_GFX_PREF(Once, "image.mem.decode_bytes_at_a_time", ImageMemDecodeBytesAtATime, uint32_t, 200000);
DECL_GFX_PREF(Live, "image.mem.decodeondraw", ImageMemDecodeOnDraw, bool, false);
DECL_GFX_PREF(Live, "image.mem.discardable", ImageMemDiscardable, bool, false);
DECL_GFX_PREF(Live, "image.mem.max_ms_before_yield", ImageMemMaxMSBeforeYield, uint32_t, 400);
DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb", ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
DECL_GFX_PREF(Once, "image.mem.surfacecache.min_expiration_ms", ImageMemSurfaceCacheMinExpirationMS, uint32_t, 60*1000);
DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor", ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
DECL_GFX_PREF(Live, "image.mozsamplesize.enabled", ImageMozSampleSizeEnabled, bool, false);
DECL_GFX_PREF(Live, "image.multithreaded_decoding.limit", ImageMTDecodingLimit, int32_t, -1);
DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit", ImageMTDecodingLimit, int32_t, -1);
DECL_GFX_PREF(Once, "layers.acceleration.disabled", LayersAccelerationDisabled, bool, false);
DECL_GFX_PREF(Live, "layers.acceleration.draw-fps", LayersDrawFPS, bool, false);

View File

@ -38,7 +38,7 @@ GetBMPLog()
#define LINE(row) ((mBIH.height < 0) ? (-mBIH.height - (row)) : ((row) - 1))
#define PIXEL_OFFSET(row, col) (LINE(row) * mBIH.width + col)
nsBMPDecoder::nsBMPDecoder(RasterImage& aImage)
nsBMPDecoder::nsBMPDecoder(RasterImage* aImage)
: Decoder(aImage)
, mPos(0)
, mLOH(WIN_V3_HEADER_LENGTH)

View File

@ -23,7 +23,7 @@ class nsBMPDecoder : public Decoder
{
public:
explicit nsBMPDecoder(RasterImage& aImage);
explicit nsBMPDecoder(RasterImage* aImage);
~nsBMPDecoder();
// Specifies whether or not the BMP file will contain alpha data

View File

@ -68,7 +68,7 @@ namespace image {
//////////////////////////////////////////////////////////////////////
// GIF Decoder Implementation
nsGIFDecoder2::nsGIFDecoder2(RasterImage& aImage)
nsGIFDecoder2::nsGIFDecoder2(RasterImage* aImage)
: Decoder(aImage)
, mCurrentRow(-1)
, mLastFlushedRow(-1)

View File

@ -23,7 +23,7 @@ class nsGIFDecoder2 : public Decoder
{
public:
explicit nsGIFDecoder2(RasterImage& aImage);
explicit nsGIFDecoder2(RasterImage* aImage);
~nsGIFDecoder2();
virtual void WriteInternal(const char* aBuffer, uint32_t aCount) MOZ_OVERRIDE;

View File

@ -58,7 +58,7 @@ nsICODecoder::GetNumColors()
}
nsICODecoder::nsICODecoder(RasterImage& aImage)
nsICODecoder::nsICODecoder(RasterImage* aImage)
: Decoder(aImage)
{
mPos = mImageOffset = mCurrIcon = mNumIcons = mBPP = mRowBytes = 0;
@ -249,7 +249,7 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
}
uint16_t colorDepth = 0;
nsIntSize prefSize = mImage.GetRequestedResolution();
nsIntSize prefSize = mImage->GetRequestedResolution();
if (prefSize.width == 0 && prefSize.height == 0) {
prefSize.SizeTo(PREFICONSIZE, PREFICONSIZE);
}

View File

@ -23,7 +23,7 @@ class nsICODecoder : public Decoder
{
public:
explicit nsICODecoder(RasterImage& aImage);
explicit nsICODecoder(RasterImage* aImage);
virtual ~nsICODecoder();
// Obtains the width of the icon directory entry

View File

@ -15,7 +15,7 @@
namespace mozilla {
namespace image {
nsIconDecoder::nsIconDecoder(RasterImage& aImage)
nsIconDecoder::nsIconDecoder(RasterImage* aImage)
: Decoder(aImage),
mWidth(-1),
mHeight(-1),

View File

@ -38,7 +38,7 @@ class nsIconDecoder : public Decoder
{
public:
explicit nsIconDecoder(RasterImage& aImage);
explicit nsIconDecoder(RasterImage* aImage);
virtual ~nsIconDecoder();
virtual void WriteInternal(const char* aBuffer, uint32_t aCount) MOZ_OVERRIDE;

View File

@ -84,7 +84,7 @@ METHODDEF(void) my_error_exit (j_common_ptr cinfo);
#define MAX_JPEG_MARKER_LENGTH (((uint32_t)1 << 16) - 1)
nsJPEGDecoder::nsJPEGDecoder(RasterImage& aImage,
nsJPEGDecoder::nsJPEGDecoder(RasterImage* aImage,
Decoder::DecodeStyle aDecodeStyle)
: Decoder(aImage)
, mDecodeStyle(aDecodeStyle)
@ -237,7 +237,7 @@ nsJPEGDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
return; // I/O suspension
}
int sampleSize = mImage.GetRequestedSampleSize();
int sampleSize = mImage->GetRequestedSampleSize();
if (sampleSize > 0) {
mInfo.scale_num = 1;
mInfo.scale_denom = sampleSize;

View File

@ -52,7 +52,7 @@ struct Orientation;
class nsJPEGDecoder : public Decoder
{
public:
nsJPEGDecoder(RasterImage& aImage, Decoder::DecodeStyle aDecodeStyle);
nsJPEGDecoder(RasterImage* aImage, Decoder::DecodeStyle aDecodeStyle);
virtual ~nsJPEGDecoder();
virtual void InitInternal() MOZ_OVERRIDE;

View File

@ -107,7 +107,7 @@ nsPNGDecoder::AnimFrameInfo::AnimFrameInfo(png_structp aPNG, png_infop aInfo)
const uint8_t
nsPNGDecoder::pngSignatureBytes[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
nsPNGDecoder::nsPNGDecoder(RasterImage& aImage)
nsPNGDecoder::nsPNGDecoder(RasterImage* aImage)
: Decoder(aImage),
mPNG(nullptr), mInfo(nullptr),
mCMSLine(nullptr), interlacebuf(nullptr),

View File

@ -24,7 +24,7 @@ class RasterImage;
class nsPNGDecoder : public Decoder
{
public:
explicit nsPNGDecoder(RasterImage& aImage);
explicit nsPNGDecoder(RasterImage* aImage);
virtual ~nsPNGDecoder();
virtual void InitInternal() MOZ_OVERRIDE;

View File

@ -12,7 +12,6 @@
#include "nsCOMPtr.h"
#include "nsIObserverService.h"
#include "nsIThreadPool.h"
#include "nsProxyRelease.h"
#include "nsXPCOMCIDInternal.h"
#include "prsystem.h"
@ -42,102 +41,88 @@ public:
* Called by the DecodePool when it's done some significant portion of
* decoding, so that progress can be recorded and notifications can be sent.
*/
static void Dispatch(RasterImage* aImage)
static void Dispatch(RasterImage* aImage,
Progress aProgress,
const nsIntRect& aInvalidRect,
uint32_t aFlags)
{
nsCOMPtr<nsIRunnable> worker = new NotifyProgressWorker(aImage);
MOZ_ASSERT(aImage);
nsCOMPtr<nsIRunnable> worker =
new NotifyProgressWorker(aImage, aProgress, aInvalidRect, aFlags);
NS_DispatchToMainThread(worker);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
mImage->FinishedSomeDecoding(ShutdownReason::DONE);
mImage->NotifyProgress(mProgress, mInvalidRect, mFlags);
return NS_OK;
}
private:
explicit NotifyProgressWorker(RasterImage* aImage)
NotifyProgressWorker(RasterImage* aImage, Progress aProgress,
const nsIntRect& aInvalidRect, uint32_t aFlags)
: mImage(aImage)
, mProgress(aProgress)
, mInvalidRect(aInvalidRect)
, mFlags(aFlags)
{ }
nsRefPtr<RasterImage> mImage;
const Progress mProgress;
const nsIntRect mInvalidRect;
const uint32_t mFlags;
};
class NotifyDecodeCompleteWorker : public nsRunnable
{
public:
/**
* Called by the DecodePool when decoding is complete, so that final cleanup
* can be performed.
*/
static void Dispatch(Decoder* aDecoder)
{
MOZ_ASSERT(aDecoder);
nsCOMPtr<nsIRunnable> worker = new NotifyDecodeCompleteWorker(aDecoder);
NS_DispatchToMainThread(worker);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
DecodePool::Singleton()->NotifyDecodeComplete(mDecoder);
return NS_OK;
}
private:
explicit NotifyDecodeCompleteWorker(Decoder* aDecoder)
: mDecoder(aDecoder)
{ }
nsRefPtr<Decoder> mDecoder;
};
class DecodeWorker : public nsRunnable
{
public:
explicit DecodeWorker(RasterImage* aImage)
: mImage(aImage)
{ }
explicit DecodeWorker(Decoder* aDecoder)
: mDecoder(aDecoder)
{
MOZ_ASSERT(mDecoder);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
MOZ_ASSERT(!NS_IsMainThread());
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
// If we were interrupted, we shouldn't do any work.
if (mImage->mDecodeStatus == DecodeStatus::STOPPED) {
NotifyProgressWorker::Dispatch(mImage);
return NS_OK;
}
// If someone came along and synchronously decoded us, there's nothing for us to do.
if (!mImage->mDecoder || mImage->IsDecodeFinished()) {
NotifyProgressWorker::Dispatch(mImage);
return NS_OK;
}
mImage->mDecodeStatus = DecodeStatus::ACTIVE;
size_t oldByteCount = mImage->mDecoder->BytesDecoded();
size_t maxBytes = mImage->mSourceData.Length() -
mImage->mDecoder->BytesDecoded();
DecodePool::Singleton()->DecodeSomeOfImage(mImage, DecodeUntil::DONE_BYTES,
maxBytes);
size_t bytesDecoded = mImage->mDecoder->BytesDecoded() - oldByteCount;
mImage->mDecodeStatus = DecodeStatus::WORK_DONE;
if (mImage->mDecoder &&
!mImage->mError &&
!mImage->mPendingError &&
!mImage->IsDecodeFinished() &&
bytesDecoded < maxBytes &&
bytesDecoded > 0) {
// We aren't finished decoding, and we have more data, so add this request
// to the back of the list.
DecodePool::Singleton()->RequestDecode(mImage);
} else {
// Nothing more for us to do - let everyone know what happened.
NotifyProgressWorker::Dispatch(mImage);
}
DecodePool::Singleton()->Decode(mDecoder);
return NS_OK;
}
protected:
virtual ~DecodeWorker()
{
// Dispatch mImage to main thread to prevent mImage from being destructed by decode thread.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
if (mainThread) {
// Handle ambiguous nsISupports inheritance
RasterImage* rawImg = nullptr;
mImage.swap(rawImg);
DebugOnly<nsresult> rv = NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
}
}
private:
nsRefPtr<RasterImage> mImage;
nsRefPtr<Decoder> mDecoder;
};
#ifdef MOZ_NUWA_PROCESS
@ -194,13 +179,6 @@ DecodePool::Singleton()
return sSingleton;
}
already_AddRefed<nsIEventTarget>
DecodePool::GetEventTarget()
{
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
return target.forget();
}
DecodePool::DecodePool()
: mThreadPoolMutex("Thread Pool")
{
@ -258,20 +236,11 @@ DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*)
}
void
DecodePool::RequestDecode(RasterImage* aImage)
DecodePool::AsyncDecode(Decoder* aDecoder)
{
MOZ_ASSERT(aImage->mDecoder);
aImage->mDecodingMonitor.AssertCurrentThreadIn();
MOZ_ASSERT(aDecoder);
if (aImage->mDecodeStatus == DecodeStatus::PENDING ||
aImage->mDecodeStatus == DecodeStatus::ACTIVE) {
// The image is already in our list of images to decode, or currently being
// decoded, so we don't have to do anything else.
return;
}
aImage->mDecodeStatus = DecodeStatus::PENDING;
nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aImage);
nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aDecoder);
// Dispatch to the thread pool if it exists. If it doesn't, we're currently
// shutting down, so it's OK to just drop the job on the floor.
@ -282,136 +251,90 @@ DecodePool::RequestDecode(RasterImage* aImage)
}
void
DecodePool::DecodeABitOf(RasterImage* aImage)
DecodePool::SyncDecodeIfSmall(Decoder* aDecoder)
{
MOZ_ASSERT(NS_IsMainThread());
aImage->mDecodingMonitor.AssertCurrentThreadIn();
MOZ_ASSERT(aDecoder);
// If the image is waiting for decode work to be notified, go ahead and do that.
if (aImage->mDecodeStatus == DecodeStatus::WORK_DONE) {
aImage->FinishedSomeDecoding();
if (aDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime())) {
Decode(aDecoder);
return;
}
DecodeSomeOfImage(aImage);
aImage->FinishedSomeDecoding();
// If we aren't yet finished decoding and we have more data in hand, add
// this request to the back of the priority list.
if (aImage->mDecoder &&
!aImage->mError &&
!aImage->IsDecodeFinished() &&
aImage->mSourceData.Length() > aImage->mDecoder->BytesDecoded()) {
RequestDecode(aImage);
}
AsyncDecode(aDecoder);
}
/* static */ void
DecodePool::StopDecoding(RasterImage* aImage)
{
aImage->mDecodingMonitor.AssertCurrentThreadIn();
// If we haven't got a decode request, we're not currently decoding. (Having
// a decode request doesn't imply we *are* decoding, though.)
aImage->mDecodeStatus = DecodeStatus::STOPPED;
}
nsresult
DecodePool::DecodeUntilSizeAvailable(RasterImage* aImage)
void
DecodePool::SyncDecodeIfPossible(Decoder* aDecoder)
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter lock(aImage->mDecodingMonitor);
Decode(aDecoder);
}
// If the image is waiting for decode work to be notified, go ahead and do that.
if (aImage->mDecodeStatus == DecodeStatus::WORK_DONE) {
nsresult rv = aImage->FinishedSomeDecoding();
if (NS_FAILED(rv)) {
aImage->DoError();
return rv;
already_AddRefed<nsIEventTarget>
DecodePool::GetEventTarget()
{
MutexAutoLock threadPoolLock(mThreadPoolMutex);
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
return target.forget();
}
already_AddRefed<nsIRunnable>
DecodePool::CreateDecodeWorker(Decoder* aDecoder)
{
MOZ_ASSERT(aDecoder);
nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aDecoder);
return worker.forget();
}
void
DecodePool::Decode(Decoder* aDecoder)
{
MOZ_ASSERT(aDecoder);
nsresult rv = aDecoder->Decode();
if (NS_SUCCEEDED(rv) && !aDecoder->GetDecodeDone()) {
if (aDecoder->HasProgress()) {
NotifyProgress(aDecoder);
}
}
nsresult rv = DecodeSomeOfImage(aImage, DecodeUntil::SIZE);
if (NS_FAILED(rv)) {
return rv;
}
return aImage->FinishedSomeDecoding();
}
nsresult
DecodePool::DecodeSomeOfImage(RasterImage* aImage,
DecodeUntil aDecodeUntil /* = DecodeUntil::TIME */,
uint32_t bytesToDecode /* = 0 */)
{
MOZ_ASSERT(aImage->mInitialized, "Worker active for uninitialized container");
aImage->mDecodingMonitor.AssertCurrentThreadIn();
// If an error is flagged, it probably happened while we were waiting
// in the event queue.
if (aImage->mError) {
return NS_OK;
}
// If there is an error worker pending (say because the main thread has enqueued
// another decode request for us before processing the error worker) then bail out.
if (aImage->mPendingError) {
return NS_OK;
}
// If mDecoded or we don't have a decoder, we must have finished already (for
// example, a synchronous decode request came while the worker was pending).
if (!aImage->mDecoder || aImage->mDecoded) {
return NS_OK;
}
nsRefPtr<Decoder> decoderKungFuDeathGrip = aImage->mDecoder;
uint32_t maxBytes;
if (aImage->mDecoder->IsSizeDecode()) {
// Decode all available data if we're a size decode; they're cheap, and we
// want them to be more or less synchronous.
maxBytes = aImage->mSourceData.Length();
// The decoder will ensure that a new worker gets enqueued to continue
// decoding when more data is available.
} else {
// We're only guaranteed to decode this many bytes, so in particular,
// gfxPrefs::ImageMemDecodeBytesAtATime should be set high enough for us
// to read the size from most images.
maxBytes = gfxPrefs::ImageMemDecodeBytesAtATime();
NotifyDecodeComplete(aDecoder);
}
}
void
DecodePool::NotifyProgress(Decoder* aDecoder)
{
MOZ_ASSERT(aDecoder);
if (!NS_IsMainThread()) {
NotifyProgressWorker::Dispatch(aDecoder->GetImage(),
aDecoder->TakeProgress(),
aDecoder->TakeInvalidRect(),
aDecoder->GetDecodeFlags());
return;
}
if (bytesToDecode == 0) {
bytesToDecode = aImage->mSourceData.Length() - aImage->mDecoder->BytesDecoded();
aDecoder->GetImage()->NotifyProgress(aDecoder->TakeProgress(),
aDecoder->TakeInvalidRect(),
aDecoder->GetDecodeFlags());
}
void
DecodePool::NotifyDecodeComplete(Decoder* aDecoder)
{
MOZ_ASSERT(aDecoder);
if (!NS_IsMainThread()) {
NotifyDecodeCompleteWorker::Dispatch(aDecoder);
return;
}
TimeStamp deadline = TimeStamp::Now() +
TimeDuration::FromMilliseconds(gfxPrefs::ImageMemMaxMSBeforeYield());
// We keep decoding chunks until:
// * we don't have any data left to decode,
// * the decode completes,
// * we're an DecodeUntil::SIZE decode and we get the size, or
// * we run out of time.
while (aImage->mSourceData.Length() > aImage->mDecoder->BytesDecoded() &&
bytesToDecode > 0 &&
!aImage->IsDecodeFinished() &&
!(aDecodeUntil == DecodeUntil::SIZE && aImage->mHasSize)) {
uint32_t chunkSize = min(bytesToDecode, maxBytes);
nsresult rv = aImage->DecodeSomeData(chunkSize);
if (NS_FAILED(rv)) {
aImage->DoError();
return rv;
}
bytesToDecode -= chunkSize;
// Yield if we've been decoding for too long. We check this _after_ decoding
// a chunk to ensure that we don't yield without doing any decoding.
if (aDecodeUntil == DecodeUntil::TIME && TimeStamp::Now() >= deadline) {
break;
}
}
return NS_OK;
aDecoder->Finish();
aDecoder->GetImage()->FinalizeDecoder(aDecoder);
}
} // namespace image

View File

@ -25,39 +25,15 @@ namespace image {
class Decoder;
class RasterImage;
MOZ_BEGIN_ENUM_CLASS(DecodeStatus, uint8_t)
INACTIVE,
PENDING,
ACTIVE,
WORK_DONE,
STOPPED
MOZ_END_ENUM_CLASS(DecodeStatus)
MOZ_BEGIN_ENUM_CLASS(DecodeUntil, uint8_t)
TIME,
SIZE,
DONE_BYTES
MOZ_END_ENUM_CLASS(DecodeUntil)
MOZ_BEGIN_ENUM_CLASS(ShutdownReason, uint8_t)
DONE,
NOT_NEEDED,
FATAL_ERROR
MOZ_END_ENUM_CLASS(ShutdownReason)
/**
* DecodePool is a singleton class we use when decoding large images.
* DecodePool is a singleton class that manages decoding of raster images. It
* owns a pool of image decoding threads that are used for asynchronous
* decoding.
*
* When we wish to decode an image larger than
* image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode()
* for the image. This adds the image to a queue of pending requests and posts
* the DecodePool singleton to the event queue, if it's not already pending
* there.
*
* When the DecodePool is run from the event queue, it decodes the image (and
* all others it's managing) in chunks, periodically yielding control back to
* the event loop.
* DecodePool allows callers to run a decoder, handling management of the
* decoder's lifecycle and whether it executes on the main thread,
* off-main-thread in the image decoding thread pool, or on some combination of
* the two.
*/
class DecodePool : public nsIObserver
{
@ -67,58 +43,52 @@ public:
static DecodePool* Singleton();
/**
* Ask the DecodePool to asynchronously decode this image.
*/
void RequestDecode(RasterImage* aImage);
/// Ask the DecodePool to run @aDecoder asynchronously and return immediately.
void AsyncDecode(Decoder* aDecoder);
/**
* Decode aImage for a short amount of time, and post the remainder to the
* queue.
* Run @aDecoder synchronously if the image it's decoding is small. If the
* image is too large, or if the source data isn't complete yet, run @aDecoder
* asynchronously instead.
*/
void DecodeABitOf(RasterImage* aImage);
void SyncDecodeIfSmall(Decoder* aDecoder);
/**
* Ask the DecodePool to stop decoding this image. Internally, we also
* call this function when we finish decoding an image.
* Run aDecoder synchronously if at all possible. If it can't complete
* synchronously because the source data isn't complete, asynchronously decode
* the rest.
*/
void SyncDecodeIfPossible(Decoder* aDecoder);
/**
* Returns an event target interface to the DecodePool's underlying thread
* pool. Callers can use this event target to submit work to the image
* decoding thread pool.
*
* Since the DecodePool keeps raw pointers to RasterImages, make sure you
* call this before a RasterImage is destroyed!
*/
static void StopDecoding(RasterImage* aImage);
/**
* Synchronously decode the beginning of the image until we run out of
* bytes or we get the image's size. Note that this done on a best-effort
* basis; if the size is burried too deep in the image, we'll give up.
*
* @return NS_ERROR if an error is encountered, and NS_OK otherwise. (Note
* that we return NS_OK even when the size was not found.)
*/
nsresult DecodeUntilSizeAvailable(RasterImage* aImage);
/**
* Returns an event target interface to the thread pool; primarily for
* OnDataAvailable delivery off main thread.
*
* @return An nsIEventTarget interface to mThreadPool.
* @return An nsIEventTarget interface to the thread pool.
*/
already_AddRefed<nsIEventTarget> GetEventTarget();
/**
* Decode some chunks of the given image. If aDecodeUntil is SIZE,
* decode until we have the image's size, then stop. If bytesToDecode is
* non-0, at most bytesToDecode bytes will be decoded. if aDecodeUntil is
* DONE_BYTES, decode until all bytesToDecode bytes are decoded.
* Creates a worker which can be used to attempt further decoding using the
* provided decoder.
*
* @return The new worker, which should be posted to the event target returned
* by GetEventTarget.
*/
nsresult DecodeSomeOfImage(RasterImage* aImage,
DecodeUntil aDecodeUntil = DecodeUntil::TIME,
uint32_t bytesToDecode = 0);
already_AddRefed<nsIRunnable> CreateDecodeWorker(Decoder* aDecoder);
private:
friend class DecodeWorker;
friend class NotifyDecodeCompleteWorker;
DecodePool();
virtual ~DecodePool();
void Decode(Decoder* aDecoder);
void NotifyDecodeComplete(Decoder* aDecoder);
void NotifyProgress(Decoder* aDecoder);
static StaticRefPtr<DecodePool> sSingleton;
// mThreadPoolMutex protects mThreadPool. For all RasterImages R,

View File

@ -7,10 +7,12 @@
#include "Decoder.h"
#include "mozilla/gfx/2D.h"
#include "DecodePool.h"
#include "GeckoProfiler.h"
#include "imgIContainer.h"
#include "nsIConsoleService.h"
#include "nsIScriptError.h"
#include "GeckoProfiler.h"
#include "nsProxyRelease.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
@ -20,7 +22,7 @@ using mozilla::gfx::SurfaceFormat;
namespace mozilla {
namespace image {
Decoder::Decoder(RasterImage &aImage)
Decoder::Decoder(RasterImage* aImage)
: mImage(aImage)
, mProgress(NoProgress)
, mImageData(nullptr)
@ -29,8 +31,11 @@ Decoder::Decoder(RasterImage &aImage)
, mDecodeFlags(0)
, mBytesDecoded(0)
, mSendPartialInvalidations(false)
, mDataDone(false)
, mDecodeDone(false)
, mDataError(false)
, mDecodeAborted(false)
, mImageIsTransient(false)
, mFrameCount(0)
, mFailCode(NS_OK)
, mNeedsNewFrame(false)
@ -48,6 +53,21 @@ Decoder::~Decoder()
MOZ_ASSERT(mInvalidRect.IsEmpty(),
"Destroying Decoder without taking all its invalidations");
mInitialized = false;
if (!NS_IsMainThread()) {
// Dispatch mImage to main thread to prevent it from being destructed by the
// decode thread.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
if (mainThread) {
// Handle ambiguous nsISupports inheritance.
RasterImage* rawImg = nullptr;
mImage.swap(rawImg);
DebugOnly<nsresult> rv =
NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
}
}
}
/*
@ -58,7 +78,7 @@ void
Decoder::Init()
{
// No re-initializing
NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
// Fire OnStartDecode at init time to support bug 512435.
if (!IsSizeDecode()) {
@ -98,6 +118,69 @@ Decoder::InitSharedDecoder(uint8_t* aImageData, uint32_t aImageDataLength,
mInitialized = true;
}
nsresult
Decoder::Decode()
{
MOZ_ASSERT(mInitialized, "Should be initialized here");
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
// We keep decoding chunks until the decode completes or there are no more
// chunks available.
while (!GetDecodeDone() && !HasError()) {
auto newState = mIterator->AdvanceOrScheduleResume(this);
if (newState == SourceBufferIterator::WAITING) {
// We can't continue because the rest of the data hasn't arrived from the
// network yet. We don't have to do anything special; the
// SourceBufferIterator will ensure that Decode() gets called again on a
// DecodePool thread when more data is available.
return NS_OK;
}
if (newState == SourceBufferIterator::COMPLETE) {
mDataDone = true;
nsresult finalStatus = mIterator->CompletionStatus();
if (NS_FAILED(finalStatus)) {
PostDataError();
}
return finalStatus;
}
MOZ_ASSERT(newState == SourceBufferIterator::READY);
Write(mIterator->Data(), mIterator->Length());
}
return HasError() ? NS_ERROR_FAILURE : NS_OK;
}
void
Decoder::Resume()
{
DecodePool* decodePool = DecodePool::Singleton();
MOZ_ASSERT(decodePool);
nsCOMPtr<nsIEventTarget> target = decodePool->GetEventTarget();
if (MOZ_UNLIKELY(!target)) {
// We're shutting down and the DecodePool's thread pool has been destroyed.
return;
}
nsCOMPtr<nsIRunnable> worker = decodePool->CreateDecodeWorker(this);
target->Dispatch(worker, nsIEventTarget::DISPATCH_NORMAL);
}
bool
Decoder::ShouldSyncDecode(size_t aByteLimit)
{
MOZ_ASSERT(aByteLimit > 0);
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
return mIterator->RemainingBytesIsNoMoreThan(aByteLimit);
}
void
Decoder::Write(const char* aBuffer, uint32_t aCount)
{
@ -157,7 +240,7 @@ Decoder::Write(const char* aBuffer, uint32_t aCount)
}
void
Decoder::Finish(ShutdownReason aReason)
Decoder::Finish()
{
MOZ_ASSERT(NS_IsMainThread());
@ -169,9 +252,10 @@ Decoder::Finish(ShutdownReason aReason)
if (mInFrame && !HasError())
PostFrameStop();
// If PostDecodeDone() has not been called, we need to sent teardown
// notifications.
if (!IsSizeDecode() && !mDecodeDone) {
// If PostDecodeDone() has not been called, and this decoder wasn't aborted
// early because of low-memory conditions or losing a race with another
// decoder, we need to send teardown notifications.
if (!IsSizeDecode() && !mDecodeDone && !WasAborted()) {
// Log data errors to the error console
nsCOMPtr<nsIConsoleService> consoleService =
@ -181,34 +265,29 @@ Decoder::Finish(ShutdownReason aReason)
if (consoleService && errorObject && !HasDecoderError()) {
nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated: ") +
NS_ConvertUTF8toUTF16(mImage.GetURIString()));
NS_ConvertUTF8toUTF16(mImage->GetURIString()));
if (NS_SUCCEEDED(errorObject->InitWithWindowID(
msg,
NS_ConvertUTF8toUTF16(mImage.GetURIString()),
NS_ConvertUTF8toUTF16(mImage->GetURIString()),
EmptyString(), 0, 0, nsIScriptError::errorFlag,
"Image", mImage.InnerWindowID()
"Image", mImage->InnerWindowID()
))) {
consoleService->LogMessage(errorObject);
}
}
bool usable = !HasDecoderError();
if (aReason != ShutdownReason::NOT_NEEDED && !HasDecoderError()) {
// If we only have a data error, we're usable if we have at least one complete frame.
if (GetCompleteFrameCount() == 0) {
usable = false;
}
}
// If we're usable, do exactly what we should have when the decoder
// completed.
if (usable) {
// If we only have a data error, we're usable if we have at least one
// complete frame.
if (!HasDecoderError() && GetCompleteFrameCount() > 0) {
// We're usable, so do exactly what we should have when the decoder
// completed.
if (mInFrame) {
PostFrameStop();
}
PostDecodeDone();
} else {
// We're not usable. Record some final progress indicating the error.
if (!IsSizeDecode()) {
mProgress |= FLAG_DECODE_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
}
@ -218,11 +297,23 @@ Decoder::Finish(ShutdownReason aReason)
// Set image metadata before calling DecodingComplete, because
// DecodingComplete calls Optimize().
mImageMetadata.SetOnImage(&mImage);
mImageMetadata.SetOnImage(mImage);
if (mDecodeDone) {
if (HasSize()) {
SetSizeOnImage();
}
if (mDecodeDone && !IsSizeDecode()) {
MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
mImage.DecodingComplete(mCurrentFrame.get());
// If this image wasn't animated and isn't a transient image, mark its frame
// as optimizable. We don't support optimizing animated images and
// optimizing transient images isn't worth it.
if (!mIsAnimated && !mImageIsTransient && mCurrentFrame) {
mCurrentFrame->SetOptimizable();
}
mImage->OnDecodingComplete();
}
}
@ -313,18 +404,25 @@ Decoder::EnsureFrame(uint32_t aFrameNum,
aPreviousFrame->GetPaletteDepth() != aPaletteDepth,
"Replacing first frame with the same kind of frame?");
// Remove the old frame from the SurfaceCache and release our reference to it.
IntSize prevFrameSize = aPreviousFrame->GetImageSize();
SurfaceCache::RemoveSurface(ImageKey(&mImage),
RasterSurfaceKey(prevFrameSize, aDecodeFlags, 0));
mFrameCount = 0;
// Reset our state.
mInFrame = false;
mCurrentFrame->Abort();
mCurrentFrame = RawAccessFrameRef();
RawAccessFrameRef ref = Move(mCurrentFrame);
// Add the new frame as usual.
return InternalAddFrame(aFrameNum, aFrameRect, aDecodeFlags, aFormat,
aPaletteDepth, nullptr);
MOZ_ASSERT(ref, "No ref to current frame?");
// Reinitialize the old frame.
nsIntSize oldSize = ThebesIntSize(aPreviousFrame->GetImageSize());
bool nonPremult =
aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
if (NS_FAILED(aPreviousFrame->ReinitForDecoder(oldSize, aFrameRect, aFormat,
aPaletteDepth, nonPremult))) {
NS_WARNING("imgFrame::ReinitForDecoder should succeed");
mFrameCount = 0;
aPreviousFrame->Abort();
return RawAccessFrameRef();
}
return ref;
}
RawAccessFrameRef
@ -369,12 +467,18 @@ Decoder::InternalAddFrame(uint32_t aFrameNum,
}
InsertOutcome outcome =
SurfaceCache::Insert(frame, ImageKey(&mImage),
SurfaceCache::Insert(frame, ImageKey(mImage.get()),
RasterSurfaceKey(imageSize.ToIntSize(),
aDecodeFlags,
aFrameNum),
Lifetime::Persistent);
if (outcome != InsertOutcome::SUCCESS) {
// We either hit InsertOutcome::FAILURE, which is a temporary failure due to
// low memory (we know it's not permanent because we checked CanHold()
// above), or InsertOutcome::FAILURE_ALREADY_PRESENT, which means that
// another decoder beat us to decoding this frame. Either way, we should
// abort this decoder rather than treat this as a real error.
mDecodeAborted = true;
ref->Abort();
return RawAccessFrameRef();
}
@ -405,7 +509,7 @@ Decoder::InternalAddFrame(uint32_t aFrameNum,
}
mFrameCount++;
mImage.OnAddedFrame(mFrameCount, refreshArea);
mImage->OnAddedFrame(mFrameCount, refreshArea);
return ref;
}
@ -416,9 +520,12 @@ Decoder::SetSizeOnImage()
MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size");
MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation");
mImage.SetSize(mImageMetadata.GetWidth(),
mImageMetadata.GetHeight(),
mImageMetadata.GetOrientation());
nsresult rv = mImage->SetSize(mImageMetadata.GetWidth(),
mImageMetadata.GetHeight(),
mImageMetadata.GetOrientation());
if (NS_FAILED(rv)) {
PostResizeError();
}
}
/*

View File

@ -12,22 +12,21 @@
#include "DecodePool.h"
#include "ImageMetadata.h"
#include "Orientation.h"
#include "SourceBuffer.h"
#include "mozilla/Telemetry.h"
namespace mozilla {
namespace image {
class Decoder
class Decoder : public IResumable
{
public:
explicit Decoder(RasterImage& aImage);
explicit Decoder(RasterImage* aImage);
/**
* Initialize an image decoder. Decoders may not be re-initialized.
*
* Notifications Sent: TODO
*/
void Init();
@ -42,28 +41,30 @@ public:
RawAccessFrameRef&& aFrameRef);
/**
* Writes data to the decoder.
*
* Decodes, reading all data currently available in the SourceBuffer. If more
* If aBuffer is null and aCount is 0, Write() flushes any buffered data to
* the decoder. Data is buffered if the decoder wasn't able to completely
* decode it because it needed a new frame. If it's necessary to flush data,
* NeedsToFlushData() will return true.
*
* @param aBuffer buffer containing the data to be written
* @param aCount the number of bytes to write
* data is needed, Decode() automatically ensures that it will be called again
* on a DecodePool thread when the data becomes available.
*
* Any errors are reported by setting the appropriate state on the decoder.
*
* Notifications Sent: TODO
*/
void Write(const char* aBuffer, uint32_t aCount);
nsresult Decode();
/**
* Informs the decoder that all the data has been written.
*
* Notifications Sent: TODO
* Cleans up the decoder's state and notifies our image about success or
* failure. May only be called on the main thread.
*/
void Finish(ShutdownReason aReason);
void Finish();
/**
* Given a maximum number of bytes we're willing to decode, @aByteLimit,
* returns true if we should attempt to run this decoder synchronously.
*/
bool ShouldSyncDecode(size_t aByteLimit);
/**
* Informs the shared decoder that all the data has been written.
@ -98,9 +99,20 @@ public:
return progress;
}
/**
* Returns true if there's any progress to report.
*/
bool HasProgress() const
{
return mProgress != NoProgress || !mInvalidRect.IsEmpty();
}
// We're not COM-y, so we don't get refcounts by default
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
// Implement IResumable.
virtual void Resume() MOZ_OVERRIDE;
/*
* State.
*/
@ -111,7 +123,7 @@ public:
bool IsSizeDecode() { return mSizeDecode; }
void SetSizeDecode(bool aSizeDecode)
{
NS_ABORT_IF_FALSE(!mInitialized, "Can't set size decode after Init()!");
MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
mSizeDecode = aSizeDecode;
}
@ -133,6 +145,32 @@ public:
mSendPartialInvalidations = aSend;
}
/**
* Set an iterator to the SourceBuffer which will feed data to this decoder.
*
* This should be called for almost all decoders; the exceptions are the
* contained decoders of an nsICODecoder, which will be fed manually via Write
* instead.
*
* This must be called before Init() is called.
*/
void SetIterator(SourceBufferIterator&& aIterator)
{
MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
mIterator.emplace(Move(aIterator));
}
/**
* Set whether this decoder is associated with a transient image. The decoder
* may choose to avoid certain optimizations that don't pay off for
* short-lived images in this case.
*/
void SetImageIsTransient(bool aIsTransient)
{
MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
mImageIsTransient = aIsTransient;
}
size_t BytesDecoded() const { return mBytesDecoded; }
// The amount of time we've spent inside Write() so far for this decoder.
@ -149,15 +187,28 @@ public:
uint32_t GetCompleteFrameCount() { return mInFrame ? mFrameCount - 1 : mFrameCount; }
// Error tracking
bool HasError() { return HasDataError() || HasDecoderError(); }
bool HasDataError() { return mDataError; }
bool HasDecoderError() { return NS_FAILED(mFailCode); }
nsresult GetDecoderError() { return mFailCode; }
bool HasError() const { return HasDataError() || HasDecoderError(); }
bool HasDataError() const { return mDataError; }
bool HasDecoderError() const { return NS_FAILED(mFailCode); }
nsresult GetDecoderError() const { return mFailCode; }
void PostResizeError() { PostDataError(); }
bool GetDecodeDone() const {
return mDecodeDone;
bool GetDecodeDone() const
{
return mDecodeDone || (mSizeDecode && HasSize()) || HasError() || mDataDone;
}
/**
* Returns true if this decoder was aborted.
*
* This may happen due to a low-memory condition, or because another decoder
* was racing with this one to decode the same frames with the same flags and
* this decoder lost the race. Either way, this is not a permanent situation
* and does not constitute an error, so we don't report any errors when this
* happens.
*/
bool WasAborted() const { return mDecodeAborted; }
// flags. Keep these in sync with imgIContainer.idl.
// SetDecodeFlags must be called before Init(), otherwise
// default flags are assumed.
@ -187,6 +238,11 @@ public:
ImageMetadata& GetImageMetadata() { return mImageMetadata; }
/**
* Returns a weak pointer to the image associated with this decoder.
*/
RasterImage* GetImage() const { MOZ_ASSERT(mImage); return mImage.get(); }
// Tell the decoder infrastructure to allocate a frame. By default, frame 0
// is created as an ARGB frame with no offset and with size width * height.
// If decoders need something different, they must ask for it.
@ -217,6 +273,18 @@ public:
: RawAccessFrameRef();
}
/**
* Writes data to the decoder. Only public for the benefit of nsICODecoder;
* other callers should use Decode().
*
* @param aBuffer buffer containing the data to be written
* @param aCount the number of bytes to write
*
* Any errors are reported by setting the appropriate state on the decoder.
*/
void Write(const char* aBuffer, uint32_t aCount);
protected:
virtual ~Decoder();
@ -308,11 +376,13 @@ protected:
gfx::SurfaceFormat aFormat,
uint8_t aPaletteDepth,
imgFrame* aPreviousFrame);
/*
* Member variables.
*
*/
RasterImage &mImage;
nsRefPtr<RasterImage> mImage;
Maybe<SourceBufferIterator> mIterator;
RawAccessFrameRef mCurrentFrame;
ImageMetadata mImageMetadata;
nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
@ -330,8 +400,11 @@ protected:
uint32_t mDecodeFlags;
size_t mBytesDecoded;
bool mSendPartialInvalidations;
bool mDataDone;
bool mDecodeDone;
bool mDataError;
bool mDecodeAborted;
bool mImageIsTransient;
private:
uint32_t mFrameCount; // Number of frames, including anything in-progress

View File

@ -290,6 +290,11 @@ int32_t
FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
{
RawAccessFrameRef frame = GetRawFrame(aFrameNum);
if (!frame) {
NS_WARNING("No frame; called GetTimeoutForFrame too early?");
return 100;
}
AnimationData data = frame->GetAnimationData();
// Ensure a minimal time between updates so we don't throttle the UI thread.

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,6 @@
#include "nsIObserver.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/TypedEnum.h"
#include "mozilla/WeakPtr.h"
@ -132,6 +131,13 @@ namespace image {
class Decoder;
class FrameAnimator;
class SourceBuffer;
MOZ_BEGIN_ENUM_CLASS(DecodeStrategy, uint8_t)
ASYNC,
SYNC_FOR_SMALL_IMAGES,
SYNC_IF_POSSIBLE
MOZ_END_ENUM_CLASS(DecodeStrategy)
class RasterImage MOZ_FINAL : public ImageResource
, public nsIProperties
@ -162,10 +168,10 @@ public:
virtual void OnSurfaceDiscarded() MOZ_OVERRIDE;
// Raster-specific methods
static NS_METHOD WriteToRasterImage(nsIInputStream* aIn, void* aClosure,
const char* aFromRawSegment,
uint32_t aToOffset, uint32_t aCount,
uint32_t* aWriteCount);
static NS_METHOD WriteToSourceBuffer(nsIInputStream* aIn, void* aClosure,
const char* aFromRawSegment,
uint32_t aToOffset, uint32_t aCount,
uint32_t* aWriteCount);
/* The total number of frames in this image. */
uint32_t GetNumFrames() const { return mFrameCount; }
@ -196,24 +202,36 @@ public:
*/
void SetLoopCount(int32_t aLoopCount);
/* notification that the entire image has been decoded */
void DecodingComplete(imgFrame* aFinalFrame);
/// Notification that the entire image has been decoded.
void OnDecodingComplete();
/**
* Sends the provided progress notifications to ProgressTracker.
*
* Main-thread only.
*
* @param aProgress The progress notifications to send.
* @param aInvalidRect An invalidation rect to send.
* @param aFlags The decode flags used by the decoder that generated
* these notifications, or DECODE_FLAGS_DEFAULT if the
* notifications don't come from a decoder.
*/
void NotifyProgress(Progress aProgress,
const nsIntRect& aInvalidRect = nsIntRect(),
uint32_t aFlags = 0);
/**
* Records telemetry and does final teardown of the provided decoder.
*
* Main-thread only.
*/
void FinalizeDecoder(Decoder* aDecoder);
//////////////////////////////////////////////////////////////////////////////
// Network callbacks.
//////////////////////////////////////////////////////////////////////////////
/* Add compressed source data to the imgContainer.
*
* The decoder will use this data, either immediately or at draw time, to
* decode the image.
*
* XXX This method's only caller (WriteToContainer) ignores the return
* value. Should this just return void?
*/
nsresult AddSourceData(const char *aBuffer, uint32_t aCount);
virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream* aInStr,
@ -237,7 +255,7 @@ public:
* Thus, pre-allocation simplifies code and reduces the total number of
* allocations.
*/
nsresult SetSourceSizeHint(uint32_t sizeHint);
nsresult SetSourceSizeHint(uint32_t aSizeHint);
/* Provide a hint for the requested resolution of the resulting image. */
void SetRequestedResolution(const nsIntSize requestedResolution) {
@ -267,14 +285,6 @@ public:
static void Initialize();
private:
friend class DecodePool;
friend class DecodeWorker;
friend class FrameNeededWorker;
friend class NotifyProgressWorker;
nsresult FinishedSomeDecoding(ShutdownReason aReason = ShutdownReason::DONE,
Progress aProgress = NoProgress);
void DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
gfxContext* aContext,
const nsIntSize& aSize,
@ -304,44 +314,34 @@ private:
size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf) const;
nsresult DoImageDataComplete();
already_AddRefed<layers::Image> GetCurrentImage();
void UpdateImageContainer();
enum RequestDecodeType {
ASYNCHRONOUS,
SYNCHRONOUS_NOTIFY,
SYNCHRONOUS_NOTIFY_AND_SOME_DECODE
};
NS_IMETHOD RequestDecodeCore(RequestDecodeType aDecodeType);
// We would like to just check if we have a zero lock count, but we can't do
// that for animated images because in EnsureAnimExists we lock the image and
// never unlock so that animated images always have their lock count >= 1. In
// that case we use our animation consumers count as a proxy for lock count.
bool IsUnlocked() { return (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)); }
//////////////////////////////////////////////////////////////////////////////
// Decoding.
//////////////////////////////////////////////////////////////////////////////
already_AddRefed<Decoder> CreateDecoder(bool aDoSizeDecode, uint32_t aFlags);
void WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
NS_IMETHOD Decode(DecodeStrategy aStrategy, uint32_t aFlags,
bool aDoSizeDecode = false);
private: // data
nsIntSize mSize;
Orientation mOrientation;
// Whether our frames were decoded using any special flags.
// Some flags (e.g. unpremultiplied data) may not be compatible
// with the browser's needs for displaying the image to the user.
// As such, we may need to redecode if we're being asked for
// a frame with different flags. 0 indicates default flags.
//
// Valid flag bits are imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
// and imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION.
uint32_t mFrameDecodeFlags;
nsCOMPtr<nsIProperties> mProperties;
//! All the frames of the image.
// IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
// that the frames actually exist (they may have been discarded to save memory, or
// we maybe decoding on draw).
/// If this image is animated, a FrameAnimator which manages its animation.
UniquePtr<FrameAnimator> mAnim;
// Image locking.
@ -370,37 +370,19 @@ private: // data
uint32_t mFramesNotified;
#endif
// Below are the pieces of data that can be accessed on more than one thread
// at once, and hence need to be locked by mDecodingMonitor.
// BEGIN LOCKED MEMBER VARIABLES
ReentrantMonitor mDecodingMonitor;
FallibleTArray<char> mSourceData;
// Decoder and friends
nsRefPtr<Decoder> mDecoder;
DecodeStatus mDecodeStatus;
// END LOCKED MEMBER VARIABLES
// The source data for this image.
nsRefPtr<SourceBuffer> mSourceBuffer;
// The number of frames this image has.
uint32_t mFrameCount;
// Notification state. Used to avoid recursive notifications.
Progress mNotifyProgress;
nsIntRect mNotifyInvalidRect;
bool mNotifying:1;
// Boolean flags (clustered together to conserve space):
bool mHasSize:1; // Has SetSize() been called?
bool mDecodeOnDraw:1; // Decoding on draw?
bool mTransient:1; // Is the image short-lived?
bool mDiscardable:1; // Is container discardable?
bool mHasSourceData:1; // Do we have source data?
// Do we have the frames in decoded form?
bool mDecoded:1;
bool mHasBeenDecoded:1;
bool mHasBeenDecoded:1; // Decoded at least once?
// Whether we're waiting to start animation. If we get a StartAnimation() call
// but we don't yet have more than one frame, mPendingAnimation is set so that
@ -415,27 +397,11 @@ private: // data
// off a full decode.
bool mWantFullDecode:1;
// Set when a decode worker detects an error off-main-thread. Once the error
// is handled on the main thread, mError is set, but mPendingError is used to
// stop decode work immediately.
bool mPendingError:1;
// Decoding
nsresult RequestDecodeIfNeeded(nsresult aStatus, ShutdownReason aReason,
bool aDone, bool aWasSize);
nsresult WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
nsresult SyncDecode();
nsresult InitDecoder(bool aDoSizeDecode);
nsresult WriteToDecoder(const char *aBuffer, uint32_t aCount);
nsresult DecodeSomeData(size_t aMaxBytes);
bool IsDecodeFinished();
TimeStamp mDrawStartTime;
// Initializes ProgressTracker and resets it on RasterImage destruction.
nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
nsresult ShutdownDecoder(ShutdownReason aReason);
//////////////////////////////////////////////////////////////////////////////
// Scaling.
@ -476,7 +442,6 @@ private: // data
// Helpers
bool CanDiscard();
bool StoringSourceData() const;
protected:
explicit RasterImage(ProgressTracker* aProgressTracker = nullptr,

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