mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-27 21:00:50 +00:00
Merge mozilla-central to b2g-inbound
This commit is contained in:
commit
efe3acd08b
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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++) {
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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
|
||||
|
13
configure.in
13
configure.in
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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() {}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -320,8 +320,6 @@ nsScriptNameSpaceManager::Init()
|
||||
{
|
||||
static const PLDHashTableOps hash_table_ops =
|
||||
{
|
||||
PL_DHashAllocTable,
|
||||
PL_DHashFreeTable,
|
||||
GlobalNameHashHashKey,
|
||||
GlobalNameHashMatchEntry,
|
||||
PL_DHashMoveEntryStub,
|
||||
|
@ -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
|
||||
|
@ -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': {
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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(
|
||||
"""
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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])
|
||||
|
@ -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
|
||||
|
@ -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>
|
@ -152,6 +152,8 @@ WebGLContext::InitWebGL2()
|
||||
auto xfBuffers = new WebGLRefPtr<WebGLBuffer>[mGLMaxTransformFeedbackSeparateAttribs];
|
||||
mBoundTransformFeedbackBuffers.reset(xfBuffers);
|
||||
|
||||
mBypassShaderValidation = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
*/
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -48,7 +48,7 @@ public:
|
||||
|
||||
WebGLContext* GetParentObject() const {
|
||||
return Context();
|
||||
};
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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, ¤tProgram);
|
||||
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
@ -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
|
||||
|
@ -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
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "WebGLRenderbuffer.h"
|
||||
#include "WebGLShader.h"
|
||||
#include "WebGLTexture.h"
|
||||
#include "WebGLUniformInfo.h"
|
||||
#include "WebGLVertexArray.h"
|
||||
#include "WebGLVertexAttribData.h"
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
349
dom/canvas/WebGLShaderValidator.cpp
Normal file
349
dom/canvas/WebGLShaderValidator.cpp
Normal 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
|
60
dom/canvas/WebGLShaderValidator.h
Normal file
60
dom/canvas/WebGLShaderValidator.h
Normal 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_
|
@ -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
|
@ -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)
|
||||
|
@ -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
|
||||
|
169
dom/canvas/WebGLValidateStrings.cpp
Normal file
169
dom/canvas/WebGLValidateStrings.cpp
Normal 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
|
@ -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_
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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)));
|
||||
|
@ -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);
|
||||
|
@ -268,4 +268,10 @@ MediaSourceDecoder::SetCDMProxy(CDMProxy* aProxy)
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
MediaSourceDecoder::IsActiveReader(MediaDecoderReader* aReader)
|
||||
{
|
||||
return mReader->IsActiveReader(aReader);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -760,8 +760,6 @@ XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
|
||||
}
|
||||
|
||||
static const PLDHashTableOps gOps = {
|
||||
PL_DHashAllocTable,
|
||||
PL_DHashFreeTable,
|
||||
PL_DHashVoidPtrKeyStub,
|
||||
PL_DHashMatchEntryStub,
|
||||
PL_DHashMoveEntryStub,
|
||||
|
@ -16,8 +16,6 @@ using namespace mozilla;
|
||||
|
||||
const PLDHashTableOps nsCommandParams::sHashOps =
|
||||
{
|
||||
PL_DHashAllocTable,
|
||||
PL_DHashFreeTable,
|
||||
HashKey,
|
||||
HashMatchEntry,
|
||||
HashMoveEntry,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -622,8 +622,6 @@ public:
|
||||
: mWriteNeeded(false)
|
||||
{
|
||||
mOps = (PLDHashTableOps) {
|
||||
PL_DHashAllocTable,
|
||||
PL_DHashFreeTable,
|
||||
StringHash,
|
||||
HashMatchEntry,
|
||||
MoveEntry,
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -68,7 +68,7 @@ namespace image {
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// GIF Decoder Implementation
|
||||
|
||||
nsGIFDecoder2::nsGIFDecoder2(RasterImage& aImage)
|
||||
nsGIFDecoder2::nsGIFDecoder2(RasterImage* aImage)
|
||||
: Decoder(aImage)
|
||||
, mCurrentRow(-1)
|
||||
, mLastFlushedRow(-1)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -15,7 +15,7 @@
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
nsIconDecoder::nsIconDecoder(RasterImage& aImage)
|
||||
nsIconDecoder::nsIconDecoder(RasterImage* aImage)
|
||||
: Decoder(aImage),
|
||||
mWidth(-1),
|
||||
mHeight(-1),
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
Loading…
x
Reference in New Issue
Block a user