зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c a=merge
This commit is contained in:
Коммит
be05307be5
4
CLOBBER
4
CLOBBER
|
@ -22,4 +22,6 @@
|
|||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Clobber to make sure any residual damage from bug 975011 is gone.
|
||||
Bug 800200: Removing the old JavaScript debugging API, js/jsd. I'm advised
|
||||
that our build system doesn't cope well with deletions, and that a spoonful
|
||||
of clobber helps the medicine go down (in a most delightful way).
|
||||
|
|
|
@ -139,7 +139,6 @@ loop.shared.models = (function() {
|
|||
throw new Error("Can't start session as it's not ready");
|
||||
}
|
||||
this.session = this.sdk.initSession(this.get("sessionId"));
|
||||
this.listenTo(this.session, "sessionConnected", this._sessionConnected);
|
||||
this.listenTo(this.session, "streamCreated", this._streamCreated);
|
||||
this.listenTo(this.session, "connectionDestroyed",
|
||||
this._connectionDestroyed);
|
||||
|
@ -147,7 +146,8 @@ loop.shared.models = (function() {
|
|||
this._sessionDisconnected);
|
||||
this.listenTo(this.session, "networkDisconnected",
|
||||
this._networkDisconnected);
|
||||
this.session.connect(this.get("apiKey"), this.get("sessionToken"));
|
||||
this.session.connect(this.get("apiKey"), this.get("sessionToken"),
|
||||
this._onConnectCompletion.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -160,14 +160,22 @@ loop.shared.models = (function() {
|
|||
},
|
||||
|
||||
/**
|
||||
* Session is created.
|
||||
* Manages connection status
|
||||
* triggers apropriate event for connection error/success
|
||||
* http://tokbox.com/opentok/tutorials/connect-session/js/
|
||||
* http://tokbox.com/opentok/tutorials/hello-world/js/
|
||||
* http://tokbox.com/opentok/libraries/client/js/reference/SessionConnectEvent.html
|
||||
*
|
||||
* @param {SessionConnectEvent} event
|
||||
* @param {error|null} error
|
||||
*/
|
||||
_sessionConnected: function(event) {
|
||||
this.trigger("session:connected", event);
|
||||
_onConnectCompletion: function(error) {
|
||||
if (error) {
|
||||
this.trigger("session:connection-error", error);
|
||||
this.endSession();
|
||||
} else {
|
||||
this.trigger("session:connected");
|
||||
this.set("ongoing", true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -103,10 +103,22 @@ loop.shared.router = (function(l10n) {
|
|||
this._onPeerHungup);
|
||||
this.listenTo(this._conversation, "session:network-disconnected",
|
||||
this._onNetworkDisconnected);
|
||||
this.listenTo(this._conversation, "session:connection-error",
|
||||
this._notifyError);
|
||||
|
||||
BaseRouter.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify the user that the connection was not possible
|
||||
* @param {{code: number, message: string}} error
|
||||
*/
|
||||
_notifyError: function(error) {
|
||||
console.log(error);
|
||||
this._notifier.errorL10n("connection_error_see_console_notification");
|
||||
this.endCall();
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts the call. This method should be overriden.
|
||||
*/
|
||||
|
|
|
@ -13,6 +13,7 @@ use_latest_firefox.innerHTML=To use Loop, please use the latest version of <a hr
|
|||
incompatible_device=Incompatible device
|
||||
sorry_device_unsupported=Sorry, Loop does not currently support your device.
|
||||
use_firefox_windows_mac_linux=Please open this page using the latest Firefox on Windows, Android, Mac or Linux.
|
||||
connection_error_see_console_notification=Call failed; see console for details.
|
||||
|
||||
[fr]
|
||||
call_has_ended=L'appel est terminé.
|
||||
|
|
|
@ -26,7 +26,9 @@ describe("loop.shared.models", function() {
|
|||
apiKey: "apiKey"
|
||||
};
|
||||
fakeSession = _.extend({
|
||||
connect: sandbox.spy(),
|
||||
connect: function () {},
|
||||
endSession: sandbox.stub(),
|
||||
set: sandbox.stub(),
|
||||
disconnect: sandbox.spy(),
|
||||
unpublish: sandbox.spy()
|
||||
}, Backbone.Events);
|
||||
|
@ -163,12 +165,72 @@ describe("loop.shared.models", function() {
|
|||
sinon.assert.calledOnce(fakeSDK.initSession);
|
||||
});
|
||||
|
||||
describe("Session events", function() {
|
||||
it("should trigger a session:connected event on sessionConnected",
|
||||
function(done) {
|
||||
model.once("session:connected", function(){ done(); });
|
||||
it("should call connect", function() {
|
||||
fakeSession.connect = sandbox.stub();
|
||||
|
||||
fakeSession.trigger("sessionConnected");
|
||||
model.startSession();
|
||||
|
||||
sinon.assert.calledOnce(fakeSession.connect);
|
||||
sinon.assert.calledWithExactly(fakeSession.connect,
|
||||
sinon.match.string, sinon.match.string,
|
||||
sinon.match.func);
|
||||
});
|
||||
|
||||
it("should set ongoing to true when no error is called back",
|
||||
function() {
|
||||
fakeSession.connect = function(key, token, cb) {
|
||||
cb(null);
|
||||
};
|
||||
sinon.stub(model, "set");
|
||||
|
||||
model.startSession();
|
||||
|
||||
sinon.assert.calledWith(model.set, "ongoing", true);
|
||||
});
|
||||
|
||||
it("should trigger session:connected when no error is called back",
|
||||
function() {
|
||||
fakeSession.connect = function(key, token, cb) {
|
||||
cb(null);
|
||||
};
|
||||
sandbox.stub(model, "trigger");
|
||||
|
||||
model.startSession();
|
||||
|
||||
sinon.assert.calledWithExactly(model.trigger, "session:connected");
|
||||
});
|
||||
|
||||
describe("Session events", function() {
|
||||
|
||||
it("should trigger a fail event when an error is called back",
|
||||
function() {
|
||||
fakeSession.connect = function(key, token, cb) {
|
||||
cb({
|
||||
error: true
|
||||
});
|
||||
};
|
||||
sinon.stub(model, "endSession");
|
||||
|
||||
model.startSession();
|
||||
|
||||
sinon.assert.calledOnce(model.endSession);
|
||||
sinon.assert.calledWithExactly(model.endSession);
|
||||
});
|
||||
|
||||
it("should trigger session:connection-error event when an error is" +
|
||||
" called back", function() {
|
||||
fakeSession.connect = function(key, token, cb) {
|
||||
cb({
|
||||
error: true
|
||||
});
|
||||
};
|
||||
sandbox.stub(model, "trigger");
|
||||
|
||||
model.startSession();
|
||||
|
||||
sinon.assert.calledOnce(model.trigger);
|
||||
sinon.assert.calledWithExactly(model.trigger,
|
||||
"session:connection-error", sinon.match.object);
|
||||
});
|
||||
|
||||
it("should trigger a session:ended event on sessionDisconnected",
|
||||
|
|
|
@ -125,6 +125,24 @@ describe("loop.shared.router", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("session:connection-error", function() {
|
||||
|
||||
it("should warn the user when .connect() call fails", function() {
|
||||
conversation.trigger("session:connection-error");
|
||||
|
||||
sinon.assert.calledOnce(notifier.errorL10n);
|
||||
sinon.assert.calledWithExactly(notifier.errorL10n, sinon.match.string);
|
||||
});
|
||||
|
||||
it("should invoke endCall()", function() {
|
||||
conversation.trigger("session:connection-error");
|
||||
|
||||
sinon.assert.calledOnce(router.endCall);
|
||||
sinon.assert.calledWithExactly(router.endCall);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it("should call startCall() once the call session is ready", function() {
|
||||
conversation.trigger("session:ready");
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ for platform in all_platforms:
|
|||
whitelist['nightly'][platform] = [
|
||||
'ac_add_options --enable-update-channel=nightly',
|
||||
'ac_add_options --enable-profiling',
|
||||
'mk_add_options CLIENT_PY_ARGS="--hg-options=\'--verbose --time\' --hgtool=../tools/buildfarm/utils/hgtool.py --skip-chatzilla --skip-comm --skip-inspector --skip-venkman --tinderbox-print"'
|
||||
'mk_add_options CLIENT_PY_ARGS="--hg-options=\'--verbose --time\' --hgtool=../tools/buildfarm/utils/hgtool.py --skip-chatzilla --skip-comm --skip-inspector --tinderbox-print"'
|
||||
]
|
||||
|
||||
for platform in ['linux32', 'linux64', 'macosx-universal']:
|
||||
|
|
|
@ -84,10 +84,6 @@ ifneq (,$(filter WINNT Darwin Android,$(OS_TARGET)))
|
|||
DEFINES += -DMOZ_SHARED_MOZGLUE=1
|
||||
endif
|
||||
|
||||
ifdef MOZ_JSDEBUGGER
|
||||
DEFINES += -DMOZ_JSDEBUGGER
|
||||
endif
|
||||
|
||||
ifdef NECKO_WIFI
|
||||
DEFINES += -DNECKO_WIFI
|
||||
endif
|
||||
|
|
|
@ -247,9 +247,6 @@
|
|||
@BINPATH@/components/inspector.xpt
|
||||
@BINPATH@/components/intl.xpt
|
||||
@BINPATH@/components/jar.xpt
|
||||
#ifdef MOZ_JSDEBUGGER
|
||||
@BINPATH@/components/jsdservice.xpt
|
||||
#endif
|
||||
@BINPATH@/components/jsdebugger.xpt
|
||||
@BINPATH@/components/jsdownloads.xpt
|
||||
@BINPATH@/components/jsinspector.xpt
|
||||
|
|
|
@ -28,3 +28,5 @@ close_window=Close this window
|
|||
|
||||
cannot_start_call_session_not_ready=Can't start call, session is not ready.
|
||||
network_disconnected=The network connection terminated abruptly.
|
||||
|
||||
connection_error_see_console_notification=Call failed; see console for details.
|
||||
|
|
|
@ -31,11 +31,6 @@ leak:GI___strdup
|
|||
### Many leaks only affect some test suites. The suite annotations are not checked.
|
||||
###
|
||||
|
||||
# Bug 800200 - JSD1 is leaking, but it is about to be removed, so ignore it. m4
|
||||
leak:jsd_CreateLock
|
||||
leak:jsdScript::GetExecutableLines
|
||||
leak:jsdService::ActivateDebugger
|
||||
|
||||
# Bug 979928 - WebRTC is leaky. m2, m3
|
||||
leak:/media/mtransport/
|
||||
leak:/media/webrtc/signaling/
|
||||
|
|
18
configure.in
18
configure.in
|
@ -3849,7 +3849,6 @@ MOZ_BRANDING_DIRECTORY=
|
|||
MOZ_OFFICIAL_BRANDING=
|
||||
MOZ_FEEDS=1
|
||||
MOZ_WEBAPP_RUNTIME=
|
||||
MOZ_JSDEBUGGER=1
|
||||
MOZ_AUTH_EXTENSION=1
|
||||
MOZ_RAW=
|
||||
MOZ_VORBIS=
|
||||
|
@ -4944,15 +4943,6 @@ if test -n "$MOZ_ANDROID_BEAM"; then
|
|||
AC_DEFINE(MOZ_ANDROID_BEAM)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = JS Debugger XPCOM component (js/jsd)
|
||||
dnl ========================================================
|
||||
MOZ_ARG_DISABLE_BOOL(jsd,
|
||||
[ --disable-jsd Disable JavaScript debug library],
|
||||
MOZ_JSDEBUGGER=,
|
||||
MOZ_JSDEBUGGER=1)
|
||||
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable IPDL's "expensive" unit tests
|
||||
dnl ========================================================
|
||||
|
@ -6182,11 +6172,6 @@ if test `echo "$MOZ_EXTENSIONS" | grep -c gio` -ne 0; then
|
|||
fi
|
||||
AC_SUBST(MOZ_GIO_COMPONENT)
|
||||
|
||||
if test -z "$MOZ_JSDEBUGGER" -a `echo "$MOZ_EXTENSIONS" | grep -c venkman` -ne 0; then
|
||||
AC_MSG_WARN([Cannot build venkman without JavaScript debug library. Removing venkman from MOZ_EXTENSIONS.])
|
||||
MOZ_EXTENSIONS=`echo $MOZ_EXTENSIONS | sed -e 's|venkman||'`
|
||||
fi
|
||||
|
||||
dnl Remove dupes
|
||||
MOZ_EXTENSIONS=`${PERL} ${srcdir}/build/unix/uniq.pl ${MOZ_EXTENSIONS}`
|
||||
|
||||
|
@ -8195,7 +8180,7 @@ if test "$MOZ_XUL"; then
|
|||
AC_DEFINE(MOZ_XUL)
|
||||
else
|
||||
dnl remove extensions that require XUL
|
||||
MOZ_EXTENSIONS=`echo $MOZ_EXTENSIONS | sed -e 's/inspector//' -e 's/venkman//' -e 's/irc//' -e 's/tasks//'`
|
||||
MOZ_EXTENSIONS=`echo $MOZ_EXTENSIONS | sed -e 's/inspector//' -e 's/irc//' -e 's/tasks//'`
|
||||
fi
|
||||
|
||||
AC_SUBST(MOZ_XUL)
|
||||
|
@ -8429,7 +8414,6 @@ AC_SUBST(MOZ_DEBUG_DISABLE_DEFS)
|
|||
AC_SUBST(MOZ_DEBUG_LDFLAGS)
|
||||
AC_SUBST(WARNINGS_AS_ERRORS)
|
||||
AC_SUBST(MOZ_EXTENSIONS)
|
||||
AC_SUBST(MOZ_JSDEBUGGER)
|
||||
AC_SUBST(MOZ_ENABLE_PROFILER_SPS)
|
||||
AC_SUBST(MOZ_JPROF)
|
||||
AC_SUBST(MOZ_SHARK)
|
||||
|
|
|
@ -125,7 +125,7 @@ SVGEllipseElement::BuildPath()
|
|||
|
||||
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
|
||||
|
||||
ArcToBezier(pathBuilder.get(), Point(x, y), Size(rx, ry), 0, Float(2*M_PI), false);
|
||||
EllipseToBezier(pathBuilder.get(), Point(x, y), Size(rx, ry));
|
||||
|
||||
return pathBuilder->Finish();
|
||||
}
|
||||
|
|
|
@ -182,7 +182,7 @@ LOCAL_INCLUDES += [
|
|||
'/js/xpconnect/wrappers',
|
||||
]
|
||||
|
||||
for var in ('MOZ_JSDEBUGGER', 'MOZ_B2G_RIL', 'MOZ_B2G_FM'):
|
||||
for var in ('MOZ_B2G_RIL', 'MOZ_B2G_FM'):
|
||||
if CONFIG[var]:
|
||||
DEFINES[var] = True
|
||||
|
||||
|
|
|
@ -230,10 +230,6 @@
|
|||
#include "mozilla/dom/SpeechSynthesis.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_JSDEBUGGER
|
||||
#include "jsdIDebuggerService.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
#include "nsPISocketTransportService.h"
|
||||
#endif
|
||||
|
@ -10881,7 +10877,6 @@ nsGlobalWindow::ShowSlowScriptDialog()
|
|||
|
||||
// Prioritize the SlowScriptDebug interface over JSD1.
|
||||
nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
|
||||
bool oldDebugPossible = false;
|
||||
|
||||
if (hasFrame) {
|
||||
const char *debugCID = "@mozilla.org/dom/slow-script-debug;1";
|
||||
|
@ -10889,33 +10884,9 @@ nsGlobalWindow::ShowSlowScriptDialog()
|
|||
if (NS_SUCCEEDED(rv)) {
|
||||
debugService->GetActivationHandler(getter_AddRefs(debugCallback));
|
||||
}
|
||||
|
||||
if (!debugCallback) {
|
||||
oldDebugPossible = js::CanCallContextDebugHandler(cx);
|
||||
#ifdef MOZ_JSDEBUGGER
|
||||
// Get the debugger service if necessary.
|
||||
if (oldDebugPossible) {
|
||||
bool jsds_IsOn = false;
|
||||
const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
|
||||
nsCOMPtr<jsdIExecutionHook> jsdHook;
|
||||
nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
|
||||
|
||||
// Check if there's a user for the debugger service that's 'on' for us
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
|
||||
jsds->GetIsOn(&jsds_IsOn);
|
||||
}
|
||||
|
||||
// If there is a debug handler registered for this runtime AND
|
||||
// ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
|
||||
// then something useful will be done with our request to debug.
|
||||
oldDebugPossible = ((jsds_IsOn && (jsdHook != nullptr)) || !jsds_IsOn);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool showDebugButton = debugCallback || oldDebugPossible;
|
||||
bool showDebugButton = !!debugCallback;
|
||||
|
||||
// Get localizable strings
|
||||
nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg;
|
||||
|
@ -11024,10 +10995,6 @@ nsGlobalWindow::ShowSlowScriptDialog()
|
|||
rv = debugCallback->HandleSlowScriptDebug(this);
|
||||
return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
|
||||
}
|
||||
|
||||
if (oldDebugPossible) {
|
||||
return js_CallContextDebugHandler(cx) ? ContinueSlowScript : KillSlowScript;
|
||||
}
|
||||
}
|
||||
JS_ClearPendingException(cx);
|
||||
return KillSlowScript;
|
||||
|
|
|
@ -70,9 +70,6 @@
|
|||
#endif
|
||||
#include "AccessCheck.h"
|
||||
|
||||
#ifdef MOZ_JSDEBUGGER
|
||||
#include "jsdIDebuggerService.h"
|
||||
#endif
|
||||
#ifdef MOZ_LOGGING
|
||||
// Force PR_LOGGING so we can get JS strict warnings even in release builds
|
||||
#define FORCE_PR_LOG 1
|
||||
|
|
|
@ -129,13 +129,9 @@ CameraPreferences::PreferenceChanged(const char* aPref, void* aClosure)
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (NS_FAILED(rv)) {
|
||||
nsCString msg;
|
||||
msg.AppendPrintf("Failed to update pref '%s' (0x%x)\n", aPref, rv);
|
||||
NS_WARNING(msg.get());
|
||||
DOM_CAMERA_LOGE("Failed to get pref '%s' (0x%x)\n", aPref, rv);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EventListenerService.h"
|
||||
#ifdef MOZ_JSDEBUGGER
|
||||
#include "jsdIDebuggerService.h"
|
||||
#endif
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
|
@ -131,35 +128,6 @@ EventListenerInfo::ToSource(nsAString& aResult)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EventListenerInfo::GetDebugObject(nsISupports** aRetVal)
|
||||
{
|
||||
*aRetVal = nullptr;
|
||||
|
||||
#ifdef MOZ_JSDEBUGGER
|
||||
nsresult rv = NS_OK;
|
||||
nsCOMPtr<jsdIDebuggerService> jsd =
|
||||
do_GetService("@mozilla.org/js/jsd/debugger-service;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, NS_OK);
|
||||
|
||||
bool isOn = false;
|
||||
jsd->GetIsOn(&isOn);
|
||||
NS_ENSURE_TRUE(isOn, NS_OK);
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
if (GetJSVal(cx, ac, &v)) {
|
||||
nsCOMPtr<jsdIValue> jsdValue;
|
||||
rv = jsd->WrapValue(v, getter_AddRefs(jsdValue));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
jsdValue.forget(aRetVal);
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EventListenerService::GetListenerInfoFor(nsIDOMEventTarget* aEventTarget,
|
||||
uint32_t* aCount,
|
||||
|
|
|
@ -144,6 +144,3 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
|||
LOCAL_INCLUDES += [
|
||||
'/dom/wifi',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_JSDEBUGGER']:
|
||||
DEFINES['MOZ_JSDEBUGGER'] = True
|
||||
|
|
|
@ -12,7 +12,7 @@ interface nsIDOMEventTarget;
|
|||
* An instance of this interface describes how an event listener
|
||||
* was added to an event target.
|
||||
*/
|
||||
[scriptable, uuid(c4776eb7-05bc-49ce-a0ca-6213a346d53a)]
|
||||
[scriptable, uuid(11ba5fd7-8db2-4b1a-9f67-342cfa11afad)]
|
||||
interface nsIEventListenerInfo : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -37,12 +37,6 @@ interface nsIEventListenerInfo : nsISupports
|
|||
* (for example with C++ listeners).
|
||||
*/
|
||||
AString toSource();
|
||||
|
||||
/**
|
||||
* If jsdIDebuggerService is active and the listener is implemented in JS,
|
||||
* this returns the listener as a jsdIValue. Otherwise null.
|
||||
*/
|
||||
nsISupports getDebugObject();
|
||||
};
|
||||
|
||||
[scriptable, uuid(f6964bfb-dabe-4cab-9733-be0ee2bf8171)]
|
||||
|
|
|
@ -22,12 +22,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=448602
|
|||
var els, root, l2, l3;
|
||||
|
||||
function runTests() {
|
||||
/*
|
||||
Disabled due to lack of present support for JSD in JM
|
||||
var jsdIDebuggerService = SpecialPowers.Ci.jsdIDebuggerService;
|
||||
var jsd = SpecialPowers.Components.classes['@mozilla.org/js/jsd/debugger-service;1']
|
||||
.getService(jsdIDebuggerService);
|
||||
*/
|
||||
els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(SpecialPowers.Ci.nsIEventListenerService);
|
||||
|
||||
|
@ -48,18 +42,6 @@ function runTests() {
|
|||
is(SpecialPowers.unwrap(infos[0].listenerObject), root.onclick,
|
||||
"Should have the right listener object (1)");
|
||||
|
||||
/*
|
||||
var jsdOn = jsd.isOn;
|
||||
if (!jsdOn) {
|
||||
is(infos[0].getDebugObject(), null,
|
||||
"If JSD isn't running, getDebugObject() should return null.")
|
||||
jsd.on();
|
||||
ok(jsd.isOn, "JSD should be running.");
|
||||
}
|
||||
var jsdvalue = infos[0].getDebugObject().QueryInterface(SpecialPowers.Ci.jsdIValue);
|
||||
is(jsdvalue.jsType, 3, "Event listener should be a function! (1)");
|
||||
*/
|
||||
|
||||
root.removeAttribute("onclick");
|
||||
root.setAttribute("onclick", "...invalid script...");
|
||||
SimpleTest.expectUncaughtException(true);
|
||||
|
@ -84,12 +66,6 @@ function runTests() {
|
|||
is(infos[0].allowsUntrusted, true, "Should allow untrusted events (2)");
|
||||
is(SpecialPowers.unwrap(infos[0].listenerObject), l,
|
||||
"Should have the right listener object (2)");
|
||||
/*
|
||||
jsdvalue = infos[0].getDebugObject().QueryInterface(SpecialPowers.Ci.jsdIValue);
|
||||
is(jsdvalue.jsType, 3, "Event listener should be a function!(2)");
|
||||
is(jsdvalue.getWrappedValue(), l, "Wrong JS value! (1)");
|
||||
*/
|
||||
|
||||
is(infos[1].toSource(), "(function (e) { alert(e); })",
|
||||
"Unexpected serialization (3)");
|
||||
is(infos[1].type, "foo", "Wrong type (3)");
|
||||
|
@ -98,11 +74,6 @@ function runTests() {
|
|||
is(SpecialPowers.unwrap(infos[1].listenerObject), l,
|
||||
"Should have the right listener object (3)");
|
||||
|
||||
/*
|
||||
jsdvalue2 = infos[1].getDebugObject().QueryInterface(SpecialPowers.Ci.jsdIValue);
|
||||
is(jsdvalue2.jsType, 3, "Event listener should be a function! (3)");
|
||||
is(jsdvalue2.getWrappedValue(), l, "Wrong JS value! (2)");
|
||||
*/
|
||||
root.removeEventListener("foo", l, true);
|
||||
root.removeEventListener("foo", l, false);
|
||||
infos = els.getListenerInfoFor(root, {});
|
||||
|
@ -142,12 +113,6 @@ function runTests() {
|
|||
|
||||
ok(hasDocumentInChain, "Should have document in event target chain!");
|
||||
ok(hasWindowInChain, "Should have window in event target chain!");
|
||||
/*
|
||||
if (!jsdOn) {
|
||||
jsd.off();
|
||||
ok(!jsd.isOn, "JSD shouldn't be running anymore.");
|
||||
}
|
||||
*/
|
||||
|
||||
try {
|
||||
els.getListenerInfoFor(null, {});
|
||||
|
|
|
@ -24,19 +24,31 @@
|
|||
}
|
||||
|
||||
function periodicCheck(type, checkFunc, successMessage, done) {
|
||||
var interval = setInterval(function periodic() {
|
||||
var num = 0;
|
||||
var timeout;
|
||||
function periodic() {
|
||||
if (checkFunc()) {
|
||||
ok(true, type + ' is ' + successMessage);
|
||||
clearInterval(interval);
|
||||
interval = null;
|
||||
done();
|
||||
} else {
|
||||
setupNext();
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
function setupNext() {
|
||||
// exponential backoff on the timer
|
||||
// on a very slow system (like the b2g emulator) a long timeout is
|
||||
// necessary, but we want to run fast if we can
|
||||
timeout = setTimeout(periodic, 200 << num);
|
||||
num++;
|
||||
}
|
||||
|
||||
setupNext();
|
||||
|
||||
return function cancel() {
|
||||
if (interval) {
|
||||
if (timeout) {
|
||||
ok(false, type + ' (' + successMessage + ')' +
|
||||
' failed after waiting full duration');
|
||||
clearInterval(interval);
|
||||
clearTimeout(timeout);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
@ -59,16 +71,22 @@
|
|||
var silent = check(constraintApplied, isSilence(view), 'be silence for audio');
|
||||
return sampleCount > 0 && silent;
|
||||
}
|
||||
function disconnect() {
|
||||
source.disconnect();
|
||||
analyser.disconnect();
|
||||
done();
|
||||
}
|
||||
return periodicCheck('audio', testAudio,
|
||||
(constraintApplied ? '' : 'not ') + 'silent', done);
|
||||
(constraintApplied ? '' : 'not ') + 'silent', disconnect);
|
||||
}
|
||||
|
||||
function mkElement(type) {
|
||||
var display = document.getElementById('display');
|
||||
// this makes an unattached element
|
||||
// it's not rendered to save the cycles that costs on b2g emulator
|
||||
// and it gets droped (and GC'd) when the test is done
|
||||
var e = document.createElement(type);
|
||||
e.width = 32;
|
||||
e.height = 24;
|
||||
display.appendChild(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
|
|
@ -187,6 +187,50 @@ var commandsPeerConnection = [
|
|||
});
|
||||
}
|
||||
],
|
||||
[
|
||||
'PC_REMOTE_CHECK_FOR_DUPLICATED_PORTS_IN_SDP',
|
||||
function (test) {
|
||||
var re = /a=candidate.* (UDP|TCP) [\d]+ ([\d\.]+) ([\d]+) typ host/g;
|
||||
|
||||
function _sdpCandidatesIntoArray(sdp) {
|
||||
var regexArray = [];
|
||||
var resultArray = [];
|
||||
while ((regexArray = re.exec(sdp)) !== null) {
|
||||
info("regexArray: " + regexArray);
|
||||
if ((regexArray[1] === "TCP") && (regexArray[3] === "9")) {
|
||||
// As both sides can advertise TCP active connection on port 9 lets
|
||||
// ignore them all together
|
||||
info("Ignoring TCP candidate on port 9");
|
||||
continue;
|
||||
}
|
||||
const triple = regexArray[1] + ":" + regexArray[2] + ":" + regexArray[3];
|
||||
info("triple: " + triple);
|
||||
if (resultArray.indexOf(triple) !== -1) {
|
||||
dump("SDP: " + sdp.replace(/[\r]/g, '') + "\n");
|
||||
ok(false, "This Transport:IP:Port " + triple + " appears twice in the SDP above!");
|
||||
}
|
||||
resultArray.push(triple);
|
||||
}
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
const offerTriples = _sdpCandidatesIntoArray(test._local_offer.sdp);
|
||||
info("Offer ICE host candidates: " + JSON.stringify(offerTriples));
|
||||
|
||||
const answerTriples = _sdpCandidatesIntoArray(test.pcRemote._last_answer.sdp);
|
||||
info("Answer ICE host candidates: " + JSON.stringify(answerTriples));
|
||||
|
||||
for (var i=0; i< offerTriples.length; i++) {
|
||||
if (answerTriples.indexOf(offerTriples[i]) !== -1) {
|
||||
dump("SDP offer: " + test._local_offer.sdp.replace(/[\r]/g, '') + "\n");
|
||||
dump("SDP answer: " + test.pcRemote._last_answer.sdp.replace(/[\r]/g, '') + "\n");
|
||||
ok(false, "This IP:Port " + offerTriples[i] + " appears in SDP offer and answer!");
|
||||
}
|
||||
}
|
||||
|
||||
test.next();
|
||||
}
|
||||
],
|
||||
[
|
||||
'PC_REMOTE_SET_LOCAL_DESCRIPTION',
|
||||
function (test) {
|
||||
|
|
|
@ -36,8 +36,8 @@ function theTest() {
|
|||
}
|
||||
var cancelAudioCheck = audioIsSilence(withConstraint, stream, checkDone);
|
||||
var cancelVideoCheck = videoIsBlack(withConstraint, stream, checkDone);
|
||||
setTimeout(cancelAudioCheck, 20000);
|
||||
setTimeout(cancelVideoCheck, 20000);
|
||||
setTimeout(cancelAudioCheck, 3*60*1000);
|
||||
setTimeout(cancelVideoCheck, 3*60*1000);
|
||||
}, function(e) {
|
||||
ok(false, 'gUM error: ' + e);
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
|
||||
#include "SharedWorker.h"
|
||||
|
@ -17,6 +18,7 @@ using mozilla::dom::EventHandlerNonNull;
|
|||
using mozilla::dom::MessagePortBase;
|
||||
using mozilla::dom::Optional;
|
||||
using mozilla::dom::Sequence;
|
||||
using mozilla::dom::AutoNoJSAPI;
|
||||
using namespace mozilla;
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
@ -42,6 +44,28 @@ public:
|
|||
mEvents.SwapElements(aEvents);
|
||||
}
|
||||
|
||||
bool PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
return aWorkerPrivate->ModifyBusyCount(aCx, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
if (!aDispatchResult) {
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
aWorkerPrivate->ModifyBusyCount(aCx, false);
|
||||
}
|
||||
if (aCx) {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
|
||||
};
|
||||
|
@ -281,6 +305,8 @@ DelayedEventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|||
mMessagePort->AssertCorrectThread();
|
||||
MOZ_ASSERT(mEvents.Length());
|
||||
|
||||
AutoNoJSAPI nojsapi;
|
||||
|
||||
bool ignored;
|
||||
for (uint32_t i = 0; i < mEvents.Length(); i++) {
|
||||
mMessagePort->DispatchEvent(mEvents[i], &ignored);
|
||||
|
|
|
@ -11,20 +11,242 @@
|
|||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "RuntimeService.h"
|
||||
#include "ServiceWorker.h"
|
||||
#include "WorkerInlines.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
NS_IMPL_ISUPPORTS0(ServiceWorkerRegistration)
|
||||
|
||||
UpdatePromise::UpdatePromise()
|
||||
: mState(Pending)
|
||||
{
|
||||
MOZ_COUNT_CTOR(UpdatePromise);
|
||||
}
|
||||
|
||||
UpdatePromise::~UpdatePromise()
|
||||
{
|
||||
MOZ_COUNT_DTOR(UpdatePromise);
|
||||
}
|
||||
|
||||
void
|
||||
UpdatePromise::AddPromise(Promise* aPromise)
|
||||
{
|
||||
MOZ_ASSERT(mState == Pending);
|
||||
mPromises.AppendElement(aPromise);
|
||||
}
|
||||
|
||||
void
|
||||
UpdatePromise::ResolveAllPromises(const nsACString& aScriptSpec, const nsACString& aScope)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mState == Pending);
|
||||
mState = Resolved;
|
||||
RuntimeService* rs = RuntimeService::GetOrCreateService();
|
||||
MOZ_ASSERT(rs);
|
||||
|
||||
nsTArray<nsTWeakRef<Promise>> array;
|
||||
array.SwapElements(mPromises);
|
||||
for (uint32_t i = 0; i < array.Length(); ++i) {
|
||||
nsTWeakRef<Promise>& pendingPromise = array.ElementAt(i);
|
||||
if (pendingPromise) {
|
||||
nsCOMPtr<nsIGlobalObject> go =
|
||||
do_QueryInterface(pendingPromise->GetParentObject());
|
||||
MOZ_ASSERT(go);
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JS::Rooted<JSObject*> global(cx, go->GetGlobalJSObject());
|
||||
JSAutoCompartment ac(cx, global);
|
||||
|
||||
GlobalObject domGlobal(cx, global);
|
||||
|
||||
nsRefPtr<ServiceWorker> serviceWorker;
|
||||
nsresult rv = rs->CreateServiceWorker(domGlobal,
|
||||
NS_ConvertUTF8toUTF16(aScriptSpec),
|
||||
aScope,
|
||||
getter_AddRefs(serviceWorker));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
pendingPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
continue;
|
||||
}
|
||||
|
||||
pendingPromise->MaybeResolve(serviceWorker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UpdatePromise::RejectAllPromises(nsresult aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mState == Pending);
|
||||
mState = Rejected;
|
||||
|
||||
nsTArray<nsTWeakRef<Promise>> array;
|
||||
array.SwapElements(mPromises);
|
||||
for (uint32_t i = 0; i < array.Length(); ++i) {
|
||||
nsTWeakRef<Promise>& pendingPromise = array.ElementAt(i);
|
||||
if (pendingPromise) {
|
||||
// Since ServiceWorkerContainer is only exposed to windows we can be
|
||||
// certain about this cast.
|
||||
nsCOMPtr<nsPIDOMWindow> window =
|
||||
do_QueryInterface(pendingPromise->GetParentObject());
|
||||
MOZ_ASSERT(window);
|
||||
nsRefPtr<DOMError> domError = new DOMError(window, aRv);
|
||||
pendingPromise->MaybeRejectBrokenly(domError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FinishFetchOnMainThreadRunnable : public nsRunnable
|
||||
{
|
||||
nsMainThreadPtrHandle<ServiceWorkerUpdateInstance> mUpdateInstance;
|
||||
public:
|
||||
FinishFetchOnMainThreadRunnable
|
||||
(const nsMainThreadPtrHandle<ServiceWorkerUpdateInstance>& aUpdateInstance)
|
||||
: mUpdateInstance(aUpdateInstance)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run() MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
class FinishSuccessfulFetchWorkerRunnable : public WorkerRunnable
|
||||
{
|
||||
nsMainThreadPtrHandle<ServiceWorkerUpdateInstance> mUpdateInstance;
|
||||
public:
|
||||
FinishSuccessfulFetchWorkerRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
const nsMainThreadPtrHandle<ServiceWorkerUpdateInstance>& aUpdateInstance)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
|
||||
mUpdateInstance(aUpdateInstance)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
if (!aWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsRefPtr<FinishFetchOnMainThreadRunnable> r =
|
||||
new FinishFetchOnMainThreadRunnable(mUpdateInstance);
|
||||
NS_DispatchToMainThread(r);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Allows newer calls to Update() to 'abort' older calls.
|
||||
// Each call to Update() creates the instance which handles creating the
|
||||
// worker and queues up a runnable to resolve the update promise once the
|
||||
// worker has successfully been parsed.
|
||||
class ServiceWorkerUpdateInstance MOZ_FINAL : public nsISupports
|
||||
{
|
||||
// Owner of this instance.
|
||||
ServiceWorkerRegistration* mRegistration;
|
||||
nsCString mScriptSpec;
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
|
||||
bool mAborted;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
ServiceWorkerUpdateInstance(ServiceWorkerRegistration *aRegistration,
|
||||
nsPIDOMWindow* aWindow)
|
||||
: mRegistration(aRegistration),
|
||||
// Capture the current script spec in case register() gets called.
|
||||
mScriptSpec(aRegistration->mScriptSpec),
|
||||
mWindow(aWindow),
|
||||
mAborted(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
void
|
||||
Abort()
|
||||
{
|
||||
MOZ_ASSERT(!mAborted);
|
||||
mAborted = true;
|
||||
}
|
||||
|
||||
void
|
||||
Update()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
MOZ_ASSERT(swm);
|
||||
|
||||
nsRefPtr<ServiceWorker> serviceWorker;
|
||||
nsresult rv = swm->CreateServiceWorkerForWindow(mWindow,
|
||||
mScriptSpec,
|
||||
mRegistration->mScope,
|
||||
getter_AddRefs(serviceWorker));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
swm->RejectUpdatePromiseObservers(mRegistration, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<ServiceWorkerUpdateInstance> handle =
|
||||
new nsMainThreadPtrHolder<ServiceWorkerUpdateInstance>(this);
|
||||
// FIXME(nsm): Deal with error case (worker failed to download, redirect,
|
||||
// parse) in error handler patch.
|
||||
nsRefPtr<FinishSuccessfulFetchWorkerRunnable> r =
|
||||
new FinishSuccessfulFetchWorkerRunnable(serviceWorker->GetWorkerPrivate(), handle);
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
if (!r->Dispatch(cx)) {
|
||||
swm->RejectUpdatePromiseObservers(mRegistration, NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FetchDone()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
if (mAborted) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
MOZ_ASSERT(swm);
|
||||
swm->FinishFetch(mRegistration, mWindow);
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS0(ServiceWorkerUpdateInstance)
|
||||
|
||||
NS_IMETHODIMP
|
||||
FinishFetchOnMainThreadRunnable::Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mUpdateInstance->FetchDone();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ServiceWorkerRegistration::ServiceWorkerRegistration(const nsACString& aScope)
|
||||
: mScope(aScope),
|
||||
mPendingUninstall(false)
|
||||
{ }
|
||||
|
||||
ServiceWorkerRegistration::~ServiceWorkerRegistration()
|
||||
{ }
|
||||
|
||||
//////////////////////////
|
||||
// ServiceWorkerManager //
|
||||
//////////////////////////
|
||||
|
@ -89,13 +311,15 @@ public:
|
|||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
ServiceWorkerManager::ServiceWorkerDomainInfo* domainInfo =
|
||||
swm->mDomainMap.Get(domain);
|
||||
// FIXME(nsm): Refactor this pattern.
|
||||
// XXXnsm: This pattern can be refactored if we end up using it
|
||||
// often enough.
|
||||
if (!swm->mDomainMap.Get(domain, &domainInfo)) {
|
||||
domainInfo = new ServiceWorkerManager::ServiceWorkerDomainInfo;
|
||||
swm->mDomainMap.Put(domain, domainInfo);
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerRegistration> registration = domainInfo->GetRegistration(mScope);
|
||||
nsRefPtr<ServiceWorkerRegistration> registration =
|
||||
domainInfo->GetRegistration(mScope);
|
||||
|
||||
nsCString spec;
|
||||
rv = mScriptURI->GetSpec(spec);
|
||||
|
@ -107,9 +331,6 @@ public:
|
|||
if (registration) {
|
||||
registration->mPendingUninstall = false;
|
||||
if (spec.Equals(registration->mScriptSpec)) {
|
||||
// FIXME(nsm): Force update on Shift+Reload. Algorithm not specified for
|
||||
// that yet.
|
||||
|
||||
// There is an existing update in progress. Resolve with whatever it
|
||||
// results in.
|
||||
if (registration->HasUpdatePromise()) {
|
||||
|
@ -117,8 +338,8 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// There is no update in progress and since SW updating is upto the UA, we
|
||||
// will not update right now. Simply resolve with whatever worker we
|
||||
// There is no update in progress and since SW updating is upto the UA,
|
||||
// we will not update right now. Simply resolve with whatever worker we
|
||||
// have.
|
||||
ServiceWorkerInfo info = registration->Newest();
|
||||
if (info.IsValid()) {
|
||||
|
@ -143,10 +364,16 @@ public:
|
|||
|
||||
registration->mScriptSpec = spec;
|
||||
|
||||
// FIXME(nsm): Call Update. Same bug, different patch.
|
||||
// For now if the registration reaches this spot, the promise remains
|
||||
// unresolved.
|
||||
return NS_OK;
|
||||
rv = swm->Update(registration, mWindow);
|
||||
MOZ_ASSERT(registration->HasUpdatePromise());
|
||||
|
||||
// We append this register() call's promise after calling Update() because
|
||||
// we don't want this one to be aborted when the others (existing updates
|
||||
// for the same registration) are aborted. Update() sets a new
|
||||
// UpdatePromise on the registration.
|
||||
registration->mUpdatePromise->AddPromise(mPromise);
|
||||
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -154,7 +381,8 @@ public:
|
|||
// automatically reject the Promise.
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
||||
const nsAString& aScriptURL, nsISupports** aPromise)
|
||||
const nsAString& aScriptURL,
|
||||
nsISupports** aPromise)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
@ -206,10 +434,9 @@ ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// https://github.com/slightlyoff/ServiceWorker/issues/262
|
||||
// allowIfInheritsPrincipal: allow data: URLs for now.
|
||||
// Data URLs are not allowed.
|
||||
rv = documentPrincipal->CheckMayLoad(scriptURI, true /* report */,
|
||||
true /* allowIfInheritsPrincipal */);
|
||||
false /* allowIfInheritsPrincipal */);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
@ -238,11 +465,51 @@ ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
|||
return NS_DispatchToCurrentThread(registerRunnable);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::RejectUpdatePromiseObservers(ServiceWorkerRegistration* aRegistration,
|
||||
nsresult aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aRegistration->HasUpdatePromise());
|
||||
aRegistration->mUpdatePromise->RejectAllPromises(aRv);
|
||||
aRegistration->mUpdatePromise = nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update() does not return the Promise that the spec says it should. Callers
|
||||
* may access the registration's (new) Promise after calling this method.
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::Update(ServiceWorkerRegistration* aRegistration,
|
||||
nsPIDOMWindow* aWindow)
|
||||
{
|
||||
// FIXME(nsm): Same bug, different patch.
|
||||
if (aRegistration->HasUpdatePromise()) {
|
||||
NS_WARNING("Already had a UpdatePromise. Aborting that one!");
|
||||
RejectUpdatePromiseObservers(aRegistration, NS_ERROR_DOM_ABORT_ERR);
|
||||
MOZ_ASSERT(aRegistration->mUpdateInstance);
|
||||
aRegistration->mUpdateInstance->Abort();
|
||||
aRegistration->mUpdateInstance = nullptr;
|
||||
}
|
||||
|
||||
if (aRegistration->mInstallingWorker.IsValid()) {
|
||||
// FIXME(nsm): Terminate the worker. We still haven't figured out worker
|
||||
// instance ownership when not associated with a window, so let's wait on
|
||||
// this.
|
||||
// FIXME(nsm): We should be setting the state on the actual worker
|
||||
// instance.
|
||||
// FIXME(nsm): Fire "statechange" on installing worker instance.
|
||||
aRegistration->mInstallingWorker.Invalidate();
|
||||
}
|
||||
|
||||
aRegistration->mUpdatePromise = new UpdatePromise();
|
||||
// FIXME(nsm): Bug 931249. If we don't need to fetch & install, resolve
|
||||
// promise and skip this.
|
||||
// FIXME(nsm): Bug 931249. Force cache update if > 1 day.
|
||||
|
||||
aRegistration->mUpdateInstance =
|
||||
new ServiceWorkerUpdateInstance(aRegistration, aWindow);
|
||||
aRegistration->mUpdateInstance->Update();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -266,11 +533,69 @@ ServiceWorkerManager::Unregister(nsIDOMWindow* aWindow, const nsAString& aScope,
|
|||
already_AddRefed<ServiceWorkerManager>
|
||||
ServiceWorkerManager::GetInstance()
|
||||
{
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm =
|
||||
do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
nsRefPtr<ServiceWorkerManager> concrete = do_QueryObject(swm);
|
||||
return concrete.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::ResolveRegisterPromises(ServiceWorkerRegistration* aRegistration,
|
||||
const nsACString& aWorkerScriptSpec)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aRegistration->HasUpdatePromise());
|
||||
if (aRegistration->mUpdatePromise->IsRejected()) {
|
||||
aRegistration->mUpdatePromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
aRegistration->mUpdatePromise->ResolveAllPromises(aWorkerScriptSpec,
|
||||
aRegistration->mScope);
|
||||
aRegistration->mUpdatePromise = nullptr;
|
||||
}
|
||||
|
||||
// Must NS_Free() aString
|
||||
void
|
||||
ServiceWorkerManager::FinishFetch(ServiceWorkerRegistration* aRegistration,
|
||||
nsPIDOMWindow* aWindow)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MOZ_ASSERT(aRegistration->HasUpdatePromise());
|
||||
MOZ_ASSERT(aRegistration->mUpdateInstance);
|
||||
aRegistration->mUpdateInstance = nullptr;
|
||||
if (aRegistration->mUpdatePromise->IsRejected()) {
|
||||
aRegistration->mUpdatePromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// We have skipped Steps 3-8.3 of the Update algorithm here!
|
||||
|
||||
nsRefPtr<ServiceWorker> worker;
|
||||
nsresult rv = CreateServiceWorkerForWindow(aWindow,
|
||||
aRegistration->mScriptSpec,
|
||||
aRegistration->mScope,
|
||||
getter_AddRefs(worker));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
RejectUpdatePromiseObservers(aRegistration, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
ResolveRegisterPromises(aRegistration, aRegistration->mScriptSpec);
|
||||
|
||||
ServiceWorkerInfo info(aRegistration->mScriptSpec);
|
||||
Install(aRegistration, info);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::Install(ServiceWorkerRegistration* aRegistration,
|
||||
ServiceWorkerInfo aServiceWorkerInfo)
|
||||
{
|
||||
// FIXME(nsm): Same bug, different patch.
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
||||
const nsACString& aScriptSpec,
|
||||
|
|
|
@ -23,6 +23,44 @@ namespace dom {
|
|||
namespace workers {
|
||||
|
||||
class ServiceWorker;
|
||||
class ServiceWorkerUpdateInstance;
|
||||
|
||||
/**
|
||||
* UpdatePromise is a utility class that sort of imitates Promise, but not
|
||||
* completely. Using DOM Promise from C++ is a pain when we know the precise types
|
||||
* we're dealing with since it involves dealing with JSAPI. In this case we
|
||||
* also don't (yet) need the 'thenables added after resolution should trigger
|
||||
* immediately' support and other things like that. All we want is something
|
||||
* that works reasonably Promise like and can resolve real DOM Promises added
|
||||
* pre-emptively.
|
||||
*/
|
||||
class UpdatePromise MOZ_FINAL
|
||||
{
|
||||
public:
|
||||
UpdatePromise();
|
||||
~UpdatePromise();
|
||||
|
||||
void AddPromise(Promise* aPromise);
|
||||
void ResolveAllPromises(const nsACString& aScriptSpec, const nsACString& aScope);
|
||||
void RejectAllPromises(nsresult aRv);
|
||||
|
||||
bool
|
||||
IsRejected() const
|
||||
{
|
||||
return mState == Rejected;
|
||||
}
|
||||
|
||||
private:
|
||||
enum {
|
||||
Pending,
|
||||
Resolved,
|
||||
Rejected
|
||||
} mState;
|
||||
|
||||
// XXXnsm: Right now we don't need to support AddPromise() after
|
||||
// already being resolved (i.e. true Promise-like behaviour).
|
||||
nsTArray<nsTWeakRef<Promise>> mPromises;
|
||||
};
|
||||
|
||||
/*
|
||||
* Wherever the spec treats a worker instance and a description of said worker
|
||||
|
@ -32,7 +70,7 @@ class ServiceWorker;
|
|||
*/
|
||||
class ServiceWorkerInfo
|
||||
{
|
||||
const nsCString mScriptSpec;
|
||||
nsCString mScriptSpec;
|
||||
public:
|
||||
|
||||
bool
|
||||
|
@ -41,6 +79,12 @@ public:
|
|||
return !mScriptSpec.IsVoid();
|
||||
}
|
||||
|
||||
void
|
||||
Invalidate()
|
||||
{
|
||||
mScriptSpec.SetIsVoid(true);
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
GetScriptSpec() const
|
||||
{
|
||||
|
@ -49,19 +93,23 @@ public:
|
|||
}
|
||||
|
||||
ServiceWorkerInfo()
|
||||
{ }
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
explicit ServiceWorkerInfo(const nsACString& aScriptSpec)
|
||||
: mScriptSpec(aScriptSpec)
|
||||
{ }
|
||||
};
|
||||
|
||||
class ServiceWorkerRegistration
|
||||
// Needs to inherit from nsISupports because NS_ProxyRelease() does not support
|
||||
// non-ISupports classes.
|
||||
class ServiceWorkerRegistration MOZ_FINAL : public nsISupports
|
||||
{
|
||||
private:
|
||||
~ServiceWorkerRegistration() {}
|
||||
virtual ~ServiceWorkerRegistration();
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistration)
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsCString mScope;
|
||||
// The scriptURL for the registration. This may be completely different from
|
||||
|
@ -72,18 +120,20 @@ public:
|
|||
ServiceWorkerInfo mWaitingWorker;
|
||||
ServiceWorkerInfo mInstallingWorker;
|
||||
|
||||
bool mHasUpdatePromise;
|
||||
nsAutoPtr<UpdatePromise> mUpdatePromise;
|
||||
nsRefPtr<ServiceWorkerUpdateInstance> mUpdateInstance;
|
||||
|
||||
void
|
||||
AddUpdatePromiseObserver(Promise* aPromise)
|
||||
{
|
||||
// FIXME(nsm): Same bug, different patch.
|
||||
MOZ_ASSERT(HasUpdatePromise());
|
||||
mUpdatePromise->AddPromise(aPromise);
|
||||
}
|
||||
|
||||
bool
|
||||
HasUpdatePromise()
|
||||
{
|
||||
return mHasUpdatePromise;
|
||||
return mUpdatePromise;
|
||||
}
|
||||
|
||||
// When unregister() is called on a registration, it is not immediately
|
||||
|
@ -91,11 +141,7 @@ public:
|
|||
// pendingUninstall and when all controlling documents go away, removed.
|
||||
bool mPendingUninstall;
|
||||
|
||||
explicit ServiceWorkerRegistration(const nsACString& aScope)
|
||||
: mScope(aScope),
|
||||
mHasUpdatePromise(false),
|
||||
mPendingUninstall(false)
|
||||
{ }
|
||||
explicit ServiceWorkerRegistration(const nsACString& aScope);
|
||||
|
||||
ServiceWorkerInfo
|
||||
Newest() const
|
||||
|
@ -126,6 +172,8 @@ public:
|
|||
class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager
|
||||
{
|
||||
friend class RegisterRunnable;
|
||||
friend class CallInstallRunnable;
|
||||
friend class ServiceWorkerUpdateInstance;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -174,12 +222,17 @@ public:
|
|||
|
||||
nsClassHashtable<nsCStringHashKey, ServiceWorkerDomainInfo> mDomainMap;
|
||||
|
||||
// FIXME(nsm): What do we do if a page calls for register("/foo_worker.js", { scope: "/*"
|
||||
// }) and then another page calls register("/bar_worker.js", { scope: "/*" })
|
||||
// while the install is in progress. The async install steps for register
|
||||
// bar_worker.js could finish before foo_worker.js, but bar_worker still has
|
||||
// to be the winning controller.
|
||||
// FIXME(nsm): Move this into per domain?
|
||||
void
|
||||
ResolveRegisterPromises(ServiceWorkerRegistration* aRegistration,
|
||||
const nsACString& aWorkerScriptSpec);
|
||||
|
||||
void
|
||||
RejectUpdatePromiseObservers(ServiceWorkerRegistration* aRegistration,
|
||||
nsresult aResult);
|
||||
|
||||
void
|
||||
FinishFetch(ServiceWorkerRegistration* aRegistration,
|
||||
nsPIDOMWindow* aWindow);
|
||||
|
||||
static already_AddRefed<ServiceWorkerManager>
|
||||
GetInstance();
|
||||
|
@ -191,6 +244,10 @@ private:
|
|||
NS_IMETHOD
|
||||
Update(ServiceWorkerRegistration* aRegistration, nsPIDOMWindow* aWindow);
|
||||
|
||||
void
|
||||
Install(ServiceWorkerRegistration* aRegistration,
|
||||
ServiceWorkerInfo aServiceWorkerInfo);
|
||||
|
||||
NS_IMETHODIMP
|
||||
CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
||||
const nsACString& aScriptSpec,
|
||||
|
|
|
@ -40,6 +40,7 @@ SharedWorker::SharedWorker(nsPIDOMWindow* aWindow,
|
|||
SharedWorker::~SharedWorker()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
Close();
|
||||
MOZ_ASSERT(!mWorkerPrivate);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
self.addEventListener("connect", function(e) {
|
||||
var port = e.ports[0];
|
||||
port.onmessage = function(e) {
|
||||
port.postMessage(eval(e.data));
|
||||
};
|
||||
});
|
|
@ -7,6 +7,7 @@ support-files =
|
|||
bug1014466_worker.js
|
||||
bug1020226_worker.js
|
||||
bug1020226_frame.html
|
||||
bug998474_worker.js
|
||||
clearTimeouts_worker.js
|
||||
closeOnGC_server.sjs
|
||||
closeOnGC_worker.js
|
||||
|
@ -81,10 +82,12 @@ support-files =
|
|||
[test_atob.html]
|
||||
[test_blobConstructor.html]
|
||||
[test_blobWorkers.html]
|
||||
[test_bug1002702.html]
|
||||
[test_bug949946.html]
|
||||
[test_bug1010784.html]
|
||||
[test_bug1014466.html]
|
||||
[test_bug1020226.html]
|
||||
[test_bug998474.html]
|
||||
[test_chromeWorker.html]
|
||||
[test_clearTimeouts.html]
|
||||
[test_close.html]
|
||||
|
@ -121,6 +124,7 @@ skip-if = (toolkit == 'gonk' && debug) #debug-only failure
|
|||
[test_onLine.html]
|
||||
skip-if = (toolkit == 'gonk' && debug) #debug-only failure
|
||||
[test_promise.html]
|
||||
[test_promise_resolved_with_string.html]
|
||||
[test_recursion.html]
|
||||
[test_recursiveOnerror.html]
|
||||
[test_relativeLoad.html]
|
||||
|
|
|
@ -1,2 +1,8 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
worker.js
|
||||
worker2.js
|
||||
worker3.js
|
||||
|
||||
[test_installation_simple.html]
|
||||
[test_navigator.html]
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function simpleRegister() {
|
||||
var p = navigator.serviceWorker.register("/fake_worker.js");
|
||||
var p = navigator.serviceWorker.register("worker.js");
|
||||
ok(p instanceof Promise, "register() should return a Promise");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
@ -50,6 +50,48 @@
|
|||
ok(false, "non-HTTPS pages cannot register ServiceWorkers");
|
||||
}, function(e) {
|
||||
ok(e.name === "SecurityError", "Should fail with a SecurityError");
|
||||
}).then(function() {
|
||||
return new Promise((resolve) => SpecialPowers.popPrefEnv(resolve));
|
||||
});
|
||||
}
|
||||
|
||||
function realWorker() {
|
||||
var p = navigator.serviceWorker.register("worker.js");
|
||||
return p.then(function(w) {
|
||||
ok(w instanceof ServiceWorker, "Register a ServiceWorker");
|
||||
info(w.scope);
|
||||
ok(w.scope == (new URL("/*", document.baseURI)).href, "Scope should match");
|
||||
ok(w.url == (new URL("worker.js", document.baseURI)).href, "URL should be of the worker");
|
||||
}, function(e) {
|
||||
info(e.name);
|
||||
ok(false, "Registration should have succeeded!");
|
||||
});
|
||||
}
|
||||
|
||||
function abortPrevious() {
|
||||
var p = navigator.serviceWorker.register("worker2.js", { scope: "foo/*" });
|
||||
var q = navigator.serviceWorker.register("worker3.js", { scope: "foo/*" });
|
||||
|
||||
return Promise.all([
|
||||
p.then(function(w) {
|
||||
ok(false, "First registration should fail with AbortError");
|
||||
}, function(e) {
|
||||
ok(e.name === "AbortError", "First registration should fail with AbortError");
|
||||
}),
|
||||
|
||||
q.then(function(w) {
|
||||
ok(w instanceof ServiceWorker, "Second registration should succeed");
|
||||
}, function(e) {
|
||||
ok(false, "Second registration should succeed");
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
function networkError404() {
|
||||
return navigator.serviceWorker.register("404.js").then(function(w) {
|
||||
todo(false, "Should fail with NetworkError");
|
||||
}, function(e) {
|
||||
todo(e.name === "NetworkError", "Should fail with NetworkError");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -58,6 +100,10 @@
|
|||
.then(sameOriginWorker)
|
||||
.then(sameOriginScope)
|
||||
.then(httpsOnly)
|
||||
.then(realWorker)
|
||||
.then(abortPrevious)
|
||||
// FIXME(nsm): Uncomment once we have the error trapping patch from Bug 984048.
|
||||
// .then(networkError404)
|
||||
// put more tests here.
|
||||
.then(function() {
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
// empty worker, always succeed!
|
|
@ -0,0 +1 @@
|
|||
// worker2.js
|
|
@ -0,0 +1 @@
|
|||
// worker3.js
|
|
@ -0,0 +1,27 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bug 1002702</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var port = new SharedWorker('data:application/javascript,1').port;
|
||||
port.close();
|
||||
SpecialPowers.forceGC();
|
||||
ok(true, "No crash \\o/");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bug 998474</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body onload="boom();">
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function boom()
|
||||
{
|
||||
var worker = new SharedWorker("bug998474_worker.js");
|
||||
|
||||
setTimeout(function() {
|
||||
port = worker.port;
|
||||
port.postMessage("");
|
||||
|
||||
setTimeout(function() {
|
||||
port.start();
|
||||
ok(true, "Still alive!");
|
||||
SimpleTest.finish();
|
||||
}, 150);
|
||||
}, 150);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1027221
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1027221</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 1027221 **/
|
||||
// Set up a permanent atom
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var x = "x";
|
||||
// Trigger some incremental gc
|
||||
SpecialPowers.Cu.getJSTestingFunctions().gcslice(0);
|
||||
|
||||
// Kick off a worker that uses this same atom
|
||||
var w = new Worker("data:text/plain,Promise.resolve('x').then(function() { postMessage(1); });");
|
||||
// Maybe trigger some more incremental gc
|
||||
SpecialPowers.Cu.getJSTestingFunctions().gcslice(0);
|
||||
|
||||
w.onmessage = function() {
|
||||
ok(true, "Got here");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1027221">Mozilla Bug 1027221</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -620,6 +620,8 @@ public:
|
|||
DrawTarget() : mTransformDirty(false), mPermitSubpixelAA(false) {}
|
||||
virtual ~DrawTarget() {}
|
||||
|
||||
virtual DrawTargetType GetType() const = 0;
|
||||
|
||||
virtual BackendType GetBackendType() const = 0;
|
||||
/**
|
||||
* Returns a SourceSurface which is a snapshot of the current contents of the DrawTarget.
|
||||
|
|
|
@ -155,6 +155,13 @@ DrawTargetCG::~DrawTargetCG()
|
|||
CGContextRelease(mCg);
|
||||
}
|
||||
|
||||
DrawTargetType
|
||||
DrawTargetCG::GetType() const
|
||||
{
|
||||
return GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED ?
|
||||
DrawTargetType::HARDWARE_RASTER : DrawTargetType::SOFTWARE_RASTER;
|
||||
}
|
||||
|
||||
BackendType
|
||||
DrawTargetCG::GetBackendType() const
|
||||
{
|
||||
|
|
|
@ -100,6 +100,7 @@ public:
|
|||
DrawTargetCG();
|
||||
virtual ~DrawTargetCG();
|
||||
|
||||
virtual DrawTargetType GetType() const MOZ_OVERRIDE;
|
||||
virtual BackendType GetBackendType() const;
|
||||
virtual TemporaryRef<SourceSurface> Snapshot();
|
||||
|
||||
|
|
|
@ -372,6 +372,56 @@ DrawTargetCairo::~DrawTargetCairo()
|
|||
MOZ_ASSERT(!mLockedBits);
|
||||
}
|
||||
|
||||
DrawTargetType
|
||||
DrawTargetCairo::GetType() const
|
||||
{
|
||||
if (mContext) {
|
||||
cairo_surface_type_t type = cairo_surface_get_type(mSurface);
|
||||
if (type == CAIRO_SURFACE_TYPE_TEE) {
|
||||
type = cairo_surface_get_type(cairo_tee_surface_index(mSurface, 0));
|
||||
MOZ_ASSERT(type != CAIRO_SURFACE_TYPE_TEE, "C'mon!");
|
||||
MOZ_ASSERT(type == cairo_surface_get_type(cairo_tee_surface_index(mSurface, 1)),
|
||||
"What should we do here?");
|
||||
}
|
||||
switch (type) {
|
||||
case CAIRO_SURFACE_TYPE_PDF:
|
||||
case CAIRO_SURFACE_TYPE_PS:
|
||||
case CAIRO_SURFACE_TYPE_SVG:
|
||||
case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
|
||||
case CAIRO_SURFACE_TYPE_XML:
|
||||
return DrawTargetType::VECTOR;
|
||||
|
||||
case CAIRO_SURFACE_TYPE_VG:
|
||||
case CAIRO_SURFACE_TYPE_GL:
|
||||
case CAIRO_SURFACE_TYPE_GLITZ:
|
||||
case CAIRO_SURFACE_TYPE_QUARTZ:
|
||||
case CAIRO_SURFACE_TYPE_DIRECTFB:
|
||||
return DrawTargetType::HARDWARE_RASTER;
|
||||
|
||||
case CAIRO_SURFACE_TYPE_SKIA:
|
||||
case CAIRO_SURFACE_TYPE_QT:
|
||||
MOZ_ASSERT(false, "Can't determine actual DrawTargetType for DrawTargetCairo - assuming SOFTWARE_RASTER");
|
||||
// fallthrough
|
||||
case CAIRO_SURFACE_TYPE_IMAGE:
|
||||
case CAIRO_SURFACE_TYPE_XLIB:
|
||||
case CAIRO_SURFACE_TYPE_XCB:
|
||||
case CAIRO_SURFACE_TYPE_WIN32:
|
||||
case CAIRO_SURFACE_TYPE_BEOS:
|
||||
case CAIRO_SURFACE_TYPE_OS2:
|
||||
case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE:
|
||||
case CAIRO_SURFACE_TYPE_SCRIPT:
|
||||
case CAIRO_SURFACE_TYPE_RECORDING:
|
||||
case CAIRO_SURFACE_TYPE_DRM:
|
||||
case CAIRO_SURFACE_TYPE_SUBSURFACE:
|
||||
case CAIRO_SURFACE_TYPE_D2D:
|
||||
case CAIRO_SURFACE_TYPE_TEE: // included to silence warning about unhandled enum value
|
||||
return DrawTargetType::SOFTWARE_RASTER;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(false, "Could not determine DrawTargetType for DrawTargetCairo");
|
||||
return DrawTargetType::SOFTWARE_RASTER;
|
||||
}
|
||||
|
||||
IntSize
|
||||
DrawTargetCairo::GetSize()
|
||||
{
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
DrawTargetCairo();
|
||||
virtual ~DrawTargetCairo();
|
||||
|
||||
virtual DrawTargetType GetType() const MOZ_OVERRIDE;
|
||||
virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
|
||||
virtual TemporaryRef<SourceSurface> Snapshot();
|
||||
virtual IntSize GetSize();
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
DrawTargetD2D();
|
||||
virtual ~DrawTargetD2D();
|
||||
|
||||
virtual DrawTargetType GetType() const MOZ_OVERRIDE { return DrawTargetType::HARDWARE_RASTER; }
|
||||
virtual BackendType GetBackendType() const { return BackendType::DIRECT2D; }
|
||||
virtual TemporaryRef<SourceSurface> Snapshot();
|
||||
virtual IntSize GetSize() { return mSize; }
|
||||
|
|
|
@ -39,6 +39,7 @@ public:
|
|||
DrawTargetD2D1();
|
||||
virtual ~DrawTargetD2D1();
|
||||
|
||||
virtual DrawTargetType GetType() const MOZ_OVERRIDE { return DrawTargetType::HARDWARE_RASTER; }
|
||||
virtual BackendType GetBackendType() const { return BackendType::DIRECT2D1_1; }
|
||||
virtual TemporaryRef<SourceSurface> Snapshot();
|
||||
virtual IntSize GetSize() { return mSize; }
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
mFormat = aA->GetFormat();
|
||||
}
|
||||
|
||||
virtual DrawTargetType GetType() const MOZ_OVERRIDE { return mA->GetType(); }
|
||||
virtual BackendType GetBackendType() const { return mA->GetBackendType(); }
|
||||
virtual TemporaryRef<SourceSurface> Snapshot() { return new SourceSurfaceDual(mA, mB); }
|
||||
virtual IntSize GetSize() { return mA->GetSize(); }
|
||||
|
|
|
@ -19,6 +19,7 @@ public:
|
|||
DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData = false);
|
||||
~DrawTargetRecording();
|
||||
|
||||
virtual DrawTargetType GetType() const MOZ_OVERRIDE { return mFinalDT->GetType(); }
|
||||
virtual BackendType GetBackendType() const { return mFinalDT->GetBackendType(); }
|
||||
|
||||
virtual TemporaryRef<SourceSurface> Snapshot();
|
||||
|
|
|
@ -277,12 +277,12 @@ struct AutoPaintSetup {
|
|||
mPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
|
||||
SkPaint temp;
|
||||
temp.setXfermodeMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
|
||||
temp.setAlpha(U8CPU(aOptions.mAlpha*255+0.5));
|
||||
temp.setAlpha(ColorFloatToByte(aOptions.mAlpha));
|
||||
//TODO: Get a rect here
|
||||
mCanvas->saveLayer(nullptr, &temp);
|
||||
mNeedsRestore = true;
|
||||
} else {
|
||||
mPaint.setAlpha(U8CPU(aOptions.mAlpha*255.0+0.5));
|
||||
mPaint.setAlpha(ColorFloatToByte(aOptions.mAlpha));
|
||||
mAlpha = aOptions.mAlpha;
|
||||
}
|
||||
mPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
|
||||
|
@ -339,6 +339,17 @@ DrawTargetSkia::DrawSurface(SourceSurface *aSurface,
|
|||
mCanvas->drawBitmapRectToRect(bitmap.mBitmap, &sourceRect, destRect, &paint.mPaint);
|
||||
}
|
||||
|
||||
DrawTargetType
|
||||
DrawTargetSkia::GetType() const
|
||||
{
|
||||
#ifdef USE_SKIA_GPU
|
||||
if (mGrContext) {
|
||||
return DrawTargetType::HARDWARE_RASTER;
|
||||
}
|
||||
#endif
|
||||
return DrawTargetType::SOFTWARE_RASTER;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetSkia::DrawFilter(FilterNode *aNode,
|
||||
const Rect &aSourceRect,
|
||||
|
|
|
@ -31,6 +31,7 @@ public:
|
|||
DrawTargetSkia();
|
||||
virtual ~DrawTargetSkia();
|
||||
|
||||
virtual DrawTargetType GetType() const MOZ_OVERRIDE;
|
||||
virtual BackendType GetBackendType() const { return BackendType::SKIA; }
|
||||
virtual TemporaryRef<SourceSurface> Snapshot();
|
||||
virtual IntSize GetSize() { return mSize; }
|
||||
|
|
|
@ -21,6 +21,7 @@ public:
|
|||
|
||||
bool Init(const TileSet& mTiles);
|
||||
|
||||
virtual DrawTargetType GetType() const MOZ_OVERRIDE { return mTiles[0].mDrawTarget->GetType(); }
|
||||
virtual BackendType GetBackendType() const { return mTiles[0].mDrawTarget->GetBackendType(); }
|
||||
virtual TemporaryRef<SourceSurface> Snapshot();
|
||||
virtual IntSize GetSize() { return IntSize(mRect.XMost(), mRect.YMost()); }
|
||||
|
|
|
@ -235,11 +235,20 @@ GfxOpToSkiaOp(CompositionOp op)
|
|||
}
|
||||
}
|
||||
|
||||
static inline SkColor ColorToSkColor(const Color &color, Float aAlpha)
|
||||
/* There's quite a bit of inconsistency about
|
||||
* whether float colors should be rounded with .5f.
|
||||
* We choose to do it to match cairo which also
|
||||
* happens to match the Direct3D specs */
|
||||
static inline U8CPU ColorFloatToByte(Float color)
|
||||
{
|
||||
//XXX: do a better job converting to int
|
||||
return SkColorSetARGB(U8CPU(color.a*aAlpha*255.0), U8CPU(color.r*255.0),
|
||||
U8CPU(color.g*255.0), U8CPU(color.b*255.0));
|
||||
return U8CPU(color*255.f + .5f);
|
||||
};
|
||||
|
||||
static inline SkColor ColorToSkColor(const Color &color, Float aAlpha)
|
||||
{
|
||||
return SkColorSetARGB(ColorFloatToByte(color.a*aAlpha), ColorFloatToByte(color.r),
|
||||
ColorFloatToByte(color.g), ColorFloatToByte(color.b));
|
||||
}
|
||||
|
||||
static inline SkRect
|
||||
|
|
|
@ -83,6 +83,51 @@ void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius,
|
|||
}
|
||||
}
|
||||
|
||||
/* This is basically the ArcToBezier with the parameters for drawing a circle
|
||||
* inlined which vastly simplifies it and avoids a bunch of transcedental function
|
||||
* calls which should make it faster. */
|
||||
template <typename T>
|
||||
void EllipseToBezier(T* aSink, const Point &aOrigin, const Size &aRadius)
|
||||
{
|
||||
Point startPoint(aOrigin.x + aRadius.width,
|
||||
aOrigin.y);
|
||||
|
||||
aSink->LineTo(startPoint);
|
||||
|
||||
// Calculate kappa constant for partial curve. The sign of angle in the
|
||||
// tangent will actually ensure this is negative for a counter clockwise
|
||||
// sweep, so changing signs later isn't needed.
|
||||
Float kappaFactor = (4.0f / 3.0f) * tan((M_PI/2.0f) / 4.0f);
|
||||
Float kappaX = kappaFactor * aRadius.width;
|
||||
Float kappaY = kappaFactor * aRadius.height;
|
||||
Float cosStartAngle = 1;
|
||||
Float sinStartAngle = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// We guarantee here the current point is the start point of the next
|
||||
// curve segment.
|
||||
Point currentStartPoint(aOrigin.x + cosStartAngle * aRadius.width,
|
||||
aOrigin.y + sinStartAngle * aRadius.height);
|
||||
Point currentEndPoint(aOrigin.x + -sinStartAngle * aRadius.width,
|
||||
aOrigin.y + cosStartAngle * aRadius.height);
|
||||
|
||||
Point tangentStart(-sinStartAngle, cosStartAngle);
|
||||
Point cp1 = currentStartPoint;
|
||||
cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY);
|
||||
|
||||
Point revTangentEnd(cosStartAngle, sinStartAngle);
|
||||
Point cp2 = currentEndPoint;
|
||||
cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY);
|
||||
|
||||
aSink->BezierTo(cp1, cp2, currentEndPoint);
|
||||
|
||||
// cos(x+pi/2) == -sin(x)
|
||||
// sin(x+pi/2) == cos(x)
|
||||
Float tmp = cosStartAngle;
|
||||
cosStartAngle = -sinStartAngle;
|
||||
sinStartAngle = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a path represending a rounded rectangle to the path being built by
|
||||
* aPathBuilder.
|
||||
|
|
|
@ -72,6 +72,12 @@ MOZ_BEGIN_ENUM_CLASS(FilterType, int8_t)
|
|||
UNPREMULTIPLY
|
||||
MOZ_END_ENUM_CLASS(FilterType)
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(DrawTargetType, int8_t)
|
||||
SOFTWARE_RASTER = 0,
|
||||
HARDWARE_RASTER,
|
||||
VECTOR
|
||||
MOZ_END_ENUM_CLASS(DrawTargetType)
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(BackendType, int8_t)
|
||||
NONE = 0,
|
||||
DIRECT2D,
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "LayerSorter.h" // for SortLayersBy3DZOrder
|
||||
#include "LayersLogging.h" // for AppendToString
|
||||
#include "ReadbackLayer.h" // for ReadbackLayer
|
||||
#include "gfxImageSurface.h"
|
||||
#include "gfxPlatform.h" // for gfxPlatform
|
||||
#include "gfxUtils.h" // for gfxUtils, etc
|
||||
#include "gfx2DGlue.h"
|
||||
|
@ -1232,18 +1231,13 @@ void WriteSnapshotLinkToDumpFile(T* aObj, std::stringstream& aStream)
|
|||
template <typename T>
|
||||
void WriteSnapshotToDumpFile_internal(T* aObj, DataSourceSurface* aSurf)
|
||||
{
|
||||
nsRefPtr<gfxImageSurface> deprecatedSurf =
|
||||
new gfxImageSurface(aSurf->GetData(),
|
||||
ThebesIntSize(aSurf->GetSize()),
|
||||
aSurf->Stride(),
|
||||
SurfaceFormatToImageFormat(aSurf->GetFormat()));
|
||||
nsCString string(aObj->Name());
|
||||
string.Append('-');
|
||||
string.AppendInt((uint64_t)aObj);
|
||||
if (gfxUtils::sDumpPaintFile) {
|
||||
fprintf_stderr(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading());
|
||||
}
|
||||
deprecatedSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile);
|
||||
gfxUtils::DumpAsDataURI(aSurf, gfxUtils::sDumpPaintFile);
|
||||
if (gfxUtils::sDumpPaintFile) {
|
||||
fprintf_stderr(gfxUtils::sDumpPaintFile, "\";");
|
||||
}
|
||||
|
|
|
@ -55,6 +55,17 @@ public:
|
|||
uint64_t mAsyncID;
|
||||
};
|
||||
|
||||
void
|
||||
RemoveTextureFromCompositableTracker::ReleaseTextureClient()
|
||||
{
|
||||
if (mTextureClient) {
|
||||
TextureClientReleaseTask* task = new TextureClientReleaseTask(mTextureClient);
|
||||
RefPtr<ISurfaceAllocator> allocator = mTextureClient->GetAllocator();
|
||||
mTextureClient = nullptr;
|
||||
allocator->GetMessageLoop()->PostTask(FROM_HERE, task);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
CompositableClient::TransactionCompleteted(PCompositableChild* aActor, uint64_t aTransactionId)
|
||||
{
|
||||
|
|
|
@ -42,22 +42,22 @@ public:
|
|||
~RemoveTextureFromCompositableTracker()
|
||||
{
|
||||
MOZ_COUNT_DTOR(RemoveTextureFromCompositableTracker);
|
||||
ReleaseTextureClient();
|
||||
}
|
||||
|
||||
virtual void Complete() MOZ_OVERRIDE
|
||||
{
|
||||
// The TextureClient's recycling is postponed until the transaction
|
||||
// complete.
|
||||
mTextureClient = nullptr;
|
||||
ReleaseTextureClient();
|
||||
}
|
||||
|
||||
virtual void Cancel() MOZ_OVERRIDE
|
||||
{
|
||||
mTextureClient = nullptr;
|
||||
ReleaseTextureClient();
|
||||
}
|
||||
|
||||
virtual void SetTextureClient(TextureClient* aTextureClient) MOZ_OVERRIDE
|
||||
{
|
||||
ReleaseTextureClient();
|
||||
mTextureClient = aTextureClient;
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void ReleaseTextureClient();
|
||||
|
||||
private:
|
||||
RefPtr<TextureClient> mTextureClient;
|
||||
};
|
||||
|
|
|
@ -203,6 +203,7 @@ TextureClient::InitIPDLActor(CompositableForwarder* aForwarder)
|
|||
MOZ_ASSERT(mActor);
|
||||
mActor->mForwarder = aForwarder;
|
||||
mActor->mTextureClient = this;
|
||||
mAllocator = aForwarder;
|
||||
mShared = true;
|
||||
return mActor->IPCOpen();
|
||||
}
|
||||
|
|
|
@ -364,7 +364,13 @@ protected:
|
|||
mFlags |= aFlags;
|
||||
}
|
||||
|
||||
ISurfaceAllocator* GetAllocator()
|
||||
{
|
||||
return mAllocator;
|
||||
}
|
||||
|
||||
RefPtr<TextureChild> mActor;
|
||||
RefPtr<ISurfaceAllocator> mAllocator;
|
||||
TextureFlags mFlags;
|
||||
bool mShared;
|
||||
bool mValid;
|
||||
|
@ -372,10 +378,30 @@ protected:
|
|||
FenceHandle mAcquireFenceHandle;
|
||||
|
||||
friend class TextureChild;
|
||||
friend class RemoveTextureFromCompositableTracker;
|
||||
friend void TestTextureClientSurface(TextureClient*, gfxImageSurface*);
|
||||
friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&);
|
||||
};
|
||||
|
||||
/**
|
||||
* Task that releases TextureClient pointer on a specified thread.
|
||||
*/
|
||||
class TextureClientReleaseTask : public Task
|
||||
{
|
||||
public:
|
||||
TextureClientReleaseTask(TextureClient* aClient)
|
||||
: mTextureClient(aClient) {
|
||||
}
|
||||
|
||||
virtual void Run() MOZ_OVERRIDE
|
||||
{
|
||||
mTextureClient = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
mozilla::RefPtr<TextureClient> mTextureClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* TextureClient that wraps a random access buffer such as a Shmem or raw memory.
|
||||
* This class must be inherited to implement the memory allocation and access bits.
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <utility> // for pair
|
||||
#include "ContentHost.h" // for ContentHostDoubleBuffered, etc
|
||||
#include "Effects.h" // for EffectMask, Effect, etc
|
||||
#include "gfxUtils.h"
|
||||
#include "ImageHost.h" // for ImageHostBuffered, etc
|
||||
#include "TiledContentHost.h" // for TiledContentHost
|
||||
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
|
||||
|
@ -227,12 +228,8 @@ CompositableHost::DumpTextureHost(std::stringstream& aStream, TextureHost* aText
|
|||
dSurf->GetSize(),
|
||||
dSurf->Stride(),
|
||||
dSurf->GetFormat());
|
||||
nsRefPtr<gfxASurface> surf = platform->GetThebesSurfaceForDrawTarget(dt);
|
||||
if (!surf) {
|
||||
return;
|
||||
}
|
||||
// TODO stream surface
|
||||
surf->DumpAsDataURL(stderr);
|
||||
gfxUtils::DumpAsDataURI(dt, stderr);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -85,7 +85,9 @@ class ISurfaceAllocator : public AtomicRefCountedWithFinalize<ISurfaceAllocator>
|
|||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(ISurfaceAllocator)
|
||||
ISurfaceAllocator() {}
|
||||
ISurfaceAllocator()
|
||||
: mDefaultMessageLoop(MessageLoop::current())
|
||||
{}
|
||||
|
||||
void Finalize();
|
||||
|
||||
|
@ -163,6 +165,11 @@ public:
|
|||
virtual bool IPCOpen() const { return true; }
|
||||
virtual bool IsSameProcess() const = 0;
|
||||
|
||||
virtual MessageLoop * GetMessageLoop() const
|
||||
{
|
||||
return mDefaultMessageLoop;
|
||||
}
|
||||
|
||||
// Returns true if aSurface wraps a Shmem.
|
||||
static bool IsShmem(SurfaceDescriptor* aSurface);
|
||||
|
||||
|
@ -177,6 +184,8 @@ protected:
|
|||
// This is used to implement an extremely simple & naive heap allocator.
|
||||
std::vector<mozilla::ipc::Shmem> mUsedShmems;
|
||||
|
||||
MessageLoop* mDefaultMessageLoop;
|
||||
|
||||
friend class AtomicRefCountedWithFinalize<ISurfaceAllocator>;
|
||||
};
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ public:
|
|||
*
|
||||
* Can be called from any thread.
|
||||
*/
|
||||
MessageLoop * GetMessageLoop() const;
|
||||
virtual MessageLoop * GetMessageLoop() const MOZ_OVERRIDE;
|
||||
|
||||
PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo, uint64_t* aID) MOZ_OVERRIDE;
|
||||
bool DeallocPCompositableChild(PCompositableChild* aActor) MOZ_OVERRIDE;
|
||||
|
|
|
@ -261,7 +261,7 @@ ImageBridgeParent::RecvChildAsyncMessages(const InfallibleTArray<AsyncChildMessa
|
|||
return true;
|
||||
}
|
||||
|
||||
MessageLoop * ImageBridgeParent::GetMessageLoop() {
|
||||
MessageLoop * ImageBridgeParent::GetMessageLoop() const {
|
||||
return mMessageLoop;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ public:
|
|||
// Shutdown step 2
|
||||
virtual bool RecvStop() MOZ_OVERRIDE;
|
||||
|
||||
MessageLoop * GetMessageLoop();
|
||||
virtual MessageLoop* GetMessageLoop() const MOZ_OVERRIDE;
|
||||
|
||||
|
||||
// ISurfaceAllocator
|
||||
|
|
|
@ -28,14 +28,14 @@ class nsIAtom;
|
|||
class nsIWidget;
|
||||
|
||||
// IID for the nsITheme interface
|
||||
// {b0f3efe9-0bd4-4f6b-8daa-0ec7f6006822}
|
||||
// {7a3474d9-3bd6-407c-8657-c5c7633639f0}
|
||||
#define NS_ITHEME_IID \
|
||||
{ 0x4440b5c7, 0xd8bd, 0x4d9c, \
|
||||
{ 0x9c, 0x3e, 0xa5, 0xe6, 0x26, 0x81, 0x10, 0xa0 } }
|
||||
// {D930E29B-6909-44e5-AB4B-AF10D6923705}
|
||||
{ 0x7a3474d9, 0x3bd6, 0x407c, \
|
||||
{ 0x86, 0x57, 0xc5, 0xc7, 0x63, 0x36, 0x39, 0xf0 } }
|
||||
// {0ae05515-cf7a-45a8-9e02-6556de7685b1}
|
||||
#define NS_THEMERENDERER_CID \
|
||||
{ 0x9020805b, 0x14a3, 0x4125, \
|
||||
{ 0xa5, 0x63, 0x4a, 0x8c, 0x5d, 0xe0, 0xa9, 0xa3 } }
|
||||
{ 0x0ae05515, 0xcf7a, 0x45a8, \
|
||||
{ 0x9e, 0x02, 0x65, 0x56, 0xde, 0x76, 0x85, 0xb1 } }
|
||||
|
||||
/**
|
||||
* nsITheme is a service that provides platform-specific native
|
||||
|
@ -113,7 +113,7 @@ public:
|
|||
* minimum size; if false, this size is the only valid size for the
|
||||
* widget.
|
||||
*/
|
||||
NS_IMETHOD GetMinimumWidgetSize(nsRenderingContext* aContext,
|
||||
NS_IMETHOD GetMinimumWidgetSize(nsPresContext* aPresContext,
|
||||
nsIFrame* aFrame,
|
||||
uint8_t aWidgetType,
|
||||
nsIntSize* aResult,
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
#include "nsRect.h" // for nsRect, nsIntRect
|
||||
#include "nsRegion.h" // for nsIntRegionRectIterator, etc
|
||||
|
||||
class gfxASurface;
|
||||
|
||||
// XXXTodo: rename FORM_TWIPS to FROM_APPUNITS
|
||||
#define FROM_TWIPS(_x) ((gfxFloat)((_x)/(mP2A)))
|
||||
#define FROM_TWIPS_INT(_x) (NSToIntRound((gfxFloat)((_x)/(mP2A))))
|
||||
|
@ -65,13 +63,6 @@ static int32_t FindSafeLength(const char *aString, uint32_t aLength,
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
//// nsRenderingContext
|
||||
|
||||
void
|
||||
nsRenderingContext::Init(nsDeviceContext* aContext,
|
||||
gfxASurface *aThebesSurface)
|
||||
{
|
||||
Init(aContext, new gfxContext(aThebesSurface));
|
||||
}
|
||||
|
||||
void
|
||||
nsRenderingContext::Init(nsDeviceContext* aContext,
|
||||
gfxContext *aThebesContext)
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "nsString.h" // for nsString
|
||||
#include "nscore.h" // for char16_t
|
||||
|
||||
class gfxASurface;
|
||||
class nsIntRegion;
|
||||
struct nsPoint;
|
||||
struct nsRect;
|
||||
|
@ -45,7 +44,6 @@ public:
|
|||
|
||||
NS_INLINE_DECL_REFCOUNTING(nsRenderingContext)
|
||||
|
||||
void Init(nsDeviceContext* aContext, gfxASurface* aThebesSurface);
|
||||
void Init(nsDeviceContext* aContext, gfxContext* aThebesContext);
|
||||
void Init(nsDeviceContext* aContext, DrawTarget* aDrawTarget);
|
||||
|
||||
|
|
|
@ -683,179 +683,6 @@ gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxASurface::WriteAsPNG(const char* aFile)
|
||||
{
|
||||
FILE *file = fopen(aFile, "wb");
|
||||
if (file) {
|
||||
WriteAsPNG_internal(file, true);
|
||||
fclose(file);
|
||||
} else {
|
||||
NS_WARNING("Failed to create file!\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxASurface::DumpAsDataURL(FILE* aOutput)
|
||||
{
|
||||
WriteAsPNG_internal(aOutput, false);
|
||||
}
|
||||
|
||||
void
|
||||
gfxASurface::CopyAsDataURL()
|
||||
{
|
||||
WriteAsPNG_internal(nullptr, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to a PNG file. If aBinary is true, then it is written
|
||||
* as binary, otherwise as a data URL. If no file is specified then
|
||||
* data is copied to the clipboard (must not be binary!).
|
||||
*/
|
||||
void
|
||||
gfxASurface::WriteAsPNG_internal(FILE* aFile, bool aBinary)
|
||||
{
|
||||
nsRefPtr<gfxImageSurface> imgsurf = GetAsImageSurface();
|
||||
nsIntSize size;
|
||||
|
||||
// FIXME/bug 831898: hack r5g6b5 for now.
|
||||
if (!imgsurf || imgsurf->Format() == gfxImageFormat::RGB16_565) {
|
||||
size = GetSize();
|
||||
if (size.width == -1 && size.height == -1) {
|
||||
printf("Could not determine surface size\n");
|
||||
return;
|
||||
}
|
||||
|
||||
imgsurf =
|
||||
new gfxImageSurface(nsIntSize(size.width, size.height),
|
||||
gfxImageFormat::ARGB32);
|
||||
|
||||
if (!imgsurf || imgsurf->CairoStatus()) {
|
||||
printf("Could not allocate image surface\n");
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
|
||||
if (!ctx || ctx->HasError()) {
|
||||
printf("Could not allocate image context\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
ctx->SetSource(this, gfxPoint(0, 0));
|
||||
ctx->Paint();
|
||||
}
|
||||
size = imgsurf->GetSize();
|
||||
|
||||
nsCOMPtr<imgIEncoder> encoder =
|
||||
do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png");
|
||||
if (!encoder) {
|
||||
int32_t w = std::min(size.width, 8);
|
||||
int32_t h = std::min(size.height, 8);
|
||||
printf("Could not create encoder. Printing %dx%d pixels.\n", w, h);
|
||||
for (int32_t y = 0; y < h; ++y) {
|
||||
for (int32_t x = 0; x < w; ++x) {
|
||||
printf("%x ", reinterpret_cast<uint32_t*>(imgsurf->Data())[y*imgsurf->Stride()+ x]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = encoder->InitFromData(imgsurf->Data(),
|
||||
size.width * size.height * 4,
|
||||
size.width,
|
||||
size.height,
|
||||
imgsurf->Stride(),
|
||||
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||
NS_LITERAL_STRING(""));
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIInputStream> imgStream;
|
||||
CallQueryInterface(encoder.get(), getter_AddRefs(imgStream));
|
||||
if (!imgStream)
|
||||
return;
|
||||
|
||||
uint64_t bufSize64;
|
||||
rv = imgStream->Available(&bufSize64);
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
if (bufSize64 > UINT32_MAX - 16)
|
||||
return;
|
||||
|
||||
uint32_t bufSize = (uint32_t)bufSize64;
|
||||
|
||||
// ...leave a little extra room so we can call read again and make sure we
|
||||
// got everything. 16 bytes for better padding (maybe)
|
||||
bufSize += 16;
|
||||
uint32_t imgSize = 0;
|
||||
char* imgData = (char*)moz_malloc(bufSize);
|
||||
if (!imgData)
|
||||
return;
|
||||
uint32_t numReadThisTime = 0;
|
||||
while ((rv = imgStream->Read(&imgData[imgSize],
|
||||
bufSize - imgSize,
|
||||
&numReadThisTime)) == NS_OK && numReadThisTime > 0)
|
||||
{
|
||||
imgSize += numReadThisTime;
|
||||
if (imgSize == bufSize) {
|
||||
// need a bigger buffer, just double
|
||||
bufSize *= 2;
|
||||
char* newImgData = (char*)moz_realloc(imgData, bufSize);
|
||||
if (!newImgData) {
|
||||
moz_free(imgData);
|
||||
return;
|
||||
}
|
||||
imgData = newImgData;
|
||||
}
|
||||
}
|
||||
|
||||
if (aBinary) {
|
||||
if (aFile) {
|
||||
fwrite(imgData, 1, imgSize, aFile);
|
||||
} else {
|
||||
NS_WARNING("Can't write binary image data without a file!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// base 64, result will be null-terminated
|
||||
nsCString encodedImg;
|
||||
rv = Base64Encode(Substring(imgData, imgSize), encodedImg);
|
||||
moz_free(imgData);
|
||||
if (NS_FAILED(rv)) // not sure why this would fail
|
||||
return;
|
||||
|
||||
nsCString string("data:image/png;base64,");
|
||||
string.Append(encodedImg);
|
||||
|
||||
if (aFile) {
|
||||
#ifdef ANDROID
|
||||
if (aFile == stdout || aFile == stderr) {
|
||||
// ADB logcat cuts off long strings so we will break it down
|
||||
const char* cStr = string.BeginReading();
|
||||
size_t len = strlen(cStr);
|
||||
while (true) {
|
||||
printf_stderr("IMG: %.140s\n", cStr);
|
||||
if (len <= 140)
|
||||
break;
|
||||
len -= 140;
|
||||
cStr += 140;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
fprintf_stderr(aFile, "%s", string.BeginReading());
|
||||
} else {
|
||||
nsCOMPtr<nsIClipboardHelper> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
|
||||
if (clipboard) {
|
||||
clipboard->CopyString(NS_ConvertASCIItoUTF16(string), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
gfxASurface::SetOpaqueRect(const gfxRect& aRect)
|
||||
{
|
||||
|
|
|
@ -175,27 +175,6 @@ public:
|
|||
|
||||
virtual const nsIntSize GetSize() const;
|
||||
|
||||
/**
|
||||
* Debug functions to encode the current image as a PNG and export it.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Writes a binary PNG file.
|
||||
*/
|
||||
void WriteAsPNG(const char* aFile);
|
||||
|
||||
/**
|
||||
* Write as a PNG encoded Data URL to a file.
|
||||
*/
|
||||
void DumpAsDataURL(FILE* aOutput = stdout);
|
||||
|
||||
/**
|
||||
* Copy a PNG encoded Data URL to the clipboard.
|
||||
*/
|
||||
void CopyAsDataURL();
|
||||
|
||||
void WriteAsPNG_internal(FILE* aFile, bool aBinary);
|
||||
|
||||
void SetOpaqueRect(const gfxRect& aRect);
|
||||
|
||||
const gfxRect& GetOpaqueRect() {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "gfxColor.h"
|
||||
#include "gfxMatrix.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "gfxASurface.h"
|
||||
#include "gfxPattern.h"
|
||||
#include "gfxPlatform.h"
|
||||
|
@ -1958,33 +1959,30 @@ gfxContext::RoundedRectangle(const gfxRect& rect,
|
|||
void
|
||||
gfxContext::WriteAsPNG(const char* aFile)
|
||||
{
|
||||
nsRefPtr<gfxASurface> surf = CurrentSurface();
|
||||
if (surf) {
|
||||
surf->WriteAsPNG(aFile);
|
||||
if (mDT) {
|
||||
gfxUtils::WriteAsPNG(mDT, aFile);
|
||||
} else {
|
||||
NS_WARNING("No surface found!");
|
||||
NS_WARNING("No DrawTarget found!");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxContext::DumpAsDataURL()
|
||||
gfxContext::DumpAsDataURI()
|
||||
{
|
||||
nsRefPtr<gfxASurface> surf = CurrentSurface();
|
||||
if (surf) {
|
||||
surf->DumpAsDataURL();
|
||||
if (mDT) {
|
||||
gfxUtils::DumpAsDataURI(mDT);
|
||||
} else {
|
||||
NS_WARNING("No surface found!");
|
||||
NS_WARNING("No DrawTarget found!");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxContext::CopyAsDataURL()
|
||||
gfxContext::CopyAsDataURI()
|
||||
{
|
||||
nsRefPtr<gfxASurface> surf = CurrentSurface();
|
||||
if (surf) {
|
||||
surf->CopyAsDataURL();
|
||||
if (mDT) {
|
||||
gfxUtils::CopyAsDataURI(mDT);
|
||||
} else {
|
||||
NS_WARNING("No surface found!");
|
||||
NS_WARNING("No DrawTarget found!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -707,12 +707,12 @@ public:
|
|||
/**
|
||||
* Write as a PNG encoded Data URL to stdout.
|
||||
*/
|
||||
void DumpAsDataURL();
|
||||
void DumpAsDataURI();
|
||||
|
||||
/**
|
||||
* Copy a PNG encoded Data URL to the clipboard.
|
||||
*/
|
||||
void CopyAsDataURL();
|
||||
void CopyAsDataURI();
|
||||
#endif
|
||||
|
||||
static mozilla::gfx::UserDataKey sDontUseAsSourceKey;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "gfxAndroidPlatform.h"
|
||||
#include "mozilla/Omnijar.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsNetUtil.h"
|
||||
#define gfxToolkitPlatform gfxAndroidPlatform
|
||||
|
@ -1084,7 +1085,8 @@ gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
|
|||
uint32_t bufSize = item->RealSize();
|
||||
// We use fallible allocation here; if there's not enough RAM, we'll simply
|
||||
// ignore the bundled fonts and fall back to the device's installed fonts.
|
||||
nsAutoPtr<uint8_t> buf(static_cast<uint8_t*>(moz_malloc(bufSize)));
|
||||
static const fallible_t fallible = fallible_t();
|
||||
nsAutoArrayPtr<uint8_t> buf(new (fallible) uint8_t[bufSize]);
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -876,17 +876,33 @@ gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
IgnorePrincipal(nsIURI *aURI)
|
||||
{
|
||||
nsresult rv;
|
||||
bool inherits = false;
|
||||
rv = NS_URIChainHasFlags(aURI,
|
||||
nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
|
||||
&inherits);
|
||||
return NS_SUCCEEDED(rv) && inherits;
|
||||
}
|
||||
|
||||
bool
|
||||
gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
|
||||
{
|
||||
bool equal;
|
||||
if (NS_FAILED(mURI->Equals(aKey->mURI, &equal)) || !equal) {
|
||||
bool result;
|
||||
if (NS_FAILED(mURI->Equals(aKey->mURI, &result)) || !result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NS_FAILED(mPrincipal->Equals(aKey->mPrincipal, &equal)) || !equal) {
|
||||
// For data: URIs, we don't care about the principal; otherwise, check it.
|
||||
if (!IgnorePrincipal(mURI)) {
|
||||
NS_ASSERTION(mPrincipal && aKey->mPrincipal,
|
||||
"only data: URIs are allowed to omit the principal");
|
||||
if (NS_FAILED(mPrincipal->Equals(aKey->mPrincipal, &result)) || !result) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mPrivate != aKey->mPrivate) {
|
||||
return false;
|
||||
|
@ -925,7 +941,16 @@ gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry)
|
|||
}
|
||||
|
||||
gfxUserFontData *data = aFontEntry->mUserFontData;
|
||||
sUserFonts->PutEntry(Key(data->mURI, data->mPrincipal, aFontEntry,
|
||||
// For data: URIs, the principal is ignored; anyone who has the same
|
||||
// data: URI is able to load it and get an equivalent font.
|
||||
// Otherwise, the principal is used as part of the cache key.
|
||||
nsIPrincipal *principal;
|
||||
if (IgnorePrincipal(data->mURI)) {
|
||||
principal = nullptr;
|
||||
} else {
|
||||
principal = data->mPrincipal;
|
||||
}
|
||||
sUserFonts->PutEntry(Key(data->mURI, principal, aFontEntry,
|
||||
data->mPrivate));
|
||||
|
||||
#ifdef DEBUG_USERFONT_CACHE
|
||||
|
@ -965,7 +990,15 @@ gfxUserFontSet::UserFontCache::GetFont(nsIURI *aSrcURI,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, aPrincipal, aProxy,
|
||||
// Ignore principal when looking up a data: URI.
|
||||
nsIPrincipal *principal;
|
||||
if (IgnorePrincipal(aSrcURI)) {
|
||||
principal = nullptr;
|
||||
} else {
|
||||
principal = aPrincipal;
|
||||
}
|
||||
|
||||
Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, principal, aProxy,
|
||||
aPrivate));
|
||||
if (entry) {
|
||||
return entry->GetFontEntry();
|
||||
|
@ -990,21 +1023,22 @@ gfxUserFontSet::UserFontCache::Entry::DumpEntry(Entry* aEntry, void* aUserData)
|
|||
{
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString principalURISpec;
|
||||
nsAutoCString principalURISpec("(null)");
|
||||
bool setDomain = false;
|
||||
|
||||
if (aEntry->mPrincipal) {
|
||||
nsCOMPtr<nsIURI> principalURI;
|
||||
rv = aEntry->mPrincipal->GetURI(getter_AddRefs(principalURI));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
principalURI->GetSpec(principalURISpec);
|
||||
}
|
||||
|
||||
bool setDomain = false;
|
||||
nsCOMPtr<nsIURI> domainURI;
|
||||
|
||||
aEntry->mPrincipal->GetDomain(getter_AddRefs(domainURI));
|
||||
if (domainURI) {
|
||||
setDomain = true;
|
||||
}
|
||||
}
|
||||
|
||||
NS_ASSERTION(aEntry->mURI, "null URI in userfont cache entry");
|
||||
|
||||
|
|
|
@ -294,7 +294,7 @@ public:
|
|||
// entry and the corresponding "real" font entry.
|
||||
struct Key {
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal; // use nullptr with data: URLs
|
||||
gfxFontEntry *mFontEntry;
|
||||
bool mPrivate;
|
||||
|
||||
|
@ -333,8 +333,10 @@ public:
|
|||
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
|
||||
|
||||
static PLDHashNumber HashKey(const KeyTypePointer aKey) {
|
||||
uint32_t principalHash;
|
||||
uint32_t principalHash = 0;
|
||||
if (aKey->mPrincipal) {
|
||||
aKey->mPrincipal->GetHashValue(&principalHash);
|
||||
}
|
||||
return mozilla::HashGeneric(principalHash + int(aKey->mPrivate),
|
||||
nsURIHashKey::HashKey(aKey->mURI),
|
||||
HashFeatures(aKey->mFontEntry->mFeatureSettings),
|
||||
|
@ -365,7 +367,7 @@ public:
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal; // or nullptr for data: URLs
|
||||
|
||||
// The "real" font entry corresponding to this downloaded font.
|
||||
// The font entry MUST notify the cache when it is destroyed
|
||||
|
|
|
@ -10,9 +10,19 @@
|
|||
#include "gfxImageSurface.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "gfxDrawable.h"
|
||||
#include "imgIEncoder.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/DataSurfaceHelpers.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIClipboardHelper.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsRegion.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "yuv_convert.h"
|
||||
#include "ycbcr_to_rgb565.h"
|
||||
#include "GeckoProfiler.h"
|
||||
|
@ -1105,83 +1115,258 @@ gfxUtils::GetColorForFrameNumber(uint64_t aFrameNumber)
|
|||
return colors[aFrameNumber % sNumFrameColors];
|
||||
}
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
/* static */ nsresult
|
||||
gfxUtils::EncodeSourceSurface(SourceSurface* aSurface,
|
||||
const nsACString& aMimeType,
|
||||
const nsAString& aOutputOptions,
|
||||
BinaryOrData aBinaryOrData,
|
||||
FILE* aFile)
|
||||
{
|
||||
MOZ_ASSERT(aBinaryOrData == eDataURIEncode || aFile,
|
||||
"Copying binary encoding to clipboard not currently supported");
|
||||
|
||||
const IntSize size = aSurface->GetSize();
|
||||
if (size.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
const Size floatSize(size.width, size.height);
|
||||
|
||||
RefPtr<DataSourceSurface> dataSurface;
|
||||
if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
|
||||
// FIXME bug 995807 (B8G8R8X8), bug 831898 (R5G6B5)
|
||||
dataSurface =
|
||||
CopySurfaceToDataSourceSurfaceWithFormat(aSurface,
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
} else {
|
||||
dataSurface = aSurface->GetDataSurface();
|
||||
}
|
||||
if (!dataSurface) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoCString encoderCID(
|
||||
NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
|
||||
if (!encoder) {
|
||||
#ifdef DEBUG
|
||||
int32_t w = std::min(size.width, 8);
|
||||
int32_t h = std::min(size.height, 8);
|
||||
printf("Could not create encoder. Top-left %dx%d pixels contain:\n", w, h);
|
||||
for (int32_t y = 0; y < h; ++y) {
|
||||
for (int32_t x = 0; x < w; ++x) {
|
||||
printf("%x ", reinterpret_cast<uint32_t*>(map.mData)[y*map.mStride+x]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
dataSurface->Unmap();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv = encoder->InitFromData(map.mData,
|
||||
BufferSizeFromStrideAndHeight(map.mStride, size.height),
|
||||
size.width,
|
||||
size.height,
|
||||
map.mStride,
|
||||
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||
aOutputOptions);
|
||||
dataSurface->Unmap();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIInputStream> imgStream;
|
||||
CallQueryInterface(encoder.get(), getter_AddRefs(imgStream));
|
||||
if (!imgStream) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
uint64_t bufSize64;
|
||||
rv = imgStream->Available(&bufSize64);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_TRUE(bufSize64 < UINT32_MAX - 16, NS_ERROR_FAILURE);
|
||||
|
||||
uint32_t bufSize = (uint32_t)bufSize64;
|
||||
|
||||
// ...leave a little extra room so we can call read again and make sure we
|
||||
// got everything. 16 bytes for better padding (maybe)
|
||||
bufSize += 16;
|
||||
uint32_t imgSize = 0;
|
||||
Vector<char> imgData;
|
||||
if (!imgData.initCapacity(bufSize)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
uint32_t numReadThisTime = 0;
|
||||
while ((rv = imgStream->Read(imgData.begin() + imgSize,
|
||||
bufSize - imgSize,
|
||||
&numReadThisTime)) == NS_OK && numReadThisTime > 0)
|
||||
{
|
||||
imgSize += numReadThisTime;
|
||||
if (imgSize == bufSize) {
|
||||
// need a bigger buffer, just double
|
||||
bufSize *= 2;
|
||||
if (!imgData.resizeUninitialized(bufSize)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aBinaryOrData == eBinaryEncode) {
|
||||
if (aFile) {
|
||||
fwrite(imgData.begin(), 1, imgSize, aFile);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// base 64, result will be null-terminated
|
||||
nsCString encodedImg;
|
||||
rv = Base64Encode(Substring(imgData.begin(), imgSize), encodedImg);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString string("data:");
|
||||
string.Append(aMimeType);
|
||||
string.Append(";base64,");
|
||||
string.Append(encodedImg);
|
||||
|
||||
if (aFile) {
|
||||
#ifdef ANDROID
|
||||
if (aFile == stdout || aFile == stderr) {
|
||||
// ADB logcat cuts off long strings so we will break it down
|
||||
const char* cStr = string.BeginReading();
|
||||
size_t len = strlen(cStr);
|
||||
while (true) {
|
||||
printf_stderr("IMG: %.140s\n", cStr);
|
||||
if (len <= 140)
|
||||
break;
|
||||
len -= 140;
|
||||
cStr += 140;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
fprintf(aFile, "%s", string.BeginReading());
|
||||
} else {
|
||||
nsCOMPtr<nsIClipboardHelper> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
|
||||
if (clipboard) {
|
||||
clipboard->CopyString(NS_ConvertASCIItoUTF16(string), nullptr);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile)
|
||||
{
|
||||
WriteAsPNG(aSurface, NS_ConvertUTF16toUTF8(aFile).get());
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::WriteAsPNG(SourceSurface* aSurface, const char* aFile)
|
||||
{
|
||||
FILE* file = fopen(aFile, "wb");
|
||||
|
||||
if (!file) {
|
||||
// Maybe the directory doesn't exist; try creating it, then fopen again.
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
nsCOMPtr<nsIFile> comFile = do_CreateInstance("@mozilla.org/file/local;1");
|
||||
if (comFile) {
|
||||
NS_ConvertUTF8toUTF16 utf16path((nsDependentCString(aFile)));
|
||||
rv = comFile->InitWithPath(utf16path);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsCOMPtr<nsIFile> dirPath;
|
||||
comFile->GetParent(getter_AddRefs(dirPath));
|
||||
if (dirPath) {
|
||||
rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
|
||||
if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) {
|
||||
file = fopen(aFile, "wb");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!file) {
|
||||
NS_WARNING("Failed to open file to create PNG!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
|
||||
EmptyString(), eBinaryEncode, file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::WriteAsPNG(DrawTarget* aDT, const nsAString& aFile)
|
||||
{
|
||||
WriteAsPNG(aDT, NS_ConvertUTF16toUTF8(aFile).get());
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile)
|
||||
{
|
||||
aDT->Flush();
|
||||
nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT);
|
||||
if (surf) {
|
||||
surf->WriteAsPNG(aFile);
|
||||
RefPtr<SourceSurface> surface = aDT->Snapshot();
|
||||
if (surface) {
|
||||
WriteAsPNG(surface, aFile);
|
||||
} else {
|
||||
NS_WARNING("Failed to get Thebes surface!");
|
||||
NS_WARNING("Failed to get surface!");
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::DumpAsDataURL(DrawTarget* aDT)
|
||||
gfxUtils::WriteAsPNG(nsIPresShell* aShell, const char* aFile)
|
||||
{
|
||||
aDT->Flush();
|
||||
nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT);
|
||||
if (surf) {
|
||||
surf->DumpAsDataURL();
|
||||
int32_t width = 1000, height = 1000;
|
||||
nsRect r(0, 0, aShell->GetPresContext()->DevPixelsToAppUnits(width),
|
||||
aShell->GetPresContext()->DevPixelsToAppUnits(height));
|
||||
|
||||
RefPtr<mozilla::gfx::DrawTarget> dt = gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenContentDrawTarget(IntSize(width, height),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
NS_ENSURE_TRUE(dt, /*void*/);
|
||||
|
||||
nsRefPtr<gfxContext> context = new gfxContext(dt);
|
||||
aShell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
|
||||
WriteAsPNG(dt.get(), aFile);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::DumpAsDataURI(SourceSurface* aSurface, FILE* aFile)
|
||||
{
|
||||
EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
|
||||
EmptyString(), eDataURIEncode, aFile);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::DumpAsDataURI(DrawTarget* aDT, FILE* aFile)
|
||||
{
|
||||
RefPtr<SourceSurface> surface = aDT->Snapshot();
|
||||
if (surface) {
|
||||
DumpAsDataURI(surface, aFile);
|
||||
} else {
|
||||
NS_WARNING("Failed to get Thebes surface!");
|
||||
NS_WARNING("Failed to get surface!");
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::CopyAsDataURL(DrawTarget* aDT)
|
||||
gfxUtils::CopyAsDataURI(SourceSurface* aSurface)
|
||||
{
|
||||
aDT->Flush();
|
||||
nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT);
|
||||
if (surf) {
|
||||
surf->CopyAsDataURL();
|
||||
EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
|
||||
EmptyString(), eDataURIEncode, nullptr);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::CopyAsDataURI(DrawTarget* aDT)
|
||||
{
|
||||
RefPtr<SourceSurface> surface = aDT->Snapshot();
|
||||
if (surface) {
|
||||
CopyAsDataURI(surface);
|
||||
} else {
|
||||
NS_WARNING("Failed to get Thebes surface!");
|
||||
NS_WARNING("Failed to get surface!");
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::WriteAsPNG(gfx::SourceSurface* aSourceSurface, const char* aFile)
|
||||
{
|
||||
RefPtr<gfx::DataSourceSurface> dataSurface = aSourceSurface->GetDataSurface();
|
||||
RefPtr<gfx::DrawTarget> dt
|
||||
= gfxPlatform::GetPlatform()
|
||||
->CreateDrawTargetForData(dataSurface->GetData(),
|
||||
dataSurface->GetSize(),
|
||||
dataSurface->Stride(),
|
||||
aSourceSurface->GetFormat());
|
||||
gfxUtils::WriteAsPNG(dt.get(), aFile);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::DumpAsDataURL(gfx::SourceSurface* aSourceSurface)
|
||||
{
|
||||
RefPtr<gfx::DataSourceSurface> dataSurface = aSourceSurface->GetDataSurface();
|
||||
RefPtr<gfx::DrawTarget> dt
|
||||
= gfxPlatform::GetPlatform()
|
||||
->CreateDrawTargetForData(dataSurface->GetData(),
|
||||
dataSurface->GetSize(),
|
||||
dataSurface->Stride(),
|
||||
aSourceSurface->GetFormat());
|
||||
gfxUtils::DumpAsDataURL(dt.get());
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::CopyAsDataURL(gfx::SourceSurface* aSourceSurface)
|
||||
{
|
||||
RefPtr<gfx::DataSourceSurface> dataSurface = aSourceSurface->GetDataSurface();
|
||||
RefPtr<gfx::DrawTarget> dt
|
||||
= gfxPlatform::GetPlatform()
|
||||
->CreateDrawTargetForData(dataSurface->GetData(),
|
||||
dataSurface->GetSize(),
|
||||
dataSurface->Stride(),
|
||||
aSourceSurface->GetFormat());
|
||||
|
||||
gfxUtils::CopyAsDataURL(dt.get());
|
||||
}
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
static bool sDumpPaintList = getenv("MOZ_DUMP_PAINT_LIST") != 0;
|
||||
|
||||
/* static */ bool
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
class gfxASurface;
|
||||
class gfxDrawable;
|
||||
class nsIntRegion;
|
||||
class nsIPresShell;
|
||||
|
||||
struct nsIntRect;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -27,6 +29,7 @@ struct PlanarYCbCrData;
|
|||
class gfxUtils {
|
||||
public:
|
||||
typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
typedef mozilla::gfx::IntPoint IntPoint;
|
||||
typedef mozilla::gfx::Matrix Matrix;
|
||||
typedef mozilla::gfx::SourceSurface SourceSurface;
|
||||
|
@ -228,45 +231,78 @@ public:
|
|||
static const mozilla::gfx::Color& GetColorForFrameNumber(uint64_t aFrameNumber);
|
||||
static const uint32_t sNumFrameColors;
|
||||
|
||||
|
||||
enum BinaryOrData {
|
||||
eBinaryEncode,
|
||||
eDataURIEncode
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes the given surface to PNG/JPEG/BMP/etc. using imgIEncoder.
|
||||
*
|
||||
* @param aMimeType The MIME-type of the image type that the surface is to
|
||||
* be encoded to. Used to create an appropriate imgIEncoder instance to
|
||||
* do the encoding.
|
||||
*
|
||||
* @param aOutputOptions Passed directly to imgIEncoder::InitFromData as
|
||||
* the value of the |outputOptions| parameter. Callers are responsible
|
||||
* for making sure that this is a sane value for the passed MIME-type
|
||||
* (i.e. for the type of encoder that will be created).
|
||||
*
|
||||
* @aBinaryOrData Flag used to determine if the surface is simply encoded
|
||||
* to the requested binary image format, or if the binary image is
|
||||
* further converted to base-64 and written out as a 'data:' URI.
|
||||
*
|
||||
* @aFile If specified, the encoded data is written out to aFile, otherwise
|
||||
* it is copied to the clipboard.
|
||||
*
|
||||
* TODO: Copying to the clipboard as a binary file is not currently
|
||||
* supported.
|
||||
*/
|
||||
static nsresult
|
||||
EncodeSourceSurface(SourceSurface* aSurface,
|
||||
const nsACString& aMimeType,
|
||||
const nsAString& aOutputOptions,
|
||||
BinaryOrData aBinaryOrData,
|
||||
FILE* aFile);
|
||||
|
||||
/**
|
||||
* Write as a PNG file to the path aFile.
|
||||
*/
|
||||
static void WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile);
|
||||
static void WriteAsPNG(SourceSurface* aSurface, const char* aFile);
|
||||
static void WriteAsPNG(DrawTarget* aDT, const nsAString& aFile);
|
||||
static void WriteAsPNG(DrawTarget* aDT, const char* aFile);
|
||||
static void WriteAsPNG(nsIPresShell* aShell, const char* aFile);
|
||||
|
||||
/**
|
||||
* Dump as a PNG encoded Data URL to a FILE stream (using stdout by
|
||||
* default).
|
||||
*
|
||||
* Rather than giving aFile a default argument we have separate functions
|
||||
* to make them easier to use from a debugger.
|
||||
*/
|
||||
static void DumpAsDataURI(SourceSurface* aSourceSurface, FILE* aFile);
|
||||
static inline void DumpAsDataURI(SourceSurface* aSourceSurface) {
|
||||
DumpAsDataURI(aSourceSurface, stdout);
|
||||
}
|
||||
static void DumpAsDataURI(DrawTarget* aDT, FILE* aFile);
|
||||
static inline void DumpAsDataURI(DrawTarget* aDT) {
|
||||
DumpAsDataURI(aDT, stdout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy to the clipboard as a PNG encoded Data URL.
|
||||
*/
|
||||
static void CopyAsDataURI(SourceSurface* aSourceSurface);
|
||||
static void CopyAsDataURI(DrawTarget* aDT);
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
/**
|
||||
* Writes a binary PNG file.
|
||||
*/
|
||||
static void WriteAsPNG(mozilla::gfx::DrawTarget* aDT, const char* aFile);
|
||||
|
||||
/**
|
||||
* Write as a PNG encoded Data URL to stdout.
|
||||
*/
|
||||
static void DumpAsDataURL(mozilla::gfx::DrawTarget* aDT);
|
||||
|
||||
/**
|
||||
* Copy a PNG encoded Data URL to the clipboard.
|
||||
*/
|
||||
static void CopyAsDataURL(mozilla::gfx::DrawTarget* aDT);
|
||||
|
||||
static bool DumpPaintList();
|
||||
|
||||
static bool sDumpPainting;
|
||||
static bool sDumpPaintingToFile;
|
||||
static FILE* sDumpPaintFile;
|
||||
|
||||
/**
|
||||
* Writes a binary PNG file.
|
||||
* Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads
|
||||
*/
|
||||
static void WriteAsPNG(mozilla::gfx::SourceSurface* aSourceSurface, const char* aFile);
|
||||
|
||||
/**
|
||||
* Write as a PNG encoded Data URL to stdout.
|
||||
* Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads
|
||||
*/
|
||||
static void DumpAsDataURL(mozilla::gfx::SourceSurface* aSourceSurface);
|
||||
|
||||
/**
|
||||
* Copy a PNG encoded Data URL to the clipboard.
|
||||
* Expensive. Creates a DataSourceSurface, then a DrawTarget, then passes to DrawTarget overloads
|
||||
*/
|
||||
static void CopyAsDataURL(mozilla::gfx::SourceSurface* aSourceSurface);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
js/jsd contains code for debugging support for the C-based JavaScript engine
|
||||
in js/src. jsd_xpc.cpp provides an XPCOM binding for the library.
|
||||
|
||||
js/jsd/jsdb is a console debugger using only native code (see README in that
|
||||
directory.) This debugger is no longer being actively developed, though it
|
||||
should work.
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,12 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'jsdIDebuggerService.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'jsdservice'
|
||||
|
1054
js/jsd/jsd.h
1054
js/jsd/jsd.h
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,77 +0,0 @@
|
|||
; -*- Mode: Fundamental; tab-width: 4; indent-tabs-mode: nil -*-
|
||||
;
|
||||
; 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/.
|
||||
|
||||
|
||||
LIBRARY JSD1640.DLL
|
||||
EXETYPE WINDOWS
|
||||
PROTMODE
|
||||
|
||||
DESCRIPTION 'Netscape 16-bit JavaScript Debugger Library'
|
||||
|
||||
CODE LOADONCALL MOVEABLE DISCARDABLE
|
||||
DATA PRELOAD MOVEABLE SINGLE
|
||||
|
||||
HEAPSIZE 8192
|
||||
|
||||
EXPORTS
|
||||
WEP @1 RESIDENTNAME NONAME
|
||||
_JSD_AppendSourceText
|
||||
_JSD_ClearAllExecutionHooks
|
||||
_JSD_ClearAllExecutionHooksForScript
|
||||
_JSD_ClearDebugBreakHook
|
||||
_JSD_ClearExecutionHook
|
||||
_JSD_ClearInterruptHook
|
||||
_JSD_ClearSourceText
|
||||
_JSD_DebuggerOff
|
||||
_JSD_DebuggerOn
|
||||
_JSD_EvaluateScriptInStackFrame
|
||||
_JSD_FindSourceForURL
|
||||
_JSD_GetCallingStackFrame
|
||||
_JSD_GetClosestLine
|
||||
_JSD_GetClosestPC
|
||||
_JSD_GetCountOfStackFrames
|
||||
_JSD_GetMajorVersion
|
||||
_JSD_GetMinorVersion
|
||||
_JSD_GetPCForStackFrame
|
||||
_JSD_GetScriptBaseLineNumber
|
||||
_JSD_GetScriptFilename
|
||||
_JSD_GetScriptForStackFrame
|
||||
_JSD_GetScriptFunctionId
|
||||
_JSD_GetScriptHook
|
||||
_JSD_GetScriptLineExtent
|
||||
_JSD_GetSourceAlterCount
|
||||
_JSD_GetSourceStatus
|
||||
_JSD_GetSourceText
|
||||
_JSD_GetSourceURL
|
||||
_JSD_GetStackFrame
|
||||
_JSD_IncrementSourceAlterCount
|
||||
_JSD_IsSourceDirty
|
||||
_JSD_IterateScripts
|
||||
_JSD_IterateSources
|
||||
_JSD_LockScriptSubsystem
|
||||
_JSD_LockSourceTextSubsystem
|
||||
_JSD_NewSourceText
|
||||
_JSD_SetDebugBreakHook
|
||||
_JSD_SetErrorReporter
|
||||
_JSD_SetExecutionHook
|
||||
_JSD_SetInterruptHook
|
||||
_JSD_SetScriptHook
|
||||
_JSD_SetSourceDirty
|
||||
_JSD_SetUserCallbacks
|
||||
_JSD_UnlockScriptSubsystem
|
||||
_JSD_UnlockSourceTextSubsystem
|
||||
_Java_netscape_jsdebug_DebugController__0005fsetController_stub
|
||||
_Java_netscape_jsdebug_DebugController_executeScriptInStackFrame_stub
|
||||
_Java_netscape_jsdebug_DebugController_sendInterrupt_stub
|
||||
_Java_netscape_jsdebug_DebugController_setInstructionHook0_stub
|
||||
_Java_netscape_jsdebug_JSPC_getSourceLocation_stub
|
||||
_Java_netscape_jsdebug_JSSourceTextProvider_loadSourceTextItem_stub
|
||||
_Java_netscape_jsdebug_JSSourceTextProvider_refreshSourceTextVector_stub
|
||||
_Java_netscape_jsdebug_JSStackFrameInfo_getCaller0_stub
|
||||
_Java_netscape_jsdebug_JSStackFrameInfo_getPC_stub
|
||||
_Java_netscape_jsdebug_JSThreadState_countStackFrames_stub
|
||||
_Java_netscape_jsdebug_JSThreadState_getCurrentFrame_stub
|
||||
_Java_netscape_jsdebug_Script_getClosestPC_stub
|
|
@ -1,67 +0,0 @@
|
|||
/* -*- Mode: C; tab-width: 4; 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/. */
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Version stamp for this .DLL
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <ver.h>
|
||||
|
||||
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
|
||||
FILEVERSION 4 // major, minor, release (alpha 1), build #
|
||||
|
||||
PRODUCTVERSION 4
|
||||
|
||||
FILEFLAGSMASK 0
|
||||
|
||||
FILEFLAGS 0 // final version
|
||||
|
||||
FILEOS VOS_DOS_WINDOWS16
|
||||
|
||||
FILETYPE VFT_DLL
|
||||
|
||||
FILESUBTYPE 0 // not used
|
||||
|
||||
BEGIN
|
||||
|
||||
BLOCK "StringFileInfo"
|
||||
|
||||
BEGIN
|
||||
|
||||
BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual
|
||||
|
||||
BEGIN
|
||||
|
||||
VALUE "CompanyName", "Netscape Communications Corporation\0"
|
||||
|
||||
VALUE "FileDescription", "Netscape 16-bit JavaScript Debugger Module\0"
|
||||
|
||||
VALUE "FileVersion", "4.0\0"
|
||||
|
||||
VALUE "InternalName", "JSD1640\0"
|
||||
|
||||
VALUE "LegalCopyright", "Copyright Netscape Communications. 1994-96\0"
|
||||
|
||||
VALUE "LegalTrademarks", "Netscape, Mozilla\0"
|
||||
|
||||
VALUE "OriginalFilename","JSD1640.DLL\0"
|
||||
|
||||
VALUE "ProductName", "NETSCAPE\0"
|
||||
|
||||
VALUE "ProductVersion", "4.0\0"
|
||||
|
||||
END
|
||||
|
||||
END
|
||||
|
||||
END
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
/* -*- Mode: C; tab-width: 4; 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/. */
|
||||
|
||||
|
||||
|
||||
//Microsoft Developer Studio generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winver.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,0,0,0
|
||||
PRODUCTVERSION 4,0,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x10004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904e4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Netscape Communications Corporation\0"
|
||||
VALUE "FileDescription", "Netscape 32-bit JavaScript Debugger Module\0"
|
||||
VALUE "FileVersion", "4.0\0"
|
||||
VALUE "InternalName", "JSD3240\0"
|
||||
VALUE "LegalCopyright", "Copyright Netscape Communications. 1994-96\0"
|
||||
VALUE "LegalTrademarks", "Netscape, Mozilla\0"
|
||||
VALUE "OriginalFilename", "jsd3240.dll\0"
|
||||
VALUE "ProductName", "NETSCAPE\0"
|
||||
VALUE "ProductVersion", "4.0\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"#include ""winver.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
|
@ -1,147 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* JavaScript Debugging support - Atom support
|
||||
*/
|
||||
|
||||
#include "jsd.h"
|
||||
|
||||
/* #define TEST_ATOMS 1 */
|
||||
|
||||
#ifdef TEST_ATOMS
|
||||
static void
|
||||
_testAtoms(JSDContext*jsdc)
|
||||
{
|
||||
JSDAtom* atom0 = jsd_AddAtom(jsdc, "foo");
|
||||
JSDAtom* atom1 = jsd_AddAtom(jsdc, "foo");
|
||||
JSDAtom* atom2 = jsd_AddAtom(jsdc, "bar");
|
||||
JSDAtom* atom3 = jsd_CloneAtom(jsdc, atom1);
|
||||
JSDAtom* atom4 = jsd_CloneAtom(jsdc, atom2);
|
||||
|
||||
const char* c0 = JSD_ATOM_TO_STRING(atom0);
|
||||
const char* c1 = JSD_ATOM_TO_STRING(atom1);
|
||||
const char* c2 = JSD_ATOM_TO_STRING(atom2);
|
||||
const char* c3 = JSD_ATOM_TO_STRING(atom3);
|
||||
const char* c4 = JSD_ATOM_TO_STRING(atom4);
|
||||
|
||||
jsd_DropAtom(jsdc, atom0);
|
||||
jsd_DropAtom(jsdc, atom1);
|
||||
jsd_DropAtom(jsdc, atom2);
|
||||
jsd_DropAtom(jsdc, atom3);
|
||||
jsd_DropAtom(jsdc, atom4);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
_atom_smasher(JSHashEntry *he, int i, void *arg)
|
||||
{
|
||||
MOZ_ASSERT(he);
|
||||
MOZ_ASSERT(he->value);
|
||||
MOZ_ASSERT(((JSDAtom*)(he->value))->str);
|
||||
|
||||
free(((JSDAtom*)(he->value))->str);
|
||||
free(he->value);
|
||||
he->value = nullptr;
|
||||
he->key = nullptr;
|
||||
return HT_ENUMERATE_NEXT;
|
||||
}
|
||||
|
||||
static int
|
||||
_compareAtomKeys(const void *v1, const void *v2)
|
||||
{
|
||||
return 0 == strcmp((const char*)v1, (const char*)v2);
|
||||
}
|
||||
|
||||
static int
|
||||
_compareAtoms(const void *v1, const void *v2)
|
||||
{
|
||||
return 0 == strcmp(((JSDAtom*)v1)->str, ((JSDAtom*)v2)->str);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
jsd_CreateAtomTable(JSDContext* jsdc)
|
||||
{
|
||||
jsdc->atoms = JS_NewHashTable(256, JS_HashString,
|
||||
_compareAtomKeys, _compareAtoms,
|
||||
nullptr, nullptr);
|
||||
#ifdef TEST_ATOMS
|
||||
_testAtoms(jsdc);
|
||||
#endif
|
||||
return !!jsdc->atoms;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_DestroyAtomTable(JSDContext* jsdc)
|
||||
{
|
||||
if( jsdc->atoms )
|
||||
{
|
||||
JS_HashTableEnumerateEntries(jsdc->atoms, _atom_smasher, nullptr);
|
||||
JS_HashTableDestroy(jsdc->atoms);
|
||||
jsdc->atoms = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
JSDAtom*
|
||||
jsd_AddAtom(JSDContext* jsdc, const char* str)
|
||||
{
|
||||
JSDAtom* atom;
|
||||
|
||||
if(!str)
|
||||
{
|
||||
MOZ_ASSERT(0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSD_LOCK_ATOMS(jsdc);
|
||||
|
||||
atom = (JSDAtom*) JS_HashTableLookup(jsdc->atoms, str);
|
||||
|
||||
if( atom )
|
||||
atom->refcount++;
|
||||
else
|
||||
{
|
||||
atom = (JSDAtom*) malloc(sizeof(JSDAtom));
|
||||
if( atom )
|
||||
{
|
||||
atom->str = strdup(str);
|
||||
atom->refcount = 1;
|
||||
if(!JS_HashTableAdd(jsdc->atoms, atom->str, atom))
|
||||
{
|
||||
free(atom->str);
|
||||
free(atom);
|
||||
atom = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSD_UNLOCK_ATOMS(jsdc);
|
||||
return atom;
|
||||
}
|
||||
|
||||
JSDAtom*
|
||||
jsd_CloneAtom(JSDContext* jsdc, JSDAtom* atom)
|
||||
{
|
||||
JSD_LOCK_ATOMS(jsdc);
|
||||
atom->refcount++;
|
||||
JSD_UNLOCK_ATOMS(jsdc);
|
||||
return atom;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_DropAtom(JSDContext* jsdc, JSDAtom* atom)
|
||||
{
|
||||
JSD_LOCK_ATOMS(jsdc);
|
||||
if(! --atom->refcount)
|
||||
{
|
||||
JS_HashTableRemove(jsdc->atoms, atom->str);
|
||||
free(atom->str);
|
||||
free(atom);
|
||||
}
|
||||
JSD_UNLOCK_ATOMS(jsdc);
|
||||
}
|
||||
|
|
@ -1,415 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* JavaScript Debugging support - 'High Level' functions
|
||||
*/
|
||||
|
||||
#include "jsd.h"
|
||||
#include "nsCxPusher.h"
|
||||
|
||||
using mozilla::AutoSafeJSContext;
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/* XXX not 'static' because of old Mac CodeWarrior bug */
|
||||
JSCList _jsd_context_list = JS_INIT_STATIC_CLIST(&_jsd_context_list);
|
||||
|
||||
/* these are used to connect JSD_SetUserCallbacks() with JSD_DebuggerOn() */
|
||||
static JSD_UserCallbacks _callbacks;
|
||||
static void* _user = nullptr;
|
||||
static JSRuntime* _jsrt = nullptr;
|
||||
|
||||
#ifdef JSD_HAS_DANGEROUS_THREAD
|
||||
static void* _dangerousThread = nullptr;
|
||||
#endif
|
||||
|
||||
#ifdef JSD_THREADSAFE
|
||||
JSDStaticLock* _jsd_global_lock = nullptr;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
void JSD_ASSERT_VALID_CONTEXT(JSDContext* jsdc)
|
||||
{
|
||||
MOZ_ASSERT(jsdc->inited);
|
||||
MOZ_ASSERT(jsdc->jsrt);
|
||||
MOZ_ASSERT(jsdc->glob);
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************/
|
||||
/* xpconnect related utility functions implemented in jsd_xpc.cpp */
|
||||
|
||||
extern void
|
||||
global_finalize(JSFreeOp* fop, JSObject* obj);
|
||||
|
||||
extern JSObject*
|
||||
CreateJSDGlobal(JSContext *cx, const JSClass *clasp);
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
|
||||
static const JSClass global_class = {
|
||||
"JSDGlobal", JSCLASS_GLOBAL_FLAGS |
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS,
|
||||
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, global_finalize,
|
||||
nullptr, nullptr, nullptr,
|
||||
JS_GlobalObjectTraceHook
|
||||
};
|
||||
|
||||
static bool
|
||||
_validateUserCallbacks(JSD_UserCallbacks* callbacks)
|
||||
{
|
||||
return !callbacks ||
|
||||
(callbacks->size && callbacks->size <= sizeof(JSD_UserCallbacks));
|
||||
}
|
||||
|
||||
static JSDContext*
|
||||
_newJSDContext(JSRuntime* jsrt,
|
||||
JSD_UserCallbacks* callbacks,
|
||||
void* user,
|
||||
JSObject* scopeobj)
|
||||
{
|
||||
JSDContext* jsdc = nullptr;
|
||||
bool ok = true;
|
||||
AutoSafeJSContext cx;
|
||||
|
||||
if( ! jsrt )
|
||||
return nullptr;
|
||||
|
||||
if( ! _validateUserCallbacks(callbacks) )
|
||||
return nullptr;
|
||||
|
||||
jsdc = (JSDContext*) calloc(1, sizeof(JSDContext));
|
||||
if( ! jsdc )
|
||||
goto label_newJSDContext_failure;
|
||||
|
||||
if( ! JSD_INIT_LOCKS(jsdc) )
|
||||
goto label_newJSDContext_failure;
|
||||
|
||||
JS_INIT_CLIST(&jsdc->links);
|
||||
|
||||
jsdc->jsrt = jsrt;
|
||||
|
||||
if( callbacks )
|
||||
memcpy(&jsdc->userCallbacks, callbacks, callbacks->size);
|
||||
|
||||
jsdc->user = user;
|
||||
|
||||
#ifdef JSD_HAS_DANGEROUS_THREAD
|
||||
jsdc->dangerousThread = _dangerousThread;
|
||||
#endif
|
||||
|
||||
JS_INIT_CLIST(&jsdc->threadsStates);
|
||||
JS_INIT_CLIST(&jsdc->sources);
|
||||
JS_INIT_CLIST(&jsdc->removedSources);
|
||||
|
||||
jsdc->sourceAlterCount = 1;
|
||||
|
||||
if( ! jsd_CreateAtomTable(jsdc) )
|
||||
goto label_newJSDContext_failure;
|
||||
|
||||
if( ! jsd_InitObjectManager(jsdc) )
|
||||
goto label_newJSDContext_failure;
|
||||
|
||||
if( ! jsd_InitScriptManager(jsdc) )
|
||||
goto label_newJSDContext_failure;
|
||||
|
||||
{
|
||||
JS::RootedObject global(cx, CreateJSDGlobal(cx, &global_class));
|
||||
if( ! global )
|
||||
goto label_newJSDContext_failure;
|
||||
|
||||
jsdc->glob = global;
|
||||
|
||||
JSAutoCompartment ac(cx, jsdc->glob);
|
||||
ok = JS::AddNamedObjectRoot(cx, &jsdc->glob, "JSD context global") &&
|
||||
JS_InitStandardClasses(cx, global);
|
||||
}
|
||||
if( ! ok )
|
||||
goto label_newJSDContext_failure;
|
||||
|
||||
jsdc->data = nullptr;
|
||||
jsdc->inited = true;
|
||||
|
||||
JSD_LOCK();
|
||||
JS_INSERT_LINK(&jsdc->links, &_jsd_context_list);
|
||||
JSD_UNLOCK();
|
||||
|
||||
return jsdc;
|
||||
|
||||
label_newJSDContext_failure:
|
||||
if( jsdc ) {
|
||||
if ( jsdc->glob )
|
||||
JS::RemoveObjectRootRT(JS_GetRuntime(cx), &jsdc->glob);
|
||||
jsd_DestroyObjectManager(jsdc);
|
||||
jsd_DestroyAtomTable(jsdc);
|
||||
free(jsdc);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
_destroyJSDContext(JSDContext* jsdc)
|
||||
{
|
||||
JSD_ASSERT_VALID_CONTEXT(jsdc);
|
||||
|
||||
JSD_LOCK();
|
||||
JS_REMOVE_LINK(&jsdc->links);
|
||||
JSD_UNLOCK();
|
||||
|
||||
if ( jsdc->glob ) {
|
||||
JS::RemoveObjectRootRT(jsdc->jsrt, &jsdc->glob);
|
||||
}
|
||||
jsd_DestroyObjectManager(jsdc);
|
||||
jsd_DestroyAtomTable(jsdc);
|
||||
|
||||
jsdc->inited = false;
|
||||
|
||||
/*
|
||||
* We should free jsdc here, but we let it leak in case there are any
|
||||
* asynchronous hooks calling into the system using it as a handle
|
||||
*
|
||||
* XXX we also leak the locks
|
||||
*/
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
JSDContext*
|
||||
jsd_DebuggerOnForUser(JSRuntime* jsrt,
|
||||
JSD_UserCallbacks* callbacks,
|
||||
void* user,
|
||||
JSObject* scopeobj)
|
||||
{
|
||||
JSDContext* jsdc;
|
||||
|
||||
jsdc = _newJSDContext(jsrt, callbacks, user, scopeobj);
|
||||
if( ! jsdc )
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* Set hooks here. The new/destroy script hooks are on even when
|
||||
* the debugger is paused. The destroy hook so we'll clean up
|
||||
* internal data structures when scripts are destroyed, and the
|
||||
* newscript hook for backwards compatibility for now. We'd like
|
||||
* to stop doing that.
|
||||
*/
|
||||
JS_SetNewScriptHookProc(jsdc->jsrt, jsd_NewScriptHookProc, jsdc);
|
||||
JS_SetDestroyScriptHookProc(jsdc->jsrt, jsd_DestroyScriptHookProc, jsdc);
|
||||
jsd_DebuggerUnpause(jsdc);
|
||||
|
||||
if( jsdc->userCallbacks.setContext )
|
||||
jsdc->userCallbacks.setContext(jsdc, jsdc->user);
|
||||
return jsdc;
|
||||
}
|
||||
|
||||
JSDContext*
|
||||
jsd_DebuggerOn(void)
|
||||
{
|
||||
MOZ_ASSERT(_jsrt);
|
||||
MOZ_ASSERT(_validateUserCallbacks(&_callbacks));
|
||||
return jsd_DebuggerOnForUser(_jsrt, &_callbacks, _user, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
jsd_DebuggerOff(JSDContext* jsdc)
|
||||
{
|
||||
jsd_DebuggerPause(jsdc, true);
|
||||
/* clear hooks here */
|
||||
JS_SetNewScriptHookProc(jsdc->jsrt, nullptr, nullptr);
|
||||
JS_SetDestroyScriptHookProc(jsdc->jsrt, nullptr, nullptr);
|
||||
|
||||
/* clean up */
|
||||
JSD_LockScriptSubsystem(jsdc);
|
||||
jsd_DestroyScriptManager(jsdc);
|
||||
JSD_UnlockScriptSubsystem(jsdc);
|
||||
jsd_DestroyAllSources(jsdc);
|
||||
|
||||
_destroyJSDContext(jsdc);
|
||||
|
||||
if( jsdc->userCallbacks.setContext )
|
||||
jsdc->userCallbacks.setContext(nullptr, jsdc->user);
|
||||
}
|
||||
|
||||
void
|
||||
jsd_DebuggerPause(JSDContext* jsdc, bool forceAllHooksOff)
|
||||
{
|
||||
JS_SetDebuggerHandler(jsdc->jsrt, nullptr, nullptr);
|
||||
if (forceAllHooksOff || !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) {
|
||||
JS_SetExecuteHook(jsdc->jsrt, nullptr, nullptr);
|
||||
JS_SetCallHook(jsdc->jsrt, nullptr, nullptr);
|
||||
}
|
||||
JS_SetThrowHook(jsdc->jsrt, nullptr, nullptr);
|
||||
JS_SetDebugErrorHook(jsdc->jsrt, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static bool
|
||||
jsd_DebugErrorHook(JSContext *cx, const char *message,
|
||||
JSErrorReport *report, void *closure);
|
||||
|
||||
void
|
||||
jsd_DebuggerUnpause(JSDContext* jsdc)
|
||||
{
|
||||
JS_SetDebuggerHandler(jsdc->jsrt, jsd_DebuggerHandler, jsdc);
|
||||
JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc);
|
||||
JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc);
|
||||
JS_SetThrowHook(jsdc->jsrt, jsd_ThrowHandler, jsdc);
|
||||
JS_SetDebugErrorHook(jsdc->jsrt, jsd_DebugErrorHook, jsdc);
|
||||
}
|
||||
|
||||
void
|
||||
jsd_SetUserCallbacks(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, void* user)
|
||||
{
|
||||
_jsrt = jsrt;
|
||||
_user = user;
|
||||
|
||||
#ifdef JSD_HAS_DANGEROUS_THREAD
|
||||
_dangerousThread = JSD_CURRENT_THREAD();
|
||||
#endif
|
||||
|
||||
if( callbacks )
|
||||
memcpy(&_callbacks, callbacks, sizeof(JSD_UserCallbacks));
|
||||
else
|
||||
memset(&_callbacks, 0 , sizeof(JSD_UserCallbacks));
|
||||
}
|
||||
|
||||
void*
|
||||
jsd_SetContextPrivate(JSDContext* jsdc, void *data)
|
||||
{
|
||||
jsdc->data = data;
|
||||
return data;
|
||||
}
|
||||
|
||||
void*
|
||||
jsd_GetContextPrivate(JSDContext* jsdc)
|
||||
{
|
||||
return jsdc->data;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_ClearAllProfileData(JSDContext* jsdc)
|
||||
{
|
||||
JSDScript *current;
|
||||
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
current = (JSDScript *)jsdc->scripts.next;
|
||||
while (current != (JSDScript *)&jsdc->scripts)
|
||||
{
|
||||
jsd_ClearScriptProfileData(jsdc, current);
|
||||
current = (JSDScript *)current->links.next;
|
||||
}
|
||||
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
}
|
||||
|
||||
JSDContext*
|
||||
jsd_JSDContextForJSContext(JSContext* context)
|
||||
{
|
||||
JSDContext* iter;
|
||||
JSDContext* jsdc = nullptr;
|
||||
JSRuntime* runtime = JS_GetRuntime(context);
|
||||
|
||||
JSD_LOCK();
|
||||
for( iter = (JSDContext*)_jsd_context_list.next;
|
||||
iter != (JSDContext*)&_jsd_context_list;
|
||||
iter = (JSDContext*)iter->links.next )
|
||||
{
|
||||
if( runtime == iter->jsrt )
|
||||
{
|
||||
jsdc = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
JSD_UNLOCK();
|
||||
return jsdc;
|
||||
}
|
||||
|
||||
static bool
|
||||
jsd_DebugErrorHook(JSContext *cx, const char *message,
|
||||
JSErrorReport *report, void *closure)
|
||||
{
|
||||
JSDContext* jsdc = (JSDContext*) closure;
|
||||
JSD_ErrorReporter errorReporter;
|
||||
void* errorReporterData;
|
||||
|
||||
if( ! jsdc )
|
||||
{
|
||||
MOZ_ASSERT(0);
|
||||
return true;
|
||||
}
|
||||
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
||||
return true;
|
||||
|
||||
/* local in case hook gets cleared on another thread */
|
||||
JSD_LOCK();
|
||||
errorReporter = jsdc->errorReporter;
|
||||
errorReporterData = jsdc->errorReporterData;
|
||||
JSD_UNLOCK();
|
||||
|
||||
if(!errorReporter)
|
||||
return true;
|
||||
|
||||
switch(errorReporter(jsdc, cx, message, report, errorReporterData))
|
||||
{
|
||||
case JSD_ERROR_REPORTER_PASS_ALONG:
|
||||
return true;
|
||||
case JSD_ERROR_REPORTER_RETURN:
|
||||
return false;
|
||||
case JSD_ERROR_REPORTER_DEBUG:
|
||||
{
|
||||
JS::RootedValue rval(cx);
|
||||
JSD_ExecutionHookProc hook;
|
||||
void* hookData;
|
||||
|
||||
/* local in case hook gets cleared on another thread */
|
||||
JSD_LOCK();
|
||||
hook = jsdc->debugBreakHook;
|
||||
hookData = jsdc->debugBreakHookData;
|
||||
JSD_UNLOCK();
|
||||
|
||||
jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUG_REQUESTED,
|
||||
hook, hookData, rval.address());
|
||||
/* XXX Should make this dependent on ExecutionHook retval */
|
||||
return true;
|
||||
}
|
||||
case JSD_ERROR_REPORTER_CLEAR_RETURN:
|
||||
if(report && JSREPORT_IS_EXCEPTION(report->flags))
|
||||
JS_ClearPendingException(cx);
|
||||
return false;
|
||||
default:
|
||||
MOZ_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_SetErrorReporter(JSDContext* jsdc,
|
||||
JSD_ErrorReporter reporter,
|
||||
void* callerdata)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->errorReporter = reporter;
|
||||
jsdc->errorReporterData = callerdata;
|
||||
JSD_UNLOCK();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_GetErrorReporter(JSDContext* jsdc,
|
||||
JSD_ErrorReporter* reporter,
|
||||
void** callerdata)
|
||||
{
|
||||
JSD_LOCK();
|
||||
if( reporter )
|
||||
*reporter = jsdc->errorReporter;
|
||||
if( callerdata )
|
||||
*callerdata = jsdc->errorReporterData;
|
||||
JSD_UNLOCK();
|
||||
return true;
|
||||
}
|
|
@ -1,330 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* JavaScript Debugging support - Hook support
|
||||
*/
|
||||
|
||||
#include "jsd.h"
|
||||
|
||||
JSTrapStatus
|
||||
jsd_InterruptHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
|
||||
void *closure)
|
||||
{
|
||||
JSDScript* jsdscript;
|
||||
JSDContext* jsdc = (JSDContext*) closure;
|
||||
JSD_ExecutionHookProc hook;
|
||||
void* hookData;
|
||||
|
||||
if( ! jsdc || ! jsdc->inited )
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
/* local in case jsdc->interruptHook gets cleared on another thread */
|
||||
JSD_LOCK();
|
||||
hook = jsdc->interruptHook;
|
||||
hookData = jsdc->interruptHookData;
|
||||
JSD_UNLOCK();
|
||||
|
||||
if (!hook)
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr());
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
if( ! jsdscript )
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_INTERRUPTED,
|
||||
hook, hookData, rval);
|
||||
}
|
||||
|
||||
JSTrapStatus
|
||||
jsd_DebuggerHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
jsval *rval, void *closure)
|
||||
{
|
||||
JSDScript* jsdscript;
|
||||
JSDContext* jsdc = (JSDContext*) closure;
|
||||
JSD_ExecutionHookProc hook;
|
||||
void* hookData;
|
||||
|
||||
if( ! jsdc || ! jsdc->inited )
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
/* local in case jsdc->debuggerHook gets cleared on another thread */
|
||||
JSD_LOCK();
|
||||
hook = jsdc->debuggerHook;
|
||||
hookData = jsdc->debuggerHookData;
|
||||
JSD_UNLOCK();
|
||||
if(!hook)
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr());
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
if( ! jsdscript )
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUGGER_KEYWORD,
|
||||
hook, hookData, rval);
|
||||
}
|
||||
|
||||
|
||||
JSTrapStatus
|
||||
jsd_ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
jsval *rvalArg, void *closure)
|
||||
{
|
||||
JSDScript* jsdscript;
|
||||
JSDContext* jsdc = (JSDContext*) closure;
|
||||
JSD_ExecutionHookProc hook;
|
||||
void* hookData;
|
||||
|
||||
if( ! jsdc || ! jsdc->inited )
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
/* local in case jsdc->throwHook gets cleared on another thread */
|
||||
JSD_LOCK();
|
||||
hook = jsdc->throwHook;
|
||||
hookData = jsdc->throwHookData;
|
||||
JSD_UNLOCK();
|
||||
if (!hook)
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr());
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
if( ! jsdscript )
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
JS::RootedValue rval(cx);
|
||||
JS_GetPendingException(cx, &rval);
|
||||
|
||||
JSTrapStatus result = jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_THROW,
|
||||
hook, hookData, rval.address());
|
||||
*rvalArg = rval;
|
||||
return result;
|
||||
}
|
||||
|
||||
JSTrapStatus
|
||||
jsd_CallExecutionHook(JSDContext* jsdc,
|
||||
JSContext *cx,
|
||||
unsigned type,
|
||||
JSD_ExecutionHookProc hook,
|
||||
void* hookData,
|
||||
jsval* rval)
|
||||
{
|
||||
unsigned hookanswer = JSD_HOOK_THROW == type ?
|
||||
JSD_HOOK_RETURN_CONTINUE_THROW :
|
||||
JSD_HOOK_RETURN_CONTINUE;
|
||||
JSDThreadState* jsdthreadstate;
|
||||
|
||||
if(hook && nullptr != (jsdthreadstate = jsd_NewThreadState(jsdc, cx)))
|
||||
{
|
||||
if ((type != JSD_HOOK_THROW && type != JSD_HOOK_INTERRUPTED) ||
|
||||
jsdc->flags & JSD_MASK_TOP_FRAME_ONLY ||
|
||||
!(jsdthreadstate->flags & TS_HAS_DISABLED_FRAME))
|
||||
{
|
||||
/*
|
||||
* if it's not a throw and it's not an interrupt,
|
||||
* or we're only masking the top frame,
|
||||
* or there are no disabled frames in this stack,
|
||||
* then call out.
|
||||
*/
|
||||
hookanswer = hook(jsdc, jsdthreadstate, type, hookData, rval);
|
||||
jsd_DestroyThreadState(jsdc, jsdthreadstate);
|
||||
}
|
||||
}
|
||||
|
||||
switch(hookanswer)
|
||||
{
|
||||
case JSD_HOOK_RETURN_ABORT:
|
||||
case JSD_HOOK_RETURN_HOOK_ERROR:
|
||||
return JSTRAP_ERROR;
|
||||
case JSD_HOOK_RETURN_RET_WITH_VAL:
|
||||
return JSTRAP_RETURN;
|
||||
case JSD_HOOK_RETURN_THROW_WITH_VAL:
|
||||
return JSTRAP_THROW;
|
||||
case JSD_HOOK_RETURN_CONTINUE:
|
||||
break;
|
||||
case JSD_HOOK_RETURN_CONTINUE_THROW:
|
||||
/* only makes sense for jsd_ThrowHandler (which init'd rval) */
|
||||
MOZ_ASSERT(JSD_HOOK_THROW == type);
|
||||
return JSTRAP_THROW;
|
||||
default:
|
||||
MOZ_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
return JSTRAP_CONTINUE;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_CallCallHook (JSDContext* jsdc,
|
||||
JSContext *cx,
|
||||
unsigned type,
|
||||
JSD_CallHookProc hook,
|
||||
void* hookData)
|
||||
{
|
||||
bool hookanswer;
|
||||
JSDThreadState* jsdthreadstate;
|
||||
|
||||
hookanswer = false;
|
||||
if(hook && nullptr != (jsdthreadstate = jsd_NewThreadState(jsdc, cx)))
|
||||
{
|
||||
hookanswer = hook(jsdc, jsdthreadstate, type, hookData);
|
||||
jsd_DestroyThreadState(jsdc, jsdthreadstate);
|
||||
}
|
||||
|
||||
return hookanswer;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_SetInterruptHook(JSDContext* jsdc,
|
||||
JSD_ExecutionHookProc hook,
|
||||
void* callerdata)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->interruptHookData = callerdata;
|
||||
jsdc->interruptHook = hook;
|
||||
JS_SetInterrupt(jsdc->jsrt, jsd_InterruptHandler, (void*) jsdc);
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_ClearInterruptHook(JSDContext* jsdc)
|
||||
{
|
||||
JSD_LOCK();
|
||||
JS_ClearInterrupt(jsdc->jsrt, nullptr, nullptr);
|
||||
jsdc->interruptHook = nullptr;
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_SetDebugBreakHook(JSDContext* jsdc,
|
||||
JSD_ExecutionHookProc hook,
|
||||
void* callerdata)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->debugBreakHookData = callerdata;
|
||||
jsdc->debugBreakHook = hook;
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_ClearDebugBreakHook(JSDContext* jsdc)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->debugBreakHook = nullptr;
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_SetDebuggerHook(JSDContext* jsdc,
|
||||
JSD_ExecutionHookProc hook,
|
||||
void* callerdata)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->debuggerHookData = callerdata;
|
||||
jsdc->debuggerHook = hook;
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_ClearDebuggerHook(JSDContext* jsdc)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->debuggerHook = nullptr;
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_SetThrowHook(JSDContext* jsdc,
|
||||
JSD_ExecutionHookProc hook,
|
||||
void* callerdata)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->throwHookData = callerdata;
|
||||
jsdc->throwHook = hook;
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_ClearThrowHook(JSDContext* jsdc)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->throwHook = nullptr;
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_SetFunctionHook(JSDContext* jsdc,
|
||||
JSD_CallHookProc hook,
|
||||
void* callerdata)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->functionHookData = callerdata;
|
||||
jsdc->functionHook = hook;
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_ClearFunctionHook(JSDContext* jsdc)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->functionHook = nullptr;
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_SetTopLevelHook(JSDContext* jsdc,
|
||||
JSD_CallHookProc hook,
|
||||
void* callerdata)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->toplevelHookData = callerdata;
|
||||
jsdc->toplevelHook = hook;
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_ClearTopLevelHook(JSDContext* jsdc)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->toplevelHook = nullptr;
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,228 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* JavaScript Debugging support - Locking and threading support
|
||||
*/
|
||||
|
||||
/*
|
||||
* ifdef JSD_USE_NSPR_LOCKS then you must build and run against NSPR2.
|
||||
* Otherwise, there are stubs that can be filled in with your own locking
|
||||
* code. Also, note that these stubs include a jsd_CurrentThread()
|
||||
* implementation that only works on Win32 - this is needed for the inprocess
|
||||
* Java-based debugger.
|
||||
*/
|
||||
|
||||
#include "jsd.h"
|
||||
|
||||
#include "js/Utility.h"
|
||||
|
||||
#ifdef JSD_THREADSAFE
|
||||
|
||||
#ifdef JSD_USE_NSPR_LOCKS
|
||||
|
||||
#include "prlock.h"
|
||||
#include "prthread.h"
|
||||
|
||||
#ifdef JSD_ATTACH_THREAD_HACK
|
||||
#include "pprthred.h" /* need this as long as JS_AttachThread is needed */
|
||||
#endif
|
||||
|
||||
struct JSDStaticLock
|
||||
{
|
||||
void* owner;
|
||||
PRLock* lock;
|
||||
int count;
|
||||
#ifdef DEBUG
|
||||
uint16_t sig;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* This exists to wrap non-NSPR theads (e.g. Java threads) in NSPR wrappers.
|
||||
* XXX We ignore the memory leak issue.
|
||||
* It is claimed that future versions of NSPR will automatically wrap on
|
||||
* the call to PR_GetCurrentThread.
|
||||
*
|
||||
* XXX We ignore the memory leak issue - i.e. we never call PR_DetachThread.
|
||||
*
|
||||
*/
|
||||
#undef _CURRENT_THREAD
|
||||
#ifdef JSD_ATTACH_THREAD_HACK
|
||||
#define _CURRENT_THREAD(out) \
|
||||
JS_BEGIN_MACRO \
|
||||
out = (void*) PR_GetCurrentThread(); \
|
||||
if(!out) \
|
||||
out = (void*) JS_AttachThread(PR_USER_THREAD, PR_PRIORITY_NORMAL, \
|
||||
nullptr); \
|
||||
MOZ_ASSERT(out); \
|
||||
JS_END_MACRO
|
||||
#else
|
||||
#define _CURRENT_THREAD(out) \
|
||||
JS_BEGIN_MACRO \
|
||||
out = (void*) PR_GetCurrentThread(); \
|
||||
MOZ_ASSERT(out); \
|
||||
JS_END_MACRO
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#define JSD_LOCK_SIG 0x10CC10CC
|
||||
void ASSERT_VALID_LOCK(JSDStaticLock* lock)
|
||||
{
|
||||
MOZ_ASSERT(lock);
|
||||
MOZ_ASSERT(lock->lock);
|
||||
MOZ_ASSERT(lock->count >= 0);
|
||||
MOZ_ASSERT(lock->sig == (uint16_t) JSD_LOCK_SIG);
|
||||
}
|
||||
#else
|
||||
#define ASSERT_VALID_LOCK(x) ((void)0)
|
||||
#endif
|
||||
|
||||
JSDStaticLock*
|
||||
jsd_CreateLock()
|
||||
{
|
||||
JSDStaticLock* lock;
|
||||
|
||||
if( ! (lock = js_pod_calloc<JSDStaticLock>()) ||
|
||||
! (lock->lock = PR_NewLock()) )
|
||||
{
|
||||
if(lock)
|
||||
{
|
||||
free(lock);
|
||||
lock = nullptr;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if(lock) lock->sig = (uint16_t) JSD_LOCK_SIG;
|
||||
#endif
|
||||
return lock;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_Lock(JSDStaticLock* lock)
|
||||
{
|
||||
void* me;
|
||||
ASSERT_VALID_LOCK(lock);
|
||||
_CURRENT_THREAD(me);
|
||||
|
||||
if(lock->owner == me)
|
||||
{
|
||||
lock->count++;
|
||||
MOZ_ASSERT(lock->count > 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
PR_Lock(lock->lock); /* this can block... */
|
||||
MOZ_ASSERT(lock->owner == 0);
|
||||
MOZ_ASSERT(lock->count == 0);
|
||||
lock->count = 1;
|
||||
lock->owner = me;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
jsd_Unlock(JSDStaticLock* lock)
|
||||
{
|
||||
void* me;
|
||||
ASSERT_VALID_LOCK(lock);
|
||||
_CURRENT_THREAD(me);
|
||||
|
||||
/* it's an error to unlock a lock you don't own */
|
||||
MOZ_ASSERT(lock->owner == me);
|
||||
if(lock->owner != me)
|
||||
return;
|
||||
|
||||
if(--lock->count == 0)
|
||||
{
|
||||
lock->owner = nullptr;
|
||||
PR_Unlock(lock->lock);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
jsd_IsLocked(JSDStaticLock* lock)
|
||||
{
|
||||
void* me;
|
||||
ASSERT_VALID_LOCK(lock);
|
||||
_CURRENT_THREAD(me);
|
||||
if (lock->owner != me)
|
||||
return false;
|
||||
MOZ_ASSERT(lock->count > 0);
|
||||
return true;
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
void*
|
||||
jsd_CurrentThread()
|
||||
{
|
||||
void* me;
|
||||
_CURRENT_THREAD(me);
|
||||
return me;
|
||||
}
|
||||
|
||||
|
||||
#else /* ! JSD_USE_NSPR_LOCKS */
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma message("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
||||
#pragma message("!! you are compiling the stubbed version of jsd_lock.c !!")
|
||||
#pragma message("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
||||
#endif
|
||||
|
||||
/*
|
||||
* NOTE: 'Real' versions of these locks must be reentrant in the sense that
|
||||
* they support nested calls to lock and unlock.
|
||||
*/
|
||||
|
||||
void*
|
||||
jsd_CreateLock()
|
||||
{
|
||||
return (void*)1;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_Lock(void* lock)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
jsd_Unlock(void* lock)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
jsd_IsLocked(void* lock)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
/*
|
||||
* This Windows only thread id code is here to allow the Java-based
|
||||
* JSDebugger to work with the single threaded js.c shell (even without
|
||||
* real locking and threading support).
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
/* bogus (but good enough) declaration*/
|
||||
extern void* __stdcall GetCurrentThreadId(void);
|
||||
#endif
|
||||
|
||||
void*
|
||||
jsd_CurrentThread()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
return (void*)1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* JSD_USE_NSPR_LOCKS */
|
||||
|
||||
#endif /* JSD_THREADSAFE */
|
|
@ -1,43 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* Header for JavaScript Debugging support - Locking and threading functions
|
||||
*/
|
||||
|
||||
#ifndef jsd_lock_h___
|
||||
#define jsd_lock_h___
|
||||
|
||||
/*
|
||||
* If you want to support threading and locking, define JSD_THREADSAFE and
|
||||
* implement the functions below.
|
||||
*/
|
||||
|
||||
/*
|
||||
* NOTE: These locks must be reentrant in the sense that they support
|
||||
* nested calls to lock and unlock.
|
||||
*/
|
||||
|
||||
typedef struct JSDStaticLock JSDStaticLock;
|
||||
|
||||
extern JSDStaticLock*
|
||||
jsd_CreateLock();
|
||||
|
||||
extern void
|
||||
jsd_Lock(JSDStaticLock* lock);
|
||||
|
||||
extern void
|
||||
jsd_Unlock(JSDStaticLock* lock);
|
||||
|
||||
#ifdef DEBUG
|
||||
extern bool
|
||||
jsd_IsLocked(JSDStaticLock* lock);
|
||||
#endif /* DEBUG */
|
||||
|
||||
extern void*
|
||||
jsd_CurrentThread();
|
||||
|
||||
#endif /* jsd_lock_h___ */
|
|
@ -1,234 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* JavaScript Debugging support - Object support
|
||||
*/
|
||||
|
||||
#include "jsd.h"
|
||||
|
||||
/*
|
||||
* #define JSD_TRACE 1
|
||||
*/
|
||||
|
||||
#ifdef JSD_TRACE
|
||||
#define TRACEOBJ(jsdc, jsdobj, which) _traceObj(jsdc, jsdobj, which)
|
||||
|
||||
static char *
|
||||
_describeObj(JSDContext* jsdc, JSDObject *jsdobj)
|
||||
{
|
||||
return
|
||||
JS_smprintf("%0x new'd in %s at line %d using ctor %s in %s at line %d",
|
||||
(int)jsdobj,
|
||||
JSD_GetObjectNewURL(jsdc, jsdobj),
|
||||
JSD_GetObjectNewLineNumber(jsdc, jsdobj),
|
||||
JSD_GetObjectConstructorName(jsdc, jsdobj),
|
||||
JSD_GetObjectConstructorURL(jsdc, jsdobj),
|
||||
JSD_GetObjectConstructorLineNumber(jsdc, jsdobj));
|
||||
}
|
||||
|
||||
static void
|
||||
_traceObj(JSDContext* jsdc, JSDObject* jsdobj, int which)
|
||||
{
|
||||
char* description;
|
||||
|
||||
if( !jsdobj )
|
||||
return;
|
||||
|
||||
description = _describeObj(jsdc, jsdobj);
|
||||
|
||||
printf("%s : %s\n",
|
||||
which == 0 ? "new " :
|
||||
which == 1 ? "final" :
|
||||
"ctor ",
|
||||
description);
|
||||
if(description)
|
||||
free(description);
|
||||
}
|
||||
#else
|
||||
#define TRACEOBJ(jsdc, jsdobj, which) ((void)0)
|
||||
#endif /* JSD_TRACE */
|
||||
|
||||
#ifdef DEBUG
|
||||
void JSD_ASSERT_VALID_OBJECT(JSDObject* jsdobj)
|
||||
{
|
||||
MOZ_ASSERT(jsdobj);
|
||||
MOZ_ASSERT(!JS_CLIST_IS_EMPTY(&jsdobj->links));
|
||||
MOZ_ASSERT(jsdobj->obj);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
_destroyJSDObject(JSDContext* jsdc, JSDObject* jsdobj)
|
||||
{
|
||||
MOZ_ASSERT(JSD_OBJECTS_LOCKED(jsdc));
|
||||
|
||||
JS_REMOVE_LINK(&jsdobj->links);
|
||||
JS_HashTableRemove(jsdc->objectsTable, jsdobj->obj);
|
||||
|
||||
if(jsdobj->newURL)
|
||||
jsd_DropAtom(jsdc, jsdobj->newURL);
|
||||
if(jsdobj->ctorURL)
|
||||
jsd_DropAtom(jsdc, jsdobj->ctorURL);
|
||||
if(jsdobj->ctorName)
|
||||
jsd_DropAtom(jsdc, jsdobj->ctorName);
|
||||
free(jsdobj);
|
||||
}
|
||||
|
||||
void
|
||||
jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj,
|
||||
JSAbstractFramePtr frame)
|
||||
{
|
||||
JSDObject* jsdobj;
|
||||
JS::RootedScript script(cx);
|
||||
JSDScript* jsdscript;
|
||||
const char* ctorURL;
|
||||
JSString* ctorNameStr;
|
||||
const char* ctorName;
|
||||
|
||||
JSD_LOCK_OBJECTS(jsdc);
|
||||
jsdobj = jsd_GetJSDObjectForJSObject(jsdc, obj);
|
||||
if( jsdobj && !jsdobj->ctorURL )
|
||||
{
|
||||
script = frame.script();
|
||||
if( script )
|
||||
{
|
||||
ctorURL = JS_GetScriptFilename(script);
|
||||
if( ctorURL )
|
||||
jsdobj->ctorURL = jsd_AddAtom(jsdc, ctorURL);
|
||||
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, frame);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
if( jsdscript && (ctorNameStr = jsd_GetScriptFunctionId(jsdc, jsdscript)) ) {
|
||||
if( (ctorName = JS_EncodeString(cx, ctorNameStr)) ) {
|
||||
jsdobj->ctorName = jsd_AddAtom(jsdc, ctorName);
|
||||
JS_free(cx, (void *) ctorName);
|
||||
}
|
||||
}
|
||||
jsdobj->ctorLineno = JS_GetScriptBaseLineNumber(cx, script);
|
||||
}
|
||||
}
|
||||
TRACEOBJ(jsdc, jsdobj, 3);
|
||||
JSD_UNLOCK_OBJECTS(jsdc);
|
||||
}
|
||||
|
||||
static JSHashNumber
|
||||
_hash_root(const void *key)
|
||||
{
|
||||
return ((JSHashNumber)(ptrdiff_t) key) >> 2; /* help lame MSVC1.5 on Win16 */
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_InitObjectManager(JSDContext* jsdc)
|
||||
{
|
||||
JS_INIT_CLIST(&jsdc->objectsList);
|
||||
jsdc->objectsTable = JS_NewHashTable(256, _hash_root,
|
||||
JS_CompareValues, JS_CompareValues,
|
||||
nullptr, nullptr);
|
||||
return !!jsdc->objectsTable;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_DestroyObjectManager(JSDContext* jsdc)
|
||||
{
|
||||
jsd_DestroyObjects(jsdc);
|
||||
JSD_LOCK_OBJECTS(jsdc);
|
||||
JS_HashTableDestroy(jsdc->objectsTable);
|
||||
JSD_UNLOCK_OBJECTS(jsdc);
|
||||
}
|
||||
|
||||
void
|
||||
jsd_DestroyObjects(JSDContext* jsdc)
|
||||
{
|
||||
JSD_LOCK_OBJECTS(jsdc);
|
||||
while( !JS_CLIST_IS_EMPTY(&jsdc->objectsList) )
|
||||
_destroyJSDObject(jsdc, (JSDObject*)JS_NEXT_LINK(&jsdc->objectsList));
|
||||
JSD_UNLOCK_OBJECTS(jsdc);
|
||||
}
|
||||
|
||||
JSDObject*
|
||||
jsd_IterateObjects(JSDContext* jsdc, JSDObject** iterp)
|
||||
{
|
||||
JSDObject *jsdobj = *iterp;
|
||||
|
||||
MOZ_ASSERT(JSD_OBJECTS_LOCKED(jsdc));
|
||||
|
||||
if( !jsdobj )
|
||||
jsdobj = (JSDObject *)jsdc->objectsList.next;
|
||||
if( jsdobj == (JSDObject *)&jsdc->objectsList )
|
||||
return nullptr;
|
||||
*iterp = (JSDObject*) jsdobj->links.next;
|
||||
return jsdobj;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
jsd_GetWrappedObject(JSDContext* jsdc, JSDObject* jsdobj)
|
||||
{
|
||||
return jsdobj->obj;
|
||||
}
|
||||
|
||||
const char*
|
||||
jsd_GetObjectNewURL(JSDContext* jsdc, JSDObject* jsdobj)
|
||||
{
|
||||
if( jsdobj->newURL )
|
||||
return JSD_ATOM_TO_STRING(jsdobj->newURL);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned
|
||||
jsd_GetObjectNewLineNumber(JSDContext* jsdc, JSDObject* jsdobj)
|
||||
{
|
||||
return jsdobj->newLineno;
|
||||
}
|
||||
|
||||
const char*
|
||||
jsd_GetObjectConstructorURL(JSDContext* jsdc, JSDObject* jsdobj)
|
||||
{
|
||||
if( jsdobj->ctorURL )
|
||||
return JSD_ATOM_TO_STRING(jsdobj->ctorURL);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned
|
||||
jsd_GetObjectConstructorLineNumber(JSDContext* jsdc, JSDObject* jsdobj)
|
||||
{
|
||||
return jsdobj->ctorLineno;
|
||||
}
|
||||
|
||||
const char*
|
||||
jsd_GetObjectConstructorName(JSDContext* jsdc, JSDObject* jsdobj)
|
||||
{
|
||||
if( jsdobj->ctorName )
|
||||
return JSD_ATOM_TO_STRING(jsdobj->ctorName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSDObject*
|
||||
jsd_GetJSDObjectForJSObject(JSDContext* jsdc, JSObject* jsobj)
|
||||
{
|
||||
JSDObject* jsdobj;
|
||||
|
||||
JSD_LOCK_OBJECTS(jsdc);
|
||||
jsdobj = (JSDObject*) JS_HashTableLookup(jsdc->objectsTable, jsobj);
|
||||
JSD_UNLOCK_OBJECTS(jsdc);
|
||||
return jsdobj;
|
||||
}
|
||||
|
||||
JSDObject*
|
||||
jsd_GetObjectForValue(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
return jsd_GetJSDObjectForJSObject(jsdc, jsdval->val.toObjectOrNull());
|
||||
}
|
||||
|
||||
JSDValue*
|
||||
jsd_GetValueForObject(JSDContext* jsdc, JSDObject* jsdobj)
|
||||
{
|
||||
return jsd_NewValue(jsdc, OBJECT_TO_JSVAL(jsdobj->obj));
|
||||
}
|
||||
|
||||
|
|
@ -1,883 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* JavaScript Debugging support - Script support
|
||||
*/
|
||||
|
||||
#include "jsd.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "nsCxPusher.h"
|
||||
|
||||
using mozilla::AutoSafeJSContext;
|
||||
|
||||
/* Comment this out to disable (NT specific) dumping as we go */
|
||||
/*
|
||||
** #ifdef DEBUG
|
||||
** #define JSD_DUMP 1
|
||||
** #endif
|
||||
*/
|
||||
|
||||
#define NOT_SET_YET -1
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
void JSD_ASSERT_VALID_SCRIPT(JSDScript* jsdscript)
|
||||
{
|
||||
MOZ_ASSERT(jsdscript);
|
||||
MOZ_ASSERT(jsdscript->script);
|
||||
}
|
||||
void JSD_ASSERT_VALID_EXEC_HOOK(JSDExecHook* jsdhook)
|
||||
{
|
||||
MOZ_ASSERT(jsdhook);
|
||||
MOZ_ASSERT(jsdhook->hook);
|
||||
}
|
||||
#endif
|
||||
|
||||
static JSDScript*
|
||||
_newJSDScript(JSDContext* jsdc,
|
||||
JSContext *cx,
|
||||
JSScript *script_)
|
||||
{
|
||||
JS::RootedScript script(cx, script_);
|
||||
if ( JS_GetScriptIsSelfHosted(script) )
|
||||
return nullptr;
|
||||
|
||||
JSDScript* jsdscript;
|
||||
unsigned lineno;
|
||||
const char* raw_filename;
|
||||
|
||||
MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
||||
|
||||
/* these are inlined javascript: urls and we can't handle them now */
|
||||
lineno = (unsigned) JS_GetScriptBaseLineNumber(cx, script);
|
||||
if( lineno == 0 )
|
||||
return nullptr;
|
||||
|
||||
jsdscript = (JSDScript*) calloc(1, sizeof(JSDScript));
|
||||
if( ! jsdscript )
|
||||
return nullptr;
|
||||
|
||||
raw_filename = JS_GetScriptFilename(script);
|
||||
|
||||
JS_HashTableAdd(jsdc->scriptsTable, (void *)script, (void *)jsdscript);
|
||||
JS_APPEND_LINK(&jsdscript->links, &jsdc->scripts);
|
||||
jsdscript->jsdc = jsdc;
|
||||
jsdscript->script = script;
|
||||
jsdscript->lineBase = lineno;
|
||||
jsdscript->lineExtent = (unsigned)NOT_SET_YET;
|
||||
jsdscript->data = nullptr;
|
||||
jsdscript->url = (char*) jsd_BuildNormalizedURL(raw_filename);
|
||||
|
||||
JS_INIT_CLIST(&jsdscript->hooks);
|
||||
|
||||
return jsdscript;
|
||||
}
|
||||
|
||||
static void
|
||||
_destroyJSDScript(JSDContext* jsdc,
|
||||
JSDScript* jsdscript)
|
||||
{
|
||||
MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
||||
|
||||
/* destroy all hooks */
|
||||
jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
|
||||
|
||||
JS_REMOVE_LINK(&jsdscript->links);
|
||||
if(jsdscript->url)
|
||||
free(jsdscript->url);
|
||||
|
||||
if (jsdscript->profileData)
|
||||
free(jsdscript->profileData);
|
||||
|
||||
free(jsdscript);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
#ifdef JSD_DUMP
|
||||
#ifndef XP_WIN
|
||||
void
|
||||
OutputDebugString (char *buf)
|
||||
{
|
||||
fprintf (stderr, "%s", buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
_dumpJSDScript(JSDContext* jsdc, JSDScript* jsdscript, const char* leadingtext)
|
||||
{
|
||||
const char* name;
|
||||
JSString* fun;
|
||||
unsigned base;
|
||||
unsigned extent;
|
||||
char Buf[256];
|
||||
size_t n;
|
||||
|
||||
name = jsd_GetScriptFilename(jsdc, jsdscript);
|
||||
fun = jsd_GetScriptFunctionId(jsdc, jsdscript);
|
||||
base = jsd_GetScriptBaseLineNumber(jsdc, jsdscript);
|
||||
extent = jsd_GetScriptLineExtent(jsdc, jsdscript);
|
||||
n = size_t(snprintf(Buf, sizeof(Buf), "%sscript=%08X, %s, ",
|
||||
leadingtext, (unsigned) jsdscript->script,
|
||||
name ? name : "no URL"));
|
||||
if (n + 1 < sizeof(Buf)) {
|
||||
if (fun) {
|
||||
n += size_t(snprintf(Buf + n, sizeof(Buf) - n, "%s", "no fun"));
|
||||
} else {
|
||||
n += JS_PutEscapedFlatString(Buf + n, sizeof(Buf) - n,
|
||||
MOZ_ASSERT_STRING_IS_FLAT(fun), 0);
|
||||
Buf[sizeof(Buf) - 1] = '\0';
|
||||
}
|
||||
if (n + 1 < sizeof(Buf))
|
||||
snprintf(Buf + n, sizeof(Buf) - n, ", %d-%d\n", base, base + extent - 1);
|
||||
}
|
||||
OutputDebugString( Buf );
|
||||
}
|
||||
|
||||
static void
|
||||
_dumpJSDScriptList( JSDContext* jsdc )
|
||||
{
|
||||
JSDScript* iterp = nullptr;
|
||||
JSDScript* jsdscript = nullptr;
|
||||
|
||||
OutputDebugString( "*** JSDScriptDump\n" );
|
||||
while( nullptr != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
|
||||
_dumpJSDScript( jsdc, jsdscript, " script: " );
|
||||
}
|
||||
#endif /* JSD_DUMP */
|
||||
|
||||
/***************************************************************************/
|
||||
static JSHashNumber
|
||||
jsd_hash_script(const void *key)
|
||||
{
|
||||
return ((JSHashNumber)(ptrdiff_t) key) >> 2; /* help lame MSVC1.5 on Win16 */
|
||||
}
|
||||
|
||||
static void *
|
||||
jsd_alloc_script_table(void *priv, size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void
|
||||
jsd_free_script_table(void *priv, void *item, size_t size)
|
||||
{
|
||||
free(item);
|
||||
}
|
||||
|
||||
static JSHashEntry *
|
||||
jsd_alloc_script_entry(void *priv, const void *item)
|
||||
{
|
||||
return (JSHashEntry*) malloc(sizeof(JSHashEntry));
|
||||
}
|
||||
|
||||
static void
|
||||
jsd_free_script_entry(void *priv, JSHashEntry *he, unsigned flag)
|
||||
{
|
||||
if (flag == HT_FREE_ENTRY)
|
||||
{
|
||||
_destroyJSDScript((JSDContext*) priv, (JSDScript*) he->value);
|
||||
free(he);
|
||||
}
|
||||
}
|
||||
|
||||
static const JSHashAllocOps script_alloc_ops = {
|
||||
jsd_alloc_script_table, jsd_free_script_table,
|
||||
jsd_alloc_script_entry, jsd_free_script_entry
|
||||
};
|
||||
|
||||
#ifndef JSD_SCRIPT_HASH_SIZE
|
||||
#define JSD_SCRIPT_HASH_SIZE 1024
|
||||
#endif
|
||||
|
||||
bool
|
||||
jsd_InitScriptManager(JSDContext* jsdc)
|
||||
{
|
||||
JS_INIT_CLIST(&jsdc->scripts);
|
||||
jsdc->scriptsTable = JS_NewHashTable(JSD_SCRIPT_HASH_SIZE, jsd_hash_script,
|
||||
JS_CompareValues, JS_CompareValues,
|
||||
&script_alloc_ops, (void*) jsdc);
|
||||
return !!jsdc->scriptsTable;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_DestroyScriptManager(JSDContext* jsdc)
|
||||
{
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
if (jsdc->scriptsTable)
|
||||
JS_HashTableDestroy(jsdc->scriptsTable);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
}
|
||||
|
||||
JSDScript*
|
||||
jsd_FindJSDScript( JSDContext* jsdc,
|
||||
JSScript *script )
|
||||
{
|
||||
MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
||||
return (JSDScript*) JS_HashTableLookup(jsdc->scriptsTable, (void *)script);
|
||||
}
|
||||
|
||||
JSDScript *
|
||||
jsd_FindOrCreateJSDScript(JSDContext *jsdc,
|
||||
JSContext *cx,
|
||||
JSScript *script_,
|
||||
JSAbstractFramePtr frame)
|
||||
{
|
||||
JS::RootedScript script(cx, script_);
|
||||
JSDScript *jsdscript;
|
||||
MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
||||
|
||||
jsdscript = jsd_FindJSDScript(jsdc, script);
|
||||
if (jsdscript)
|
||||
return jsdscript;
|
||||
|
||||
/* Fallback for unknown scripts: create a new script. */
|
||||
if (!frame) {
|
||||
JSBrokenFrameIterator iter(cx);
|
||||
if (!iter.done())
|
||||
frame = iter.abstractFramePtr();
|
||||
}
|
||||
if (frame)
|
||||
jsdscript = _newJSDScript(jsdc, cx, script);
|
||||
|
||||
return jsdscript;
|
||||
}
|
||||
|
||||
JSDProfileData*
|
||||
jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script)
|
||||
{
|
||||
if (!script->profileData)
|
||||
script->profileData = (JSDProfileData*)calloc(1, sizeof(JSDProfileData));
|
||||
|
||||
return script->profileData;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script)
|
||||
{
|
||||
return script->flags;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags)
|
||||
{
|
||||
script->flags = flags;
|
||||
}
|
||||
|
||||
unsigned
|
||||
jsd_GetScriptCallCount(JSDContext* jsdc, JSDScript *script)
|
||||
{
|
||||
if (script->profileData)
|
||||
return script->profileData->callCount;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned
|
||||
jsd_GetScriptMaxRecurseDepth(JSDContext* jsdc, JSDScript *script)
|
||||
{
|
||||
if (script->profileData)
|
||||
return script->profileData->maxRecurseDepth;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double
|
||||
jsd_GetScriptMinExecutionTime(JSDContext* jsdc, JSDScript *script)
|
||||
{
|
||||
if (script->profileData)
|
||||
return script->profileData->minExecutionTime;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double
|
||||
jsd_GetScriptMaxExecutionTime(JSDContext* jsdc, JSDScript *script)
|
||||
{
|
||||
if (script->profileData)
|
||||
return script->profileData->maxExecutionTime;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double
|
||||
jsd_GetScriptTotalExecutionTime(JSDContext* jsdc, JSDScript *script)
|
||||
{
|
||||
if (script->profileData)
|
||||
return script->profileData->totalExecutionTime;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double
|
||||
jsd_GetScriptMinOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
|
||||
{
|
||||
if (script->profileData)
|
||||
return script->profileData->minOwnExecutionTime;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double
|
||||
jsd_GetScriptMaxOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
|
||||
{
|
||||
if (script->profileData)
|
||||
return script->profileData->maxOwnExecutionTime;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double
|
||||
jsd_GetScriptTotalOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
|
||||
{
|
||||
if (script->profileData)
|
||||
return script->profileData->totalOwnExecutionTime;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_ClearScriptProfileData(JSDContext* jsdc, JSDScript *script)
|
||||
{
|
||||
if (script->profileData)
|
||||
{
|
||||
free(script->profileData);
|
||||
script->profileData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
JSScript *
|
||||
jsd_GetJSScript (JSDContext *jsdc, JSDScript *script)
|
||||
{
|
||||
return script->script;
|
||||
}
|
||||
|
||||
JSFunction *
|
||||
jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
return JS_GetScriptFunction(cx, script->script);
|
||||
}
|
||||
|
||||
JSDScript*
|
||||
jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp)
|
||||
{
|
||||
JSDScript *jsdscript = *iterp;
|
||||
|
||||
MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
||||
|
||||
if( !jsdscript )
|
||||
jsdscript = (JSDScript *)jsdc->scripts.next;
|
||||
if( jsdscript == (JSDScript *)&jsdc->scripts )
|
||||
return nullptr;
|
||||
*iterp = (JSDScript*) jsdscript->links.next;
|
||||
return jsdscript;
|
||||
}
|
||||
|
||||
void *
|
||||
jsd_SetScriptPrivate(JSDScript *jsdscript, void *data)
|
||||
{
|
||||
void *rval = jsdscript->data;
|
||||
jsdscript->data = data;
|
||||
return rval;
|
||||
}
|
||||
|
||||
void *
|
||||
jsd_GetScriptPrivate(JSDScript *jsdscript)
|
||||
{
|
||||
return jsdscript->data;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsActiveScript(JSDContext* jsdc, JSDScript *jsdscript)
|
||||
{
|
||||
JSDScript *current;
|
||||
|
||||
MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
|
||||
|
||||
for( current = (JSDScript *)jsdc->scripts.next;
|
||||
current != (JSDScript *)&jsdc->scripts;
|
||||
current = (JSDScript *)current->links.next )
|
||||
{
|
||||
if(jsdscript == current)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char*
|
||||
jsd_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript)
|
||||
{
|
||||
return jsdscript->url;
|
||||
}
|
||||
|
||||
JSString*
|
||||
jsd_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript)
|
||||
{
|
||||
JSString* str;
|
||||
JSFunction *fun = jsd_GetJSFunction(jsdc, jsdscript);
|
||||
|
||||
if( ! fun )
|
||||
return nullptr;
|
||||
str = JS_GetFunctionId(fun);
|
||||
|
||||
/* For compatibility we return "anonymous", not an empty string here. */
|
||||
return str ? str : JS_GetAnonymousString(jsdc->jsrt);
|
||||
}
|
||||
|
||||
unsigned
|
||||
jsd_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript)
|
||||
{
|
||||
return jsdscript->lineBase;
|
||||
}
|
||||
|
||||
unsigned
|
||||
jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, jsdc->glob); // Just in case.
|
||||
if( NOT_SET_YET == (int)jsdscript->lineExtent )
|
||||
jsdscript->lineExtent = JS_GetScriptLineExtent(cx, jsdscript->script);
|
||||
return jsdscript->lineExtent;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, unsigned line)
|
||||
{
|
||||
uintptr_t pc;
|
||||
|
||||
if( !jsdscript )
|
||||
return 0;
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, jsdscript->script);
|
||||
pc = (uintptr_t) JS_LineNumberToPC(cx, jsdscript->script, line );
|
||||
return pc;
|
||||
}
|
||||
|
||||
unsigned
|
||||
jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc)
|
||||
{
|
||||
unsigned first = jsdscript->lineBase;
|
||||
unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
|
||||
unsigned line = 0;
|
||||
|
||||
if (pc) {
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, jsdscript->script);
|
||||
line = JS_PCToLineNumber(cx, jsdscript->script, (jsbytecode*)pc);
|
||||
}
|
||||
|
||||
if( line < first )
|
||||
return first;
|
||||
if( line > last )
|
||||
return last;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript,
|
||||
unsigned startLine, unsigned maxLines,
|
||||
unsigned* count, unsigned** retLines, uintptr_t** retPCs)
|
||||
{
|
||||
unsigned first = jsdscript->lineBase;
|
||||
unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
|
||||
bool ok;
|
||||
jsbytecode **pcs;
|
||||
unsigned i;
|
||||
|
||||
if (last < startLine)
|
||||
return true;
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, jsdscript->script);
|
||||
|
||||
ok = JS_GetLinePCs(cx, jsdscript->script,
|
||||
startLine, maxLines,
|
||||
count, retLines, &pcs);
|
||||
|
||||
if (ok) {
|
||||
if (retPCs) {
|
||||
for (i = 0; i < *count; ++i) {
|
||||
(*retPCs)[i] = (*pcs)[i];
|
||||
}
|
||||
}
|
||||
|
||||
JS_free(cx, pcs);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata)
|
||||
{
|
||||
JSD_LOCK();
|
||||
jsdc->scriptHook = hook;
|
||||
jsdc->scriptHookData = callerdata;
|
||||
JSD_UNLOCK();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata)
|
||||
{
|
||||
JSD_LOCK();
|
||||
if( hook )
|
||||
*hook = jsdc->scriptHook;
|
||||
if( callerdata )
|
||||
*callerdata = jsdc->scriptHookData;
|
||||
JSD_UNLOCK();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, bool enable)
|
||||
{
|
||||
bool rv;
|
||||
AutoSafeJSContext cx;
|
||||
JS::RootedScript script(cx, jsdscript->script);
|
||||
JSAutoCompartment ac(cx, script);
|
||||
JSD_LOCK();
|
||||
rv = JS_SetSingleStepMode(cx, script, enable);
|
||||
JSD_UNLOCK();
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
void
|
||||
jsd_NewScriptHookProc(
|
||||
JSContext *cx,
|
||||
const char *filename, /* URL this script loads from */
|
||||
unsigned lineno, /* line where this script starts */
|
||||
JSScript *script,
|
||||
JSFunction *fun,
|
||||
void* callerdata )
|
||||
{
|
||||
JSDScript* jsdscript = nullptr;
|
||||
JSDContext* jsdc = (JSDContext*) callerdata;
|
||||
JSD_ScriptHookProc hook;
|
||||
void* hookData;
|
||||
|
||||
JSD_ASSERT_VALID_CONTEXT(jsdc);
|
||||
|
||||
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
||||
return;
|
||||
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
jsdscript = _newJSDScript(jsdc, cx, script);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
if( ! jsdscript )
|
||||
return;
|
||||
|
||||
#ifdef JSD_DUMP
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
_dumpJSDScript(jsdc, jsdscript, "***NEW Script: ");
|
||||
_dumpJSDScriptList( jsdc );
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
#endif /* JSD_DUMP */
|
||||
|
||||
/* local in case jsdc->scriptHook gets cleared on another thread */
|
||||
JSD_LOCK();
|
||||
hook = jsdc->scriptHook;
|
||||
if( hook )
|
||||
jsdscript->flags = jsdscript->flags | JSD_SCRIPT_CALL_DESTROY_HOOK_BIT;
|
||||
hookData = jsdc->scriptHookData;
|
||||
JSD_UNLOCK();
|
||||
|
||||
if( hook )
|
||||
hook(jsdc, jsdscript, true, hookData);
|
||||
}
|
||||
|
||||
void
|
||||
jsd_DestroyScriptHookProc(
|
||||
JSFreeOp *fop,
|
||||
JSScript *script_,
|
||||
void* callerdata )
|
||||
{
|
||||
JSDScript* jsdscript = nullptr;
|
||||
JSDContext* jsdc = (JSDContext*) callerdata;
|
||||
// NB: We're called during GC, so we can't push a cx. Root directly with
|
||||
// the runtime.
|
||||
JS::RootedScript script(jsdc->jsrt, script_);
|
||||
JSD_ScriptHookProc hook;
|
||||
void* hookData;
|
||||
|
||||
JSD_ASSERT_VALID_CONTEXT(jsdc);
|
||||
|
||||
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
||||
return;
|
||||
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
jsdscript = jsd_FindJSDScript(jsdc, script);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
|
||||
if( ! jsdscript )
|
||||
return;
|
||||
|
||||
#ifdef JSD_DUMP
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
_dumpJSDScript(jsdc, jsdscript, "***DESTROY Script: ");
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
#endif /* JSD_DUMP */
|
||||
|
||||
/* local in case hook gets cleared on another thread */
|
||||
JSD_LOCK();
|
||||
hook = (jsdscript->flags & JSD_SCRIPT_CALL_DESTROY_HOOK_BIT) ? jsdc->scriptHook
|
||||
: nullptr;
|
||||
hookData = jsdc->scriptHookData;
|
||||
JSD_UNLOCK();
|
||||
|
||||
if( hook )
|
||||
hook(jsdc, jsdscript, false, hookData);
|
||||
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
JS_HashTableRemove(jsdc->scriptsTable, (void *)script);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
|
||||
#ifdef JSD_DUMP
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
_dumpJSDScriptList(jsdc);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
#endif /* JSD_DUMP */
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
static JSDExecHook*
|
||||
_findHook(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc)
|
||||
{
|
||||
JSDExecHook* jsdhook;
|
||||
JSCList* list = &jsdscript->hooks;
|
||||
|
||||
for( jsdhook = (JSDExecHook*)list->next;
|
||||
jsdhook != (JSDExecHook*)list;
|
||||
jsdhook = (JSDExecHook*)jsdhook->links.next )
|
||||
{
|
||||
if (jsdhook->pc == pc)
|
||||
return jsdhook;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
_isActiveHook(JSDContext* jsdc, JSScript *script, JSDExecHook* jsdhook)
|
||||
{
|
||||
JSDExecHook* current;
|
||||
JSCList* list;
|
||||
JSDScript* jsdscript;
|
||||
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
jsdscript = jsd_FindJSDScript(jsdc, script);
|
||||
if( ! jsdscript)
|
||||
{
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
return false;
|
||||
}
|
||||
|
||||
list = &jsdscript->hooks;
|
||||
|
||||
for( current = (JSDExecHook*)list->next;
|
||||
current != (JSDExecHook*)list;
|
||||
current = (JSDExecHook*)current->links.next )
|
||||
{
|
||||
if(current == jsdhook)
|
||||
{
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
JSTrapStatus
|
||||
jsd_TrapHandler(JSContext *cx, JSScript *script_, jsbytecode *pc, jsval *rval,
|
||||
jsval closure)
|
||||
{
|
||||
JS::RootedScript script(cx, script_);
|
||||
JSDExecHook* jsdhook = (JSDExecHook*) closure.toPrivate();
|
||||
JSD_ExecutionHookProc hook;
|
||||
void* hookData;
|
||||
JSDContext* jsdc;
|
||||
|
||||
JSD_LOCK();
|
||||
|
||||
if( nullptr == (jsdc = jsd_JSDContextForJSContext(cx)) ||
|
||||
! _isActiveHook(jsdc, script, jsdhook) )
|
||||
{
|
||||
JSD_UNLOCK();
|
||||
return JSTRAP_CONTINUE;
|
||||
}
|
||||
|
||||
JSD_ASSERT_VALID_EXEC_HOOK(jsdhook);
|
||||
MOZ_ASSERT(!jsdhook->pc || jsdhook->pc == (uintptr_t)pc);
|
||||
MOZ_ASSERT(jsdhook->jsdscript->script == script);
|
||||
MOZ_ASSERT(jsdhook->jsdscript->jsdc == jsdc);
|
||||
|
||||
hook = jsdhook->hook;
|
||||
hookData = jsdhook->callerdata;
|
||||
|
||||
/* do not use jsdhook-> after this point */
|
||||
JSD_UNLOCK();
|
||||
|
||||
if( ! jsdc || ! jsdc->inited )
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_BREAKPOINT,
|
||||
hook, hookData, rval);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
jsd_SetExecutionHook(JSDContext* jsdc,
|
||||
JSDScript* jsdscript,
|
||||
uintptr_t pc,
|
||||
JSD_ExecutionHookProc hook,
|
||||
void* callerdata)
|
||||
{
|
||||
JSDExecHook* jsdhook;
|
||||
bool rv;
|
||||
|
||||
JSD_LOCK();
|
||||
if( ! hook )
|
||||
{
|
||||
jsd_ClearExecutionHook(jsdc, jsdscript, pc);
|
||||
JSD_UNLOCK();
|
||||
return true;
|
||||
}
|
||||
|
||||
jsdhook = _findHook(jsdc, jsdscript, pc);
|
||||
if( jsdhook )
|
||||
{
|
||||
jsdhook->hook = hook;
|
||||
jsdhook->callerdata = callerdata;
|
||||
JSD_UNLOCK();
|
||||
return true;
|
||||
}
|
||||
/* else... */
|
||||
|
||||
jsdhook = (JSDExecHook*)calloc(1, sizeof(JSDExecHook));
|
||||
if( ! jsdhook ) {
|
||||
JSD_UNLOCK();
|
||||
return false;
|
||||
}
|
||||
jsdhook->jsdscript = jsdscript;
|
||||
jsdhook->pc = pc;
|
||||
jsdhook->hook = hook;
|
||||
jsdhook->callerdata = callerdata;
|
||||
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, jsdscript->script);
|
||||
JS::RootedScript script(cx, jsdscript->script);
|
||||
JS::RootedValue hookValue(cx, PRIVATE_TO_JSVAL(jsdhook));
|
||||
rv = JS_SetTrap(cx, script, (jsbytecode*)pc, jsd_TrapHandler, hookValue);
|
||||
}
|
||||
|
||||
if ( ! rv ) {
|
||||
free(jsdhook);
|
||||
JSD_UNLOCK();
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks);
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_ClearExecutionHook(JSDContext* jsdc,
|
||||
JSDScript* jsdscript,
|
||||
uintptr_t pc)
|
||||
{
|
||||
JSDExecHook* jsdhook;
|
||||
|
||||
JSD_LOCK();
|
||||
|
||||
jsdhook = _findHook(jsdc, jsdscript, pc);
|
||||
if( ! jsdhook )
|
||||
{
|
||||
JSD_UNLOCK();
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, jsdscript->script);
|
||||
JS_ClearTrap(cx, jsdscript->script,
|
||||
(jsbytecode*)pc, nullptr, nullptr);
|
||||
}
|
||||
|
||||
JS_REMOVE_LINK(&jsdhook->links);
|
||||
free(jsdhook);
|
||||
|
||||
JSD_UNLOCK();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript)
|
||||
{
|
||||
JSDExecHook* jsdhook;
|
||||
JSCList* list = &jsdscript->hooks;
|
||||
JSD_LOCK();
|
||||
|
||||
while( (JSDExecHook*)list != (jsdhook = (JSDExecHook*)list->next) )
|
||||
{
|
||||
JS_REMOVE_LINK(&jsdhook->links);
|
||||
free(jsdhook);
|
||||
}
|
||||
|
||||
JS_ClearScriptTraps(jsdc->jsrt, jsdscript->script);
|
||||
JSD_UNLOCK();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_ClearAllExecutionHooks(JSDContext* jsdc)
|
||||
{
|
||||
JSDScript* jsdscript;
|
||||
JSDScript* iterp = nullptr;
|
||||
|
||||
JSD_LOCK();
|
||||
while( nullptr != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
|
||||
jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
|
||||
JSD_UNLOCK();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_ScriptCreated(JSDContext* jsdc,
|
||||
JSContext *cx,
|
||||
const char *filename, /* URL this script loads from */
|
||||
unsigned lineno, /* line where this script starts */
|
||||
JSScript *script,
|
||||
JSFunction *fun)
|
||||
{
|
||||
jsd_NewScriptHookProc(cx, filename, lineno, script, fun, jsdc);
|
||||
}
|
||||
|
||||
void
|
||||
jsd_ScriptDestroyed(JSDContext* jsdc,
|
||||
JSFreeOp *fop,
|
||||
JSScript *script)
|
||||
{
|
||||
jsd_DestroyScriptHookProc(fop, script, jsdc);
|
||||
}
|
|
@ -1,571 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* JavaScript Debugging support - Call stack support
|
||||
*/
|
||||
|
||||
#include "jsd.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "nsCxPusher.h"
|
||||
|
||||
using mozilla::AutoPushJSContext;
|
||||
|
||||
#ifdef DEBUG
|
||||
void JSD_ASSERT_VALID_THREAD_STATE(JSDThreadState* jsdthreadstate)
|
||||
{
|
||||
MOZ_ASSERT(jsdthreadstate);
|
||||
MOZ_ASSERT(jsdthreadstate->stackDepth > 0);
|
||||
}
|
||||
|
||||
void JSD_ASSERT_VALID_STACK_FRAME(JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
MOZ_ASSERT(jsdframe);
|
||||
MOZ_ASSERT(jsdframe->jsdthreadstate);
|
||||
}
|
||||
#endif
|
||||
|
||||
static JSDStackFrameInfo*
|
||||
_addNewFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSScript* script,
|
||||
uintptr_t pc,
|
||||
bool isConstructing,
|
||||
JSAbstractFramePtr frame)
|
||||
{
|
||||
JSDStackFrameInfo* jsdframe;
|
||||
JSDScript* jsdscript = nullptr;
|
||||
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
jsdscript = jsd_FindJSDScript(jsdc, script);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
if (!jsdscript || (jsdc->flags & JSD_HIDE_DISABLED_FRAMES &&
|
||||
!JSD_IS_DEBUG_ENABLED(jsdc, jsdscript)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!JSD_IS_DEBUG_ENABLED(jsdc, jsdscript))
|
||||
jsdthreadstate->flags |= TS_HAS_DISABLED_FRAME;
|
||||
|
||||
jsdframe = (JSDStackFrameInfo*) calloc(1, sizeof(JSDStackFrameInfo));
|
||||
if( ! jsdframe )
|
||||
return nullptr;
|
||||
|
||||
jsdframe->jsdthreadstate = jsdthreadstate;
|
||||
jsdframe->jsdscript = jsdscript;
|
||||
jsdframe->isConstructing = isConstructing;
|
||||
jsdframe->pc = pc;
|
||||
jsdframe->frame = frame;
|
||||
|
||||
JS_APPEND_LINK(&jsdframe->links, &jsdthreadstate->stack);
|
||||
jsdthreadstate->stackDepth++;
|
||||
|
||||
return jsdframe;
|
||||
}
|
||||
|
||||
static void
|
||||
_destroyFrame(JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
/* kill any alloc'd objects in frame here... */
|
||||
|
||||
if( jsdframe )
|
||||
free(jsdframe);
|
||||
}
|
||||
|
||||
JSDThreadState*
|
||||
jsd_NewThreadState(JSDContext* jsdc, JSContext *cx )
|
||||
{
|
||||
JSDThreadState* jsdthreadstate;
|
||||
|
||||
jsdthreadstate = (JSDThreadState*)calloc(1, sizeof(JSDThreadState));
|
||||
if( ! jsdthreadstate )
|
||||
return nullptr;
|
||||
|
||||
jsdthreadstate->context = cx;
|
||||
jsdthreadstate->thread = JSD_CURRENT_THREAD();
|
||||
JS_INIT_CLIST(&jsdthreadstate->stack);
|
||||
jsdthreadstate->stackDepth = 0;
|
||||
|
||||
JS_BeginRequest(jsdthreadstate->context);
|
||||
|
||||
JSBrokenFrameIterator iter(cx);
|
||||
while(!iter.done())
|
||||
{
|
||||
JSAbstractFramePtr frame = iter.abstractFramePtr();
|
||||
JS::RootedScript script(cx, frame.script());
|
||||
uintptr_t pc = (uintptr_t)frame.pc();
|
||||
JS::RootedValue dummyThis(cx);
|
||||
|
||||
/*
|
||||
* don't construct a JSDStackFrame for dummy frames (those without a
|
||||
* |this| object, or native frames, if JSD_INCLUDE_NATIVE_FRAMES
|
||||
* isn't set.
|
||||
*/
|
||||
if (frame.getThisValue(cx, &dummyThis))
|
||||
{
|
||||
bool isConstructing = iter.isConstructing();
|
||||
JSDStackFrameInfo *frameInfo = _addNewFrame( jsdc, jsdthreadstate, script, pc, isConstructing, frame );
|
||||
|
||||
if ((jsdthreadstate->stackDepth == 0 && !frameInfo) ||
|
||||
(jsdthreadstate->stackDepth == 1 && frameInfo &&
|
||||
frameInfo->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frameInfo->jsdscript)))
|
||||
{
|
||||
/*
|
||||
* if we failed to create the first frame, or the top frame
|
||||
* is not enabled for debugging, fail the entire thread state.
|
||||
*/
|
||||
JS_INIT_CLIST(&jsdthreadstate->links);
|
||||
JS_EndRequest(jsdthreadstate->context);
|
||||
jsd_DestroyThreadState(jsdc, jsdthreadstate);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
++iter;
|
||||
}
|
||||
JS_EndRequest(jsdthreadstate->context);
|
||||
|
||||
if (jsdthreadstate->stackDepth == 0)
|
||||
{
|
||||
free(jsdthreadstate);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
JS_APPEND_LINK(&jsdthreadstate->links, &jsdc->threadsStates);
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
return jsdthreadstate;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_DestroyThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
|
||||
{
|
||||
JSDStackFrameInfo* jsdframe;
|
||||
JSCList* list;
|
||||
|
||||
MOZ_ASSERT(jsdthreadstate);
|
||||
MOZ_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
JS_REMOVE_LINK(&jsdthreadstate->links);
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
list = &jsdthreadstate->stack;
|
||||
while( (JSDStackFrameInfo*)list != (jsdframe = (JSDStackFrameInfo*)list->next) )
|
||||
{
|
||||
JS_REMOVE_LINK(&jsdframe->links);
|
||||
_destroyFrame(jsdframe);
|
||||
}
|
||||
free(jsdthreadstate);
|
||||
}
|
||||
|
||||
unsigned
|
||||
jsd_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
|
||||
{
|
||||
unsigned count = 0;
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
|
||||
count = jsdthreadstate->stackDepth;
|
||||
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
JSDStackFrameInfo*
|
||||
jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
|
||||
{
|
||||
JSDStackFrameInfo* jsdframe = nullptr;
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
|
||||
jsdframe = (JSDStackFrameInfo*) JS_LIST_HEAD(&jsdthreadstate->stack);
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
return jsdframe;
|
||||
}
|
||||
|
||||
JSContext *
|
||||
jsd_GetJSContext (JSDContext* jsdc, JSDThreadState* jsdthreadstate)
|
||||
{
|
||||
JSContext* cx = nullptr;
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
|
||||
cx = jsdthreadstate->context;
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
return cx;
|
||||
}
|
||||
|
||||
JSDStackFrameInfo*
|
||||
jsd_GetCallingStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
JSDStackFrameInfo* nextjsdframe = nullptr;
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
|
||||
if( JS_LIST_HEAD(&jsdframe->links) != &jsdframe->jsdthreadstate->stack )
|
||||
nextjsdframe = (JSDStackFrameInfo*) JS_LIST_HEAD(&jsdframe->links);
|
||||
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
return nextjsdframe;
|
||||
}
|
||||
|
||||
JSDScript*
|
||||
jsd_GetScriptForStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
JSDScript* jsdscript = nullptr;
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
|
||||
jsdscript = jsdframe->jsdscript;
|
||||
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
return jsdscript;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
jsd_GetPCForStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
uintptr_t pc = 0;
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
|
||||
pc = jsdframe->pc;
|
||||
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
JSDValue*
|
||||
jsd_GetCallObjectForStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
JSObject* obj;
|
||||
JSDValue* jsdval = nullptr;
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
|
||||
{
|
||||
AutoPushJSContext cx(jsdthreadstate->context);
|
||||
obj = jsdframe->frame.callObject(cx);
|
||||
if(obj)
|
||||
jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));
|
||||
}
|
||||
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
return jsdval;
|
||||
}
|
||||
|
||||
JSDValue*
|
||||
jsd_GetScopeChainForStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
JS::RootedObject obj(jsdthreadstate->context);
|
||||
JSDValue* jsdval = nullptr;
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
|
||||
{
|
||||
AutoPushJSContext cx(jsdthreadstate->context);
|
||||
obj = jsdframe->frame.scopeChain(cx);
|
||||
if(obj)
|
||||
jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));
|
||||
}
|
||||
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
return jsdval;
|
||||
}
|
||||
|
||||
JSDValue*
|
||||
jsd_GetThisForStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
JSDValue* jsdval = nullptr;
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
|
||||
{
|
||||
bool ok;
|
||||
JS::RootedValue thisval(jsdthreadstate->context);
|
||||
AutoPushJSContext cx(jsdthreadstate->context);
|
||||
ok = jsdframe->frame.getThisValue(cx, &thisval);
|
||||
if(ok)
|
||||
jsdval = JSD_NewValue(jsdc, thisval);
|
||||
}
|
||||
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
return jsdval;
|
||||
}
|
||||
|
||||
JSString*
|
||||
jsd_GetIdForStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
JSString *rv = nullptr;
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
|
||||
{
|
||||
JSFunction *fun = jsdframe->frame.maybeFun();
|
||||
if( fun )
|
||||
{
|
||||
rv = JS_GetFunctionId (fun);
|
||||
|
||||
/*
|
||||
* For compatibility we return "anonymous", not an empty string
|
||||
* here.
|
||||
*/
|
||||
if( !rv )
|
||||
rv = JS_GetAnonymousString(jsdc->jsrt);
|
||||
}
|
||||
}
|
||||
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsStackFrameDebugger(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
bool rv = true;
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
|
||||
{
|
||||
rv = jsdframe->frame.isDebuggerFrame();
|
||||
}
|
||||
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsStackFrameConstructing(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
bool rv = true;
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
|
||||
{
|
||||
rv = jsdframe->isConstructing;
|
||||
}
|
||||
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_EvaluateUCScriptInStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe,
|
||||
const jschar *bytes, unsigned length,
|
||||
const char *filename, unsigned lineno,
|
||||
bool eatExceptions, JS::MutableHandleValue rval)
|
||||
{
|
||||
bool retval;
|
||||
bool valid;
|
||||
JSExceptionState* exceptionState = nullptr;
|
||||
|
||||
MOZ_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( ! valid )
|
||||
return false;
|
||||
|
||||
AutoPushJSContext cx(jsdthreadstate->context);
|
||||
MOZ_ASSERT(cx);
|
||||
|
||||
if (eatExceptions)
|
||||
exceptionState = JS_SaveExceptionState(cx);
|
||||
JS_ClearPendingException(cx);
|
||||
jsd_StartingEvalUsingFilename(jsdc, filename);
|
||||
retval = jsdframe->frame.evaluateUCInStackFrame(cx, bytes, length, filename, lineno,
|
||||
rval);
|
||||
jsd_FinishedEvalUsingFilename(jsdc, filename);
|
||||
if (eatExceptions)
|
||||
JS_RestoreExceptionState(cx, exceptionState);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_EvaluateScriptInStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe,
|
||||
const char *bytes, unsigned length,
|
||||
const char *filename, unsigned lineno,
|
||||
bool eatExceptions, JS::MutableHandleValue rval)
|
||||
{
|
||||
bool retval;
|
||||
bool valid;
|
||||
JSExceptionState* exceptionState = nullptr;
|
||||
|
||||
MOZ_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
AutoPushJSContext cx(jsdthreadstate->context);
|
||||
MOZ_ASSERT(cx);
|
||||
|
||||
if (eatExceptions)
|
||||
exceptionState = JS_SaveExceptionState(cx);
|
||||
JS_ClearPendingException(cx);
|
||||
jsd_StartingEvalUsingFilename(jsdc, filename);
|
||||
retval = jsdframe->frame.evaluateInStackFrame(cx, bytes, length, filename, lineno,
|
||||
rval);
|
||||
jsd_FinishedEvalUsingFilename(jsdc, filename);
|
||||
if (eatExceptions)
|
||||
JS_RestoreExceptionState(cx, exceptionState);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
JSString*
|
||||
jsd_ValToStringInStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe,
|
||||
jsval val)
|
||||
{
|
||||
bool valid;
|
||||
JSString* retval;
|
||||
JSExceptionState* exceptionState;
|
||||
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
|
||||
if( ! valid )
|
||||
return nullptr;
|
||||
|
||||
AutoPushJSContext cx(jsdthreadstate->context);
|
||||
|
||||
JS::RootedValue v(cx, val);
|
||||
exceptionState = JS_SaveExceptionState(cx);
|
||||
retval = JS::ToString(cx, v);
|
||||
JS_RestoreExceptionState(cx, exceptionState);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValidThreadState(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate)
|
||||
{
|
||||
JSDThreadState *cur;
|
||||
|
||||
MOZ_ASSERT( JSD_THREADSTATES_LOCKED(jsdc) );
|
||||
|
||||
for( cur = (JSDThreadState*)jsdc->threadsStates.next;
|
||||
cur != (JSDThreadState*)&jsdc->threadsStates;
|
||||
cur = (JSDThreadState*)cur->links.next )
|
||||
{
|
||||
if( cur == jsdthreadstate )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValidFrameInThreadState(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
JSDStackFrameInfo* jsdframe)
|
||||
{
|
||||
MOZ_ASSERT(JSD_THREADSTATES_LOCKED(jsdc));
|
||||
|
||||
if( ! jsd_IsValidThreadState(jsdc, jsdthreadstate) )
|
||||
return false;
|
||||
if( jsdframe->jsdthreadstate != jsdthreadstate )
|
||||
return false;
|
||||
|
||||
JSD_ASSERT_VALID_THREAD_STATE(jsdthreadstate);
|
||||
JSD_ASSERT_VALID_STACK_FRAME(jsdframe);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSContext*
|
||||
_getContextForThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
|
||||
{
|
||||
bool valid;
|
||||
JSD_LOCK_THREADSTATES(jsdc);
|
||||
valid = jsd_IsValidThreadState(jsdc, jsdthreadstate);
|
||||
JSD_UNLOCK_THREADSTATES(jsdc);
|
||||
if( valid )
|
||||
return jsdthreadstate->context;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSDValue*
|
||||
jsd_GetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
|
||||
{
|
||||
JSContext* cx;
|
||||
if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate)))
|
||||
return nullptr;
|
||||
|
||||
JS::RootedValue val(cx);
|
||||
if(JS_GetPendingException(cx, &val))
|
||||
return jsd_NewValue(jsdc, val);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_SetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate,
|
||||
JSDValue* jsdval)
|
||||
{
|
||||
JSContext* cx;
|
||||
|
||||
if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate)))
|
||||
return false;
|
||||
|
||||
if(jsdval) {
|
||||
JS::RootedValue exn(cx, JSD_GetValueWrappedJSVal(jsdc, jsdval));
|
||||
JS_SetPendingException(cx, exn);
|
||||
} else {
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,286 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* JavaScript Debugging support - Stepping support
|
||||
*/
|
||||
|
||||
#include "jsd.h"
|
||||
|
||||
/*
|
||||
* #define JSD_TRACE 1
|
||||
*/
|
||||
|
||||
#ifdef JSD_TRACE
|
||||
|
||||
static char*
|
||||
_indentSpaces(int i)
|
||||
{
|
||||
#define MAX_INDENT 63
|
||||
static char* p = nullptr;
|
||||
if(!p)
|
||||
{
|
||||
p = calloc(1, MAX_INDENT+1);
|
||||
if(!p) return "";
|
||||
memset(p, ' ', MAX_INDENT);
|
||||
}
|
||||
if(i > MAX_INDENT) return p;
|
||||
return p + MAX_INDENT-i;
|
||||
}
|
||||
|
||||
static void
|
||||
_interpreterTrace(JSDContext* jsdc, JSContext *cx, JSAbstractFramePtr frame,
|
||||
bool isConstructing, bool before)
|
||||
{
|
||||
JSDScript* jsdscript = nullptr;
|
||||
JSScript * script;
|
||||
static indent = 0;
|
||||
JSString* funName = nullptr;
|
||||
|
||||
script = frame.script();
|
||||
if(script)
|
||||
{
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, frame);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
if(jsdscript)
|
||||
funName = JSD_GetScriptFunctionId(jsdc, jsdscript);
|
||||
}
|
||||
|
||||
if(before)
|
||||
printf("%sentering ", _indentSpaces(indent++));
|
||||
else
|
||||
printf("%sleaving ", _indentSpaces(--indent));
|
||||
|
||||
if (!funName)
|
||||
printf("TOP_LEVEL");
|
||||
else
|
||||
JS_FileEscapedString(stdout, funName, 0);
|
||||
|
||||
if(before)
|
||||
{
|
||||
jsval thisVal;
|
||||
|
||||
printf("%s this: ", isConstructing ? "constructing":"");
|
||||
|
||||
if (JS_GetFrameThis(cx, frame, &thisVal))
|
||||
printf("0x%0llx", (uintptr_t) thisVal);
|
||||
else
|
||||
puts("<unavailable>");
|
||||
}
|
||||
printf("\n");
|
||||
MOZ_ASSERT(indent >= 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
_callHook(JSDContext *jsdc, JSContext *cx, JSAbstractFramePtr frame, bool isConstructing,
|
||||
bool before, unsigned type, JSD_CallHookProc hook, void *hookData)
|
||||
{
|
||||
JSDScript* jsdscript;
|
||||
JSScript* jsscript;
|
||||
bool hookresult = true;
|
||||
|
||||
if (!jsdc || !jsdc->inited)
|
||||
return false;
|
||||
|
||||
if (!hook && !(jsdc->flags & JSD_COLLECT_PROFILE_DATA))
|
||||
{
|
||||
/* no hook to call, no profile data needs to be collected,
|
||||
* so there is nothing to do here.
|
||||
*/
|
||||
return hookresult;
|
||||
}
|
||||
|
||||
if (before && isConstructing) {
|
||||
JS::RootedValue newObj(cx);
|
||||
if (!frame.getThisValue(cx, &newObj))
|
||||
return false;
|
||||
jsd_Constructing(jsdc, cx, newObj.toObjectOrNull(), frame);
|
||||
}
|
||||
|
||||
jsscript = frame.script();
|
||||
if (jsscript)
|
||||
{
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, jsscript, frame);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
|
||||
if (jsdscript)
|
||||
{
|
||||
if (JSD_IS_PROFILE_ENABLED(jsdc, jsdscript))
|
||||
{
|
||||
JSDProfileData *pdata;
|
||||
pdata = jsd_GetScriptProfileData (jsdc, jsdscript);
|
||||
if (pdata)
|
||||
{
|
||||
if (before)
|
||||
{
|
||||
if (!pdata->lastCallStart)
|
||||
{
|
||||
int64_t now;
|
||||
JSDProfileData *callerpdata;
|
||||
|
||||
/* Get the time just the once, for consistency. */
|
||||
now = JS_Now();
|
||||
/* This contains a pointer to the profile data for
|
||||
* the caller of this function. */
|
||||
callerpdata = jsdc->callingFunctionPData;
|
||||
if (callerpdata)
|
||||
{
|
||||
int64_t ll_delta;
|
||||
pdata->caller = callerpdata;
|
||||
/* We need to 'stop' the timer for the caller.
|
||||
* Use time since last return if appropriate. */
|
||||
ll_delta = jsdc->lastReturnTime
|
||||
? now - jsdc->lastReturnTime
|
||||
: now - callerpdata->lastCallStart;
|
||||
callerpdata->runningTime += ll_delta;
|
||||
}
|
||||
/* We're the new current function, and no return
|
||||
* has happened yet. */
|
||||
jsdc->callingFunctionPData = pdata;
|
||||
jsdc->lastReturnTime = 0;
|
||||
/* This function has no running time (just been
|
||||
* called!), and we'll need the call start time. */
|
||||
pdata->runningTime = 0;
|
||||
pdata->lastCallStart = now;
|
||||
} else {
|
||||
if (++pdata->recurseDepth > pdata->maxRecurseDepth)
|
||||
pdata->maxRecurseDepth = pdata->recurseDepth;
|
||||
}
|
||||
/* make sure we're called for the return too. */
|
||||
hookresult = true;
|
||||
} else if (!pdata->recurseDepth && pdata->lastCallStart) {
|
||||
int64_t now, ll_delta;
|
||||
double delta;
|
||||
now = JS_Now();
|
||||
ll_delta = now - pdata->lastCallStart;
|
||||
delta = ll_delta;
|
||||
delta /= 1000.0;
|
||||
pdata->totalExecutionTime += delta;
|
||||
/* minExecutionTime starts as 0, so we need to overwrite
|
||||
* it on the first call always. */
|
||||
if ((0 == pdata->callCount) ||
|
||||
delta < pdata->minExecutionTime)
|
||||
{
|
||||
pdata->minExecutionTime = delta;
|
||||
}
|
||||
if (delta > pdata->maxExecutionTime)
|
||||
pdata->maxExecutionTime = delta;
|
||||
|
||||
/* If we last returned from a function (as opposed to
|
||||
* having last entered this function), we need to inc.
|
||||
* the running total by the time delta since the last
|
||||
* return, and use the running total instead of the
|
||||
* delta calculated above. */
|
||||
if (jsdc->lastReturnTime)
|
||||
{
|
||||
/* Add last chunk to running time, and use total
|
||||
* running time as 'delta'. */
|
||||
ll_delta = now - jsdc->lastReturnTime;
|
||||
pdata->runningTime += ll_delta;
|
||||
delta = pdata->runningTime;
|
||||
delta /= 1000.0;
|
||||
}
|
||||
|
||||
pdata->totalOwnExecutionTime += delta;
|
||||
/* See minExecutionTime comment above. */
|
||||
if ((0 == pdata->callCount) ||
|
||||
delta < pdata->minOwnExecutionTime)
|
||||
{
|
||||
pdata->minOwnExecutionTime = delta;
|
||||
}
|
||||
if (delta > pdata->maxOwnExecutionTime)
|
||||
pdata->maxOwnExecutionTime = delta;
|
||||
|
||||
/* Current function is now our caller. */
|
||||
jsdc->callingFunctionPData = pdata->caller;
|
||||
/* No hanging pointers, please. */
|
||||
pdata->caller = nullptr;
|
||||
/* Mark the time we returned, and indicate this
|
||||
* function is no longer running. */
|
||||
jsdc->lastReturnTime = now;
|
||||
pdata->lastCallStart = 0;
|
||||
++pdata->callCount;
|
||||
} else if (pdata->recurseDepth) {
|
||||
--pdata->recurseDepth;
|
||||
++pdata->callCount;
|
||||
}
|
||||
}
|
||||
if (hook)
|
||||
jsd_CallCallHook (jsdc, cx, type, hook, hookData);
|
||||
} else {
|
||||
if (hook)
|
||||
hookresult =
|
||||
jsd_CallCallHook (jsdc, cx, type, hook, hookData);
|
||||
else
|
||||
hookresult = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef JSD_TRACE
|
||||
_interpreterTrace(jsdc, cx, frame, isConstructing, before);
|
||||
return true;
|
||||
#else
|
||||
return hookresult;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void *
|
||||
jsd_FunctionCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing,
|
||||
bool before, bool *ok, void *closure)
|
||||
{
|
||||
JSDContext* jsdc;
|
||||
JSD_CallHookProc hook;
|
||||
void* hookData;
|
||||
|
||||
jsdc = (JSDContext*) closure;
|
||||
|
||||
/* local in case jsdc->functionHook gets cleared on another thread */
|
||||
JSD_LOCK();
|
||||
hook = jsdc->functionHook;
|
||||
hookData = jsdc->functionHookData;
|
||||
JSD_UNLOCK();
|
||||
|
||||
if (_callHook (jsdc, cx, frame, isConstructing, before,
|
||||
(before) ? JSD_HOOK_FUNCTION_CALL : JSD_HOOK_FUNCTION_RETURN,
|
||||
hook, hookData))
|
||||
{
|
||||
return closure;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *
|
||||
jsd_TopLevelCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing,
|
||||
bool before, bool *ok, void *closure)
|
||||
{
|
||||
JSDContext* jsdc;
|
||||
JSD_CallHookProc hook;
|
||||
void* hookData;
|
||||
|
||||
jsdc = (JSDContext*) closure;
|
||||
|
||||
/* local in case jsdc->toplevelHook gets cleared on another thread */
|
||||
JSD_LOCK();
|
||||
hook = jsdc->toplevelHook;
|
||||
hookData = jsdc->toplevelHookData;
|
||||
JSD_UNLOCK();
|
||||
|
||||
if (_callHook (jsdc, cx, frame, isConstructing, before,
|
||||
(before) ? JSD_HOOK_TOPLEVEL_START : JSD_HOOK_TOPLEVEL_END,
|
||||
hook, hookData))
|
||||
{
|
||||
return closure;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
}
|
|
@ -1,525 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* JavaScript Debugging support - Source Text functions
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include "jsd.h"
|
||||
#include "jsprf.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
void JSD_ASSERT_VALID_SOURCE_TEXT(JSDSourceText* jsdsrc)
|
||||
{
|
||||
MOZ_ASSERT(jsdsrc);
|
||||
MOZ_ASSERT(jsdsrc->url);
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************/
|
||||
/* XXX add notification */
|
||||
|
||||
static void
|
||||
_clearText(JSDContext* jsdc, JSDSourceText* jsdsrc)
|
||||
{
|
||||
if( jsdsrc->text )
|
||||
free(jsdsrc->text);
|
||||
jsdsrc->text = nullptr;
|
||||
jsdsrc->textLength = 0;
|
||||
jsdsrc->textSpace = 0;
|
||||
jsdsrc->status = JSD_SOURCE_CLEARED;
|
||||
jsdsrc->dirty = true;
|
||||
jsdsrc->alterCount = jsdc->sourceAlterCount++ ;
|
||||
jsdsrc->doingEval = false;
|
||||
}
|
||||
|
||||
static bool
|
||||
_appendText(JSDContext* jsdc, JSDSourceText* jsdsrc,
|
||||
const char* text, size_t length)
|
||||
{
|
||||
#define MEMBUF_GROW 1000
|
||||
|
||||
unsigned neededSize = jsdsrc->textLength + length;
|
||||
|
||||
if( neededSize > jsdsrc->textSpace )
|
||||
{
|
||||
char* newBuf;
|
||||
unsigned iNewSize;
|
||||
|
||||
/* if this is the first alloc, the req might be all that's needed*/
|
||||
if( ! jsdsrc->textSpace )
|
||||
iNewSize = length;
|
||||
else
|
||||
iNewSize = (neededSize * 5 / 4) + MEMBUF_GROW;
|
||||
|
||||
newBuf = (char*) realloc(jsdsrc->text, iNewSize);
|
||||
if( ! newBuf )
|
||||
{
|
||||
/* try again with the minimal size really asked for */
|
||||
iNewSize = neededSize;
|
||||
newBuf = (char*) realloc(jsdsrc->text, iNewSize);
|
||||
if( ! newBuf )
|
||||
{
|
||||
/* out of memory */
|
||||
_clearText( jsdc, jsdsrc );
|
||||
jsdsrc->status = JSD_SOURCE_FAILED;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
jsdsrc->text = newBuf;
|
||||
jsdsrc->textSpace = iNewSize;
|
||||
}
|
||||
|
||||
memcpy(jsdsrc->text + jsdsrc->textLength, text, length);
|
||||
jsdsrc->textLength += length;
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSDSourceText*
|
||||
_newSource(JSDContext* jsdc, char* url)
|
||||
{
|
||||
JSDSourceText* jsdsrc = (JSDSourceText*)calloc(1,sizeof(JSDSourceText));
|
||||
if( ! jsdsrc )
|
||||
return nullptr;
|
||||
|
||||
jsdsrc->url = url;
|
||||
jsdsrc->status = JSD_SOURCE_INITED;
|
||||
jsdsrc->dirty = true;
|
||||
jsdsrc->alterCount = jsdc->sourceAlterCount++ ;
|
||||
|
||||
return jsdsrc;
|
||||
}
|
||||
|
||||
static void
|
||||
_destroySource(JSDContext* jsdc, JSDSourceText* jsdsrc)
|
||||
{
|
||||
MOZ_ASSERT(nullptr == jsdsrc->text); /* must _clearText() first */
|
||||
free(jsdsrc->url);
|
||||
free(jsdsrc);
|
||||
}
|
||||
|
||||
static void
|
||||
_removeSource(JSDContext* jsdc, JSDSourceText* jsdsrc)
|
||||
{
|
||||
JS_REMOVE_LINK(&jsdsrc->links);
|
||||
_clearText(jsdc, jsdsrc);
|
||||
_destroySource(jsdc, jsdsrc);
|
||||
}
|
||||
|
||||
static JSDSourceText*
|
||||
_addSource(JSDContext* jsdc, char* url)
|
||||
{
|
||||
JSDSourceText* jsdsrc = _newSource(jsdc, url);
|
||||
if( ! jsdsrc )
|
||||
return nullptr;
|
||||
JS_INSERT_LINK(&jsdsrc->links, &jsdc->sources);
|
||||
return jsdsrc;
|
||||
}
|
||||
|
||||
static void
|
||||
_moveSourceToRemovedList(JSDContext* jsdc, JSDSourceText* jsdsrc)
|
||||
{
|
||||
_clearText(jsdc, jsdsrc);
|
||||
JS_REMOVE_LINK(&jsdsrc->links);
|
||||
JS_INSERT_LINK(&jsdsrc->links, &jsdc->removedSources);
|
||||
}
|
||||
|
||||
static void
|
||||
_removeSourceFromRemovedList( JSDContext* jsdc, JSDSourceText* jsdsrc )
|
||||
{
|
||||
JS_REMOVE_LINK(&jsdsrc->links);
|
||||
_destroySource( jsdc, jsdsrc );
|
||||
}
|
||||
|
||||
static bool
|
||||
_isSourceInSourceList(JSDContext* jsdc, JSDSourceText* jsdsrcToFind)
|
||||
{
|
||||
JSDSourceText *jsdsrc;
|
||||
|
||||
for( jsdsrc = (JSDSourceText*)jsdc->sources.next;
|
||||
jsdsrc != (JSDSourceText*)&jsdc->sources;
|
||||
jsdsrc = (JSDSourceText*)jsdsrc->links.next )
|
||||
{
|
||||
if( jsdsrc == jsdsrcToFind )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* compare strings in a case insensitive manner with a length limit
|
||||
*/
|
||||
|
||||
static int
|
||||
strncasecomp (const char* one, const char * two, int n)
|
||||
{
|
||||
const char *pA;
|
||||
const char *pB;
|
||||
|
||||
for(pA=one, pB=two;; pA++, pB++)
|
||||
{
|
||||
int tmp;
|
||||
if (pA == one+n)
|
||||
return 0;
|
||||
if (!(*pA && *pB))
|
||||
return *pA - *pB;
|
||||
tmp = tolower(*pA) - tolower(*pB);
|
||||
if (tmp)
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static const char file_url_prefix[] = "file:";
|
||||
#define FILE_URL_PREFIX_LEN (sizeof file_url_prefix - 1)
|
||||
|
||||
char*
|
||||
jsd_BuildNormalizedURL( const char* url_string )
|
||||
{
|
||||
char *new_url_string;
|
||||
|
||||
if( ! url_string )
|
||||
return nullptr;
|
||||
|
||||
if (!strncasecomp(url_string, file_url_prefix, FILE_URL_PREFIX_LEN) &&
|
||||
url_string[FILE_URL_PREFIX_LEN + 0] == '/' &&
|
||||
url_string[FILE_URL_PREFIX_LEN + 1] == '/') {
|
||||
new_url_string = JS_smprintf("%s%s",
|
||||
file_url_prefix,
|
||||
url_string + FILE_URL_PREFIX_LEN + 2);
|
||||
} else {
|
||||
new_url_string = strdup(url_string);
|
||||
}
|
||||
return new_url_string;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
void
|
||||
jsd_DestroyAllSources( JSDContext* jsdc )
|
||||
{
|
||||
JSDSourceText *jsdsrc;
|
||||
JSDSourceText *next;
|
||||
|
||||
for( jsdsrc = (JSDSourceText*)jsdc->sources.next;
|
||||
jsdsrc != (JSDSourceText*)&jsdc->sources;
|
||||
jsdsrc = next )
|
||||
{
|
||||
next = (JSDSourceText*)jsdsrc->links.next;
|
||||
_removeSource( jsdc, jsdsrc );
|
||||
}
|
||||
|
||||
for( jsdsrc = (JSDSourceText*)jsdc->removedSources.next;
|
||||
jsdsrc != (JSDSourceText*)&jsdc->removedSources;
|
||||
jsdsrc = next )
|
||||
{
|
||||
next = (JSDSourceText*)jsdsrc->links.next;
|
||||
_removeSourceFromRemovedList( jsdc, jsdsrc );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JSDSourceText*
|
||||
jsd_IterateSources(JSDContext* jsdc, JSDSourceText **iterp)
|
||||
{
|
||||
JSDSourceText *jsdsrc = *iterp;
|
||||
|
||||
if( !jsdsrc )
|
||||
jsdsrc = (JSDSourceText *)jsdc->sources.next;
|
||||
if( jsdsrc == (JSDSourceText *)&jsdc->sources )
|
||||
return nullptr;
|
||||
*iterp = (JSDSourceText *)jsdsrc->links.next;
|
||||
return jsdsrc;
|
||||
}
|
||||
|
||||
JSDSourceText*
|
||||
jsd_FindSourceForURL(JSDContext* jsdc, const char* url)
|
||||
{
|
||||
JSDSourceText *jsdsrc;
|
||||
|
||||
for( jsdsrc = (JSDSourceText *)jsdc->sources.next;
|
||||
jsdsrc != (JSDSourceText *)&jsdc->sources;
|
||||
jsdsrc = (JSDSourceText *)jsdsrc->links.next )
|
||||
{
|
||||
if( 0 == strcmp(jsdsrc->url, url) )
|
||||
return jsdsrc;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char*
|
||||
jsd_GetSourceURL(JSDContext* jsdc, JSDSourceText* jsdsrc)
|
||||
{
|
||||
return jsdsrc->url;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_GetSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc,
|
||||
const char** ppBuf, int* pLen )
|
||||
{
|
||||
*ppBuf = jsdsrc->text;
|
||||
*pLen = jsdsrc->textLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_ClearSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc)
|
||||
{
|
||||
if( JSD_SOURCE_INITED != jsdsrc->status &&
|
||||
JSD_SOURCE_PARTIAL != jsdsrc->status )
|
||||
{
|
||||
_clearText(jsdc, jsdsrc);
|
||||
}
|
||||
}
|
||||
|
||||
JSDSourceStatus
|
||||
jsd_GetSourceStatus(JSDContext* jsdc, JSDSourceText* jsdsrc)
|
||||
{
|
||||
return jsdsrc->status;
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc)
|
||||
{
|
||||
return jsdsrc->dirty;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_SetSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc, bool dirty)
|
||||
{
|
||||
jsdsrc->dirty = dirty;
|
||||
}
|
||||
|
||||
unsigned
|
||||
jsd_GetSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc)
|
||||
{
|
||||
return jsdsrc->alterCount;
|
||||
}
|
||||
|
||||
unsigned
|
||||
jsd_IncrementSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc)
|
||||
{
|
||||
return jsdsrc->alterCount = jsdc->sourceAlterCount++;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
#if defined(DEBUG) && 0
|
||||
void DEBUG_ITERATE_SOURCES( JSDContext* jsdc )
|
||||
{
|
||||
JSDSourceText* iterp = nullptr;
|
||||
JSDSourceText* jsdsrc = nullptr;
|
||||
int dummy;
|
||||
|
||||
while( nullptr != (jsdsrc = jsd_IterateSources(jsdc, &iterp)) )
|
||||
{
|
||||
const char* url;
|
||||
const char* text;
|
||||
int len;
|
||||
bool dirty;
|
||||
JSDStreamStatus status;
|
||||
bool gotSrc;
|
||||
|
||||
url = JSD_GetSourceURL(jsdc, jsdsrc);
|
||||
dirty = JSD_IsSourceDirty(jsdc, jsdsrc);
|
||||
status = JSD_GetSourceStatus(jsdc, jsdsrc);
|
||||
gotSrc = JSD_GetSourceText(jsdc, jsdsrc, &text, &len );
|
||||
|
||||
dummy = 0; /* gives us a line to set breakpoint... */
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define DEBUG_ITERATE_SOURCES(x) ((void)x)
|
||||
#endif
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
JSDSourceText*
|
||||
jsd_NewSourceText(JSDContext* jsdc, const char* url)
|
||||
{
|
||||
JSDSourceText* jsdsrc;
|
||||
char* new_url_string;
|
||||
|
||||
JSD_LOCK_SOURCE_TEXT(jsdc);
|
||||
|
||||
new_url_string = jsd_BuildNormalizedURL(url);
|
||||
|
||||
if( ! new_url_string )
|
||||
return nullptr;
|
||||
|
||||
jsdsrc = jsd_FindSourceForURL(jsdc, new_url_string);
|
||||
|
||||
if( jsdsrc )
|
||||
{
|
||||
if( jsdsrc->doingEval )
|
||||
{
|
||||
free(new_url_string);
|
||||
JSD_UNLOCK_SOURCE_TEXT(jsdc);
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
_moveSourceToRemovedList(jsdc, jsdsrc);
|
||||
}
|
||||
|
||||
jsdsrc = _addSource( jsdc, new_url_string );
|
||||
|
||||
JSD_UNLOCK_SOURCE_TEXT(jsdc);
|
||||
|
||||
return jsdsrc;
|
||||
}
|
||||
|
||||
JSDSourceText*
|
||||
jsd_AppendSourceText(JSDContext* jsdc,
|
||||
JSDSourceText* jsdsrc,
|
||||
const char* text, /* *not* zero terminated */
|
||||
size_t length,
|
||||
JSDSourceStatus status)
|
||||
{
|
||||
JSD_LOCK_SOURCE_TEXT(jsdc);
|
||||
|
||||
if( jsdsrc->doingEval )
|
||||
{
|
||||
JSD_UNLOCK_SOURCE_TEXT(jsdc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if( ! _isSourceInSourceList( jsdc, jsdsrc ) )
|
||||
{
|
||||
_removeSourceFromRemovedList( jsdc, jsdsrc );
|
||||
JSD_UNLOCK_SOURCE_TEXT(jsdc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if( text && length && ! _appendText( jsdc, jsdsrc, text, length ) )
|
||||
{
|
||||
jsdsrc->dirty = true;
|
||||
jsdsrc->alterCount = jsdc->sourceAlterCount++ ;
|
||||
jsdsrc->status = JSD_SOURCE_FAILED;
|
||||
_moveSourceToRemovedList(jsdc, jsdsrc);
|
||||
JSD_UNLOCK_SOURCE_TEXT(jsdc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jsdsrc->dirty = true;
|
||||
jsdsrc->alterCount = jsdc->sourceAlterCount++ ;
|
||||
jsdsrc->status = status;
|
||||
DEBUG_ITERATE_SOURCES(jsdc);
|
||||
JSD_UNLOCK_SOURCE_TEXT(jsdc);
|
||||
return jsdsrc;
|
||||
}
|
||||
|
||||
JSDSourceText*
|
||||
jsd_AppendUCSourceText(JSDContext* jsdc,
|
||||
JSDSourceText* jsdsrc,
|
||||
const jschar* text, /* *not* zero terminated */
|
||||
size_t length,
|
||||
JSDSourceStatus status)
|
||||
{
|
||||
#define UNICODE_TRUNCATE_BUF_SIZE 1024
|
||||
static char* buf = nullptr;
|
||||
int remaining = length;
|
||||
|
||||
if(!text || !length)
|
||||
return jsd_AppendSourceText(jsdc, jsdsrc, nullptr, 0, status);
|
||||
|
||||
JSD_LOCK_SOURCE_TEXT(jsdc);
|
||||
if(!buf)
|
||||
{
|
||||
buf = js_pod_malloc<char>(UNICODE_TRUNCATE_BUF_SIZE);
|
||||
if(!buf)
|
||||
{
|
||||
JSD_UNLOCK_SOURCE_TEXT(jsdc);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
while(remaining && jsdsrc) {
|
||||
int bytes = (remaining < UNICODE_TRUNCATE_BUF_SIZE) ? remaining : UNICODE_TRUNCATE_BUF_SIZE;
|
||||
int i;
|
||||
for(i = 0; i < bytes; i++)
|
||||
buf[i] = (const char) *(text++);
|
||||
jsdsrc = jsd_AppendSourceText(jsdc,jsdsrc,
|
||||
buf, bytes,
|
||||
JSD_SOURCE_PARTIAL);
|
||||
remaining -= bytes;
|
||||
}
|
||||
if(jsdsrc && status != JSD_SOURCE_PARTIAL)
|
||||
jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, nullptr, 0, status);
|
||||
|
||||
JSD_UNLOCK_SOURCE_TEXT(jsdc);
|
||||
return jsdsrc;
|
||||
}
|
||||
|
||||
/* convienence function for adding complete source of url in one call */
|
||||
bool
|
||||
jsd_AddFullSourceText(JSDContext* jsdc,
|
||||
const char* text, /* *not* zero terminated */
|
||||
size_t length,
|
||||
const char* url)
|
||||
{
|
||||
JSDSourceText* jsdsrc;
|
||||
|
||||
JSD_LOCK_SOURCE_TEXT(jsdc);
|
||||
|
||||
jsdsrc = jsd_NewSourceText(jsdc, url);
|
||||
if( jsdsrc )
|
||||
jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc,
|
||||
text, length, JSD_SOURCE_PARTIAL );
|
||||
if( jsdsrc )
|
||||
jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc,
|
||||
nullptr, 0, JSD_SOURCE_COMPLETED );
|
||||
|
||||
JSD_UNLOCK_SOURCE_TEXT(jsdc);
|
||||
|
||||
return jsdsrc ? true : false;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
void
|
||||
jsd_StartingEvalUsingFilename(JSDContext* jsdc, const char* url)
|
||||
{
|
||||
JSDSourceText* jsdsrc;
|
||||
|
||||
/* NOTE: We leave it locked! */
|
||||
JSD_LOCK_SOURCE_TEXT(jsdc);
|
||||
|
||||
jsdsrc = jsd_FindSourceForURL(jsdc, url);
|
||||
if(jsdsrc)
|
||||
{
|
||||
#if 0
|
||||
#ifndef JSD_LOWLEVEL_SOURCE
|
||||
MOZ_ASSERT(! jsdsrc->doingEval);
|
||||
#endif
|
||||
#endif
|
||||
jsdsrc->doingEval = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
jsd_FinishedEvalUsingFilename(JSDContext* jsdc, const char* url)
|
||||
{
|
||||
JSDSourceText* jsdsrc;
|
||||
|
||||
/* NOTE: We ASSUME it is locked! */
|
||||
|
||||
jsdsrc = jsd_FindSourceForURL(jsdc, url);
|
||||
if(jsdsrc)
|
||||
{
|
||||
#if 0
|
||||
#ifndef JSD_LOWLEVEL_SOURCE
|
||||
/*
|
||||
* when using this low level source addition, this jsdsrc might
|
||||
* not have existed before the eval, but does exist now (without
|
||||
* this flag set!)
|
||||
*/
|
||||
MOZ_ASSERT(jsdsrc->doingEval);
|
||||
#endif
|
||||
#endif
|
||||
jsdsrc->doingEval = false;
|
||||
}
|
||||
|
||||
JSD_UNLOCK_SOURCE_TEXT(jsdc);
|
||||
}
|
|
@ -1,746 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* JavaScript Debugging support - Value and Property support
|
||||
*/
|
||||
|
||||
#include "jsd.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "jswrapper.h"
|
||||
#include "nsCxPusher.h"
|
||||
|
||||
using mozilla::AutoSafeJSContext;
|
||||
|
||||
#ifdef DEBUG
|
||||
void JSD_ASSERT_VALID_VALUE(JSDValue* jsdval)
|
||||
{
|
||||
MOZ_ASSERT(jsdval);
|
||||
MOZ_ASSERT(jsdval->nref > 0);
|
||||
if(!JS_CLIST_IS_EMPTY(&jsdval->props))
|
||||
{
|
||||
MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS));
|
||||
MOZ_ASSERT(!jsdval->val.isPrimitive());
|
||||
}
|
||||
|
||||
if(jsdval->proto)
|
||||
{
|
||||
MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PROTO));
|
||||
MOZ_ASSERT(jsdval->proto->nref > 0);
|
||||
}
|
||||
if(jsdval->parent)
|
||||
{
|
||||
MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PARENT));
|
||||
MOZ_ASSERT(jsdval->parent->nref > 0);
|
||||
}
|
||||
if(jsdval->ctor)
|
||||
{
|
||||
MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_CTOR));
|
||||
MOZ_ASSERT(jsdval->ctor->nref > 0);
|
||||
}
|
||||
}
|
||||
|
||||
void JSD_ASSERT_VALID_PROPERTY(JSDProperty* jsdprop)
|
||||
{
|
||||
MOZ_ASSERT(jsdprop);
|
||||
MOZ_ASSERT(jsdprop->name);
|
||||
MOZ_ASSERT(jsdprop->name->nref > 0);
|
||||
MOZ_ASSERT(jsdprop->val);
|
||||
MOZ_ASSERT(jsdprop->val->nref > 0);
|
||||
if(jsdprop->alias)
|
||||
MOZ_ASSERT(jsdprop->alias->nref > 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool
|
||||
jsd_IsValueObject(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
return !jsdval->val.isPrimitive() || jsdval->val.isNull();
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValueNumber(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
return jsdval->val.isNumber();
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValueInt(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
return jsdval->val.isInt32();
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValueDouble(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
return jsdval->val.isDouble();
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValueString(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
return jsdval->val.isString();
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValueBoolean(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
return jsdval->val.isBoolean();
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValueNull(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
return jsdval->val.isNull();
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValueVoid(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
return jsdval->val.isUndefined();
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValuePrimitive(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
return jsdval->val.isPrimitive();
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValueFunction(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
AutoSafeJSContext cx; // NB: Actually unused.
|
||||
return !jsdval->val.isPrimitive() &&
|
||||
JS_ObjectIsCallable(cx, jsdval->val.toObjectOrNull());
|
||||
}
|
||||
|
||||
bool
|
||||
jsd_IsValueNative(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JS::RootedFunction fun(cx);
|
||||
|
||||
if(jsd_IsValueFunction(jsdc, jsdval))
|
||||
{
|
||||
JSAutoCompartment ac(cx, jsdval->val.toObjectOrNull());
|
||||
AutoSaveExceptionState as(cx);
|
||||
bool ok = false;
|
||||
fun = JSD_GetValueFunction(jsdc, jsdval);
|
||||
if(fun)
|
||||
ok = JS_GetFunctionScript(cx, fun) ? false : true;
|
||||
MOZ_ASSERT(fun);
|
||||
return ok;
|
||||
}
|
||||
return !jsdval->val.isPrimitive();
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
bool
|
||||
jsd_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
jsval val = jsdval->val;
|
||||
if(!val.isBoolean())
|
||||
return false;
|
||||
return val.toBoolean();
|
||||
}
|
||||
|
||||
int32_t
|
||||
jsd_GetValueInt(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
jsval val = jsdval->val;
|
||||
if(!val.isInt32())
|
||||
return 0;
|
||||
return val.toInt32();
|
||||
}
|
||||
|
||||
double
|
||||
jsd_GetValueDouble(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
if(!jsdval->val.isDouble())
|
||||
return 0;
|
||||
return jsdval->val.toDouble();
|
||||
}
|
||||
|
||||
JSString*
|
||||
jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JS::RootedValue stringval(cx);
|
||||
JS::RootedString string(cx);
|
||||
JS::RootedObject scopeObj(cx);
|
||||
|
||||
if(jsdval->string)
|
||||
return jsdval->string;
|
||||
|
||||
/* Reuse the string without copying or re-rooting it */
|
||||
if(jsdval->val.isString()) {
|
||||
jsdval->string = jsdval->val.toString();
|
||||
return jsdval->string;
|
||||
}
|
||||
|
||||
/* Objects call JS_ValueToString in their own compartment. */
|
||||
scopeObj = !jsdval->val.isPrimitive() ? jsdval->val.toObjectOrNull() : jsdc->glob;
|
||||
{
|
||||
JSAutoCompartment ac(cx, scopeObj);
|
||||
AutoSaveExceptionState as(cx);
|
||||
JS::RootedValue v(cx, jsdval->val);
|
||||
string = JS::ToString(cx, v);
|
||||
}
|
||||
|
||||
JSAutoCompartment ac2(cx, jsdc->glob);
|
||||
if(string) {
|
||||
stringval = STRING_TO_JSVAL(string);
|
||||
}
|
||||
if(!string || !JS_WrapValue(cx, &stringval)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jsdval->string = stringval.toString();
|
||||
if(!JS::AddNamedStringRoot(cx, &jsdval->string, "ValueString"))
|
||||
jsdval->string = nullptr;
|
||||
|
||||
return jsdval->string;
|
||||
}
|
||||
|
||||
JSString*
|
||||
jsd_GetValueFunctionId(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JS::RootedFunction fun(cx);
|
||||
|
||||
if(!jsdval->funName && jsd_IsValueFunction(jsdc, jsdval))
|
||||
{
|
||||
JSAutoCompartment ac(cx, jsdval->val.toObjectOrNull());
|
||||
AutoSaveExceptionState as(cx);
|
||||
fun = JSD_GetValueFunction(jsdc, jsdval);
|
||||
if(!fun)
|
||||
return nullptr;
|
||||
jsdval->funName = JS_GetFunctionId(fun);
|
||||
|
||||
/* For compatibility we return "anonymous", not an empty string here. */
|
||||
if (!jsdval->funName)
|
||||
jsdval->funName = JS_GetAnonymousString(jsdc->jsrt);
|
||||
}
|
||||
return jsdval->funName;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/*
|
||||
* Create a new JSD value referring to a jsval. Copy string values into the
|
||||
* JSD compartment. Leave all other GCTHINGs in their native compartments
|
||||
* and access them through cross-compartment calls.
|
||||
*/
|
||||
JSDValue*
|
||||
jsd_NewValue(JSDContext* jsdc, jsval value)
|
||||
{
|
||||
JS::RootedValue val(jsdc->jsrt, value);
|
||||
AutoSafeJSContext cx;
|
||||
JSDValue* jsdval;
|
||||
|
||||
if(!(jsdval = (JSDValue*) calloc(1, sizeof(JSDValue))))
|
||||
return nullptr;
|
||||
|
||||
if(val.isGCThing())
|
||||
{
|
||||
bool ok;
|
||||
JSAutoCompartment ac(cx, jsdc->glob);
|
||||
|
||||
ok = JS::AddNamedValueRoot(cx, &jsdval->val, "JSDValue");
|
||||
if(ok && val.isString()) {
|
||||
if(!JS_WrapValue(cx, &val)) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!ok)
|
||||
{
|
||||
free(jsdval);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
jsdval->val = val;
|
||||
jsdval->nref = 1;
|
||||
JS_INIT_CLIST(&jsdval->props);
|
||||
|
||||
return jsdval;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_DropValue(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
MOZ_ASSERT(jsdval->nref > 0);
|
||||
if(0 == --jsdval->nref)
|
||||
{
|
||||
jsd_RefreshValue(jsdc, jsdval);
|
||||
if(jsdval->val.isGCThing())
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, jsdc->glob);
|
||||
JS::RemoveValueRoot(cx, &jsdval->val);
|
||||
}
|
||||
free(jsdval);
|
||||
}
|
||||
}
|
||||
|
||||
jsval
|
||||
jsd_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JS::RootedValue val(cx, jsdval->val);
|
||||
if (!val.isPrimitive()) {
|
||||
JS::RootedObject obj(cx, &val.toObject());
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
obj = JS_ObjectToOuterObject(cx, obj);
|
||||
if (!obj)
|
||||
{
|
||||
JS_ClearPendingException(cx);
|
||||
val = JSVAL_NULL;
|
||||
}
|
||||
else
|
||||
val = JS::ObjectValue(*obj);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static JSDProperty* _newProperty(JSDContext* jsdc, JS::HandleValue propId,
|
||||
JS::HandleValue propValue, JS::HandleValue propAlias,
|
||||
uint8_t propFlags, unsigned additionalFlags)
|
||||
{
|
||||
JSDProperty* jsdprop;
|
||||
|
||||
if(!(jsdprop = (JSDProperty*) calloc(1, sizeof(JSDProperty))))
|
||||
return nullptr;
|
||||
|
||||
JS_INIT_CLIST(&jsdprop->links);
|
||||
jsdprop->nref = 1;
|
||||
jsdprop->flags = propFlags | additionalFlags;
|
||||
|
||||
if(!(jsdprop->name = jsd_NewValue(jsdc, propId)))
|
||||
goto new_prop_fail;
|
||||
|
||||
if(!(jsdprop->val = jsd_NewValue(jsdc, propValue)))
|
||||
goto new_prop_fail;
|
||||
|
||||
if((jsdprop->flags & JSDPD_ALIAS) &&
|
||||
!(jsdprop->alias = jsd_NewValue(jsdc, propAlias)))
|
||||
goto new_prop_fail;
|
||||
|
||||
return jsdprop;
|
||||
new_prop_fail:
|
||||
jsd_DropProperty(jsdc, jsdprop);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void _freeProps(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
JSDProperty* jsdprop;
|
||||
|
||||
while(jsdprop = (JSDProperty*)jsdval->props.next,
|
||||
jsdprop != (JSDProperty*)&jsdval->props)
|
||||
{
|
||||
JS_REMOVE_AND_INIT_LINK(&jsdprop->links);
|
||||
jsd_DropProperty(jsdc, jsdprop);
|
||||
}
|
||||
MOZ_ASSERT(JS_CLIST_IS_EMPTY(&jsdval->props));
|
||||
CLEAR_BIT_FLAG(jsdval->flags, GOT_PROPS);
|
||||
}
|
||||
|
||||
static bool _buildProps(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JS::RootedObject obj(cx);
|
||||
JSPropertyDescArray pda;
|
||||
unsigned i;
|
||||
|
||||
MOZ_ASSERT(JS_CLIST_IS_EMPTY(&jsdval->props));
|
||||
MOZ_ASSERT(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS)));
|
||||
MOZ_ASSERT(!jsdval->val.isPrimitive());
|
||||
|
||||
if(jsdval->val.isPrimitive())
|
||||
return false;
|
||||
|
||||
obj = jsdval->val.toObjectOrNull();
|
||||
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
|
||||
if(!JS_GetPropertyDescArray(cx, obj, &pda))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::RootedValue propId(cx);
|
||||
JS::RootedValue propValue(cx);
|
||||
JS::RootedValue propAlias(cx);
|
||||
uint8_t propFlags;
|
||||
for(i = 0; i < pda.length; i++)
|
||||
{
|
||||
propId = pda.array[i].id;
|
||||
propValue = pda.array[i].value;
|
||||
propAlias = pda.array[i].alias;
|
||||
propFlags = pda.array[i].flags;
|
||||
JSDProperty* prop = _newProperty(jsdc, propId, propValue, propAlias, propFlags, 0);
|
||||
if(!prop)
|
||||
{
|
||||
_freeProps(jsdc, jsdval);
|
||||
break;
|
||||
}
|
||||
JS_APPEND_LINK(&prop->links, &jsdval->props);
|
||||
}
|
||||
JS_PutPropertyDescArray(cx, &pda);
|
||||
SET_BIT_FLAG(jsdval->flags, GOT_PROPS);
|
||||
return !JS_CLIST_IS_EMPTY(&jsdval->props);
|
||||
}
|
||||
|
||||
#undef DROP_CLEAR_VALUE
|
||||
#define DROP_CLEAR_VALUE(jsdc, x) if(x){jsd_DropValue(jsdc,x); x = nullptr;}
|
||||
|
||||
void
|
||||
jsd_RefreshValue(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
if(jsdval->string)
|
||||
{
|
||||
/* if the jsval is a string, then we didn't need to root the string */
|
||||
if(!jsdval->val.isString())
|
||||
{
|
||||
JSAutoCompartment ac(cx, jsdc->glob);
|
||||
JS::RemoveStringRoot(cx, &jsdval->string);
|
||||
}
|
||||
jsdval->string = nullptr;
|
||||
}
|
||||
|
||||
jsdval->funName = nullptr;
|
||||
jsdval->className = nullptr;
|
||||
DROP_CLEAR_VALUE(jsdc, jsdval->proto);
|
||||
DROP_CLEAR_VALUE(jsdc, jsdval->parent);
|
||||
DROP_CLEAR_VALUE(jsdc, jsdval->ctor);
|
||||
_freeProps(jsdc, jsdval);
|
||||
jsdval->flags = 0;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
unsigned
|
||||
jsd_GetCountOfProperties(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
JSDProperty* jsdprop;
|
||||
unsigned count = 0;
|
||||
|
||||
if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS)))
|
||||
if(!_buildProps(jsdc, jsdval))
|
||||
return 0;
|
||||
|
||||
for(jsdprop = (JSDProperty*)jsdval->props.next;
|
||||
jsdprop != (JSDProperty*)&jsdval->props;
|
||||
jsdprop = (JSDProperty*)jsdprop->links.next)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
JSDProperty*
|
||||
jsd_IterateProperties(JSDContext* jsdc, JSDValue* jsdval, JSDProperty **iterp)
|
||||
{
|
||||
JSDProperty* jsdprop = *iterp;
|
||||
if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS)))
|
||||
{
|
||||
MOZ_ASSERT(!jsdprop);
|
||||
if(!_buildProps(jsdc, jsdval))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!jsdprop)
|
||||
jsdprop = (JSDProperty*)jsdval->props.next;
|
||||
if(jsdprop == (JSDProperty*)&jsdval->props)
|
||||
return nullptr;
|
||||
*iterp = (JSDProperty*)jsdprop->links.next;
|
||||
|
||||
MOZ_ASSERT(jsdprop);
|
||||
jsdprop->nref++;
|
||||
return jsdprop;
|
||||
}
|
||||
|
||||
JSDProperty*
|
||||
jsd_GetValueProperty(JSDContext* jsdc, JSDValue* jsdval, JSString* nameStr)
|
||||
{
|
||||
JS::RootedString name(jsdc->jsrt, nameStr);
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoCompartment acBase(cx, jsdc->glob);
|
||||
JSDProperty* jsdprop;
|
||||
JSDProperty* iter = nullptr;
|
||||
JS::RootedObject obj(cx);
|
||||
JS::RootedValue val(cx), nameval(cx);
|
||||
JS::RootedId nameid(cx);
|
||||
JS::RootedValue propId(cx);
|
||||
JS::RootedValue propValue(cx);
|
||||
JS::RootedValue propAlias(cx);
|
||||
uint8_t propFlags;
|
||||
|
||||
if(!jsd_IsValueObject(jsdc, jsdval))
|
||||
return nullptr;
|
||||
|
||||
/* If we already have the prop, then return it */
|
||||
while(nullptr != (jsdprop = jsd_IterateProperties(jsdc, jsdval, &iter)))
|
||||
{
|
||||
JSString* propName = jsd_GetValueString(jsdc, jsdprop->name);
|
||||
if(propName) {
|
||||
int result;
|
||||
if (JS_CompareStrings(cx, propName, name, &result) && !result)
|
||||
return jsdprop;
|
||||
}
|
||||
JSD_DropProperty(jsdc, jsdprop);
|
||||
}
|
||||
/* Not found in property list, look it up explicitly */
|
||||
|
||||
nameval = STRING_TO_JSVAL(name);
|
||||
if(!JS_ValueToId(cx, nameval, &nameid))
|
||||
return nullptr;
|
||||
|
||||
if(!(obj = jsdval->val.toObjectOrNull()))
|
||||
return nullptr;
|
||||
|
||||
JS::Rooted<JSPropertyDescriptor> desc(cx);
|
||||
{
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
JS::RootedId id(cx, nameid);
|
||||
|
||||
if(!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc))
|
||||
return nullptr;
|
||||
if(!desc.object())
|
||||
return nullptr;
|
||||
|
||||
JS_ClearPendingException(cx);
|
||||
|
||||
if(!JS_GetPropertyById(cx, obj, id, &val))
|
||||
{
|
||||
if (JS_IsExceptionPending(cx))
|
||||
{
|
||||
if (!JS_GetPendingException(cx, &propValue))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
propFlags = JSPD_EXCEPTION;
|
||||
}
|
||||
else
|
||||
{
|
||||
propFlags = JSPD_ERROR;
|
||||
propValue = JSVAL_VOID;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
propValue = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (!JS_IdToValue(cx, nameid, &propId))
|
||||
return nullptr;
|
||||
|
||||
propAlias = JSVAL_NULL;
|
||||
propFlags |= desc.isEnumerable() ? JSPD_ENUMERATE : 0
|
||||
| desc.isReadonly() ? JSPD_READONLY : 0
|
||||
| desc.isPermanent() ? JSPD_PERMANENT : 0;
|
||||
|
||||
return _newProperty(jsdc, propId, propValue, propAlias, propFlags, JSDPD_HINTED);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve a JSFunction* from a JSDValue*. This differs from
|
||||
* JS_ValueToFunction by fully unwrapping the object first.
|
||||
*/
|
||||
JSFunction*
|
||||
jsd_GetValueFunction(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
|
||||
JS::RootedObject obj(cx);
|
||||
JS::RootedFunction fun(cx);
|
||||
|
||||
if (jsdval->val.isPrimitive())
|
||||
return nullptr;
|
||||
|
||||
obj = js::UncheckedUnwrap(jsdval->val.toObjectOrNull());
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
JS::RootedValue funval(cx, JS::ObjectValue(*obj));
|
||||
fun = JS_ValueToFunction(cx, funval);
|
||||
|
||||
return fun;
|
||||
}
|
||||
|
||||
JSDValue*
|
||||
jsd_GetValuePrototype(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROTO)))
|
||||
{
|
||||
JS::RootedObject obj(cx);
|
||||
JS::RootedObject proto(cx);
|
||||
MOZ_ASSERT(!jsdval->proto);
|
||||
SET_BIT_FLAG(jsdval->flags, GOT_PROTO);
|
||||
if(jsdval->val.isPrimitive())
|
||||
return nullptr;
|
||||
obj = jsdval->val.toObjectOrNull();
|
||||
if(!JS_GetPrototype(cx, obj, &proto))
|
||||
return nullptr;
|
||||
if(!proto)
|
||||
return nullptr;
|
||||
jsdval->proto = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(proto));
|
||||
}
|
||||
if(jsdval->proto)
|
||||
jsdval->proto->nref++;
|
||||
return jsdval->proto;
|
||||
}
|
||||
|
||||
JSDValue*
|
||||
jsd_GetValueParent(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PARENT)))
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JS::RootedObject obj(cx);
|
||||
JS::RootedObject parent(cx);
|
||||
MOZ_ASSERT(!jsdval->parent);
|
||||
SET_BIT_FLAG(jsdval->flags, GOT_PARENT);
|
||||
if(jsdval->val.isPrimitive())
|
||||
return nullptr;
|
||||
obj = jsdval->val.toObjectOrNull();
|
||||
{
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
parent = JS_GetParentOrScopeChain(cx, obj);
|
||||
}
|
||||
if(!parent)
|
||||
return nullptr;
|
||||
jsdval->parent = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(parent));
|
||||
}
|
||||
if(jsdval->parent)
|
||||
jsdval->parent->nref++;
|
||||
return jsdval->parent;
|
||||
}
|
||||
|
||||
JSDValue*
|
||||
jsd_GetValueConstructor(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_CTOR)))
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JS::RootedObject obj(cx);
|
||||
JS::RootedObject proto(cx);
|
||||
JS::RootedObject ctor(cx);
|
||||
MOZ_ASSERT(!jsdval->ctor);
|
||||
SET_BIT_FLAG(jsdval->flags, GOT_CTOR);
|
||||
if(jsdval->val.isPrimitive())
|
||||
return nullptr;
|
||||
obj = jsdval->val.toObjectOrNull();
|
||||
if(!JS_GetPrototype(cx, obj, &proto))
|
||||
return nullptr;
|
||||
if(!proto)
|
||||
return nullptr;
|
||||
{
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
ctor = JS_GetConstructor(cx, proto);
|
||||
}
|
||||
if(!ctor)
|
||||
return nullptr;
|
||||
jsdval->ctor = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(ctor));
|
||||
}
|
||||
if(jsdval->ctor)
|
||||
jsdval->ctor->nref++;
|
||||
return jsdval->ctor;
|
||||
}
|
||||
|
||||
const char*
|
||||
jsd_GetValueClassName(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
jsval val = jsdval->val;
|
||||
if(!jsdval->className && !val.isPrimitive())
|
||||
{
|
||||
JS::RootedObject obj(jsdc->jsrt, val.toObjectOrNull());
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
jsdval->className = JS_GetDebugClassName(obj);
|
||||
}
|
||||
return jsdval->className;
|
||||
}
|
||||
|
||||
JSDScript*
|
||||
jsd_GetScriptForValue(JSDContext* jsdc, JSDValue* jsdval)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JS::RootedValue val(cx, jsdval->val);
|
||||
JS::RootedScript script(cx);
|
||||
JSDScript* jsdscript;
|
||||
|
||||
if (!jsd_IsValueFunction(jsdc, jsdval))
|
||||
return nullptr;
|
||||
|
||||
{
|
||||
JSAutoCompartment ac(cx, val.toObjectOrNull());
|
||||
AutoSaveExceptionState as(cx);
|
||||
JS::RootedFunction fun(cx, JSD_GetValueFunction(jsdc, jsdval));
|
||||
if (fun)
|
||||
script = JS_GetFunctionScript(cx, fun);
|
||||
}
|
||||
|
||||
if (!script)
|
||||
return nullptr;
|
||||
|
||||
JSD_LOCK_SCRIPTS(jsdc);
|
||||
jsdscript = jsd_FindJSDScript(jsdc, script);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
return jsdscript;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
|
||||
JSDValue*
|
||||
jsd_GetPropertyName(JSDContext* jsdc, JSDProperty* jsdprop)
|
||||
{
|
||||
jsdprop->name->nref++;
|
||||
return jsdprop->name;
|
||||
}
|
||||
|
||||
JSDValue*
|
||||
jsd_GetPropertyValue(JSDContext* jsdc, JSDProperty* jsdprop)
|
||||
{
|
||||
jsdprop->val->nref++;
|
||||
return jsdprop->val;
|
||||
}
|
||||
|
||||
JSDValue*
|
||||
jsd_GetPropertyAlias(JSDContext* jsdc, JSDProperty* jsdprop)
|
||||
{
|
||||
if(jsdprop->alias)
|
||||
jsdprop->alias->nref++;
|
||||
return jsdprop->alias;
|
||||
}
|
||||
|
||||
unsigned
|
||||
jsd_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop)
|
||||
{
|
||||
return jsdprop->flags;
|
||||
}
|
||||
|
||||
void
|
||||
jsd_DropProperty(JSDContext* jsdc, JSDProperty* jsdprop)
|
||||
{
|
||||
MOZ_ASSERT(jsdprop->nref > 0);
|
||||
if(0 == --jsdprop->nref)
|
||||
{
|
||||
MOZ_ASSERT(JS_CLIST_IS_EMPTY(&jsdprop->links));
|
||||
DROP_CLEAR_VALUE(jsdc, jsdprop->val);
|
||||
DROP_CLEAR_VALUE(jsdc, jsdprop->name);
|
||||
DROP_CLEAR_VALUE(jsdc, jsdprop->alias);
|
||||
free(jsdprop);
|
||||
}
|
||||
}
|
3495
js/jsd/jsd_xpc.cpp
3495
js/jsd/jsd_xpc.cpp
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
399
js/jsd/jsd_xpc.h
399
js/jsd/jsd_xpc.h
|
@ -1,399 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 JSDSERVICE_H___
|
||||
#define JSDSERVICE_H___
|
||||
|
||||
#include "jsdIDebuggerService.h"
|
||||
#include "jsdebug.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nspr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
// #if defined(DEBUG_rginda_l)
|
||||
// # define DEBUG_verbose
|
||||
// #endif
|
||||
|
||||
struct LiveEphemeral {
|
||||
/* link in a chain of live values list */
|
||||
PRCList links;
|
||||
jsdIEphemeral *value;
|
||||
void *key;
|
||||
};
|
||||
|
||||
struct PCMapEntry {
|
||||
uint32_t pc, line;
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* reflected jsd data structures
|
||||
*******************************************************************************/
|
||||
|
||||
class jsdObject MOZ_FINAL : public jsdIObject
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_JSDIOBJECT
|
||||
|
||||
/* you'll normally use use FromPtr() instead of directly constructing one */
|
||||
jsdObject (JSDContext *aCx, JSDObject *aObject) :
|
||||
mCx(aCx), mObject(aObject)
|
||||
{
|
||||
}
|
||||
|
||||
static jsdIObject *FromPtr (JSDContext *aCx,
|
||||
JSDObject *aObject)
|
||||
{
|
||||
if (!aObject)
|
||||
return nullptr;
|
||||
|
||||
jsdIObject *rv = new jsdObject (aCx, aObject);
|
||||
NS_IF_ADDREF(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
jsdObject(); /* no implementation */
|
||||
jsdObject(const jsdObject&); /* no implementation */
|
||||
|
||||
~jsdObject() {}
|
||||
|
||||
JSDContext *mCx;
|
||||
JSDObject *mObject;
|
||||
};
|
||||
|
||||
|
||||
class jsdProperty : public jsdIProperty
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_JSDIPROPERTY
|
||||
NS_DECL_JSDIEPHEMERAL
|
||||
|
||||
jsdProperty (JSDContext *aCx, JSDProperty *aProperty);
|
||||
|
||||
static jsdIProperty *FromPtr (JSDContext *aCx,
|
||||
JSDProperty *aProperty)
|
||||
{
|
||||
if (!aProperty)
|
||||
return nullptr;
|
||||
|
||||
jsdIProperty *rv = new jsdProperty (aCx, aProperty);
|
||||
NS_IF_ADDREF(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void InvalidateAll();
|
||||
|
||||
private:
|
||||
jsdProperty(); /* no implementation */
|
||||
jsdProperty(const jsdProperty&); /* no implementation */
|
||||
|
||||
virtual ~jsdProperty ();
|
||||
|
||||
bool mValid;
|
||||
LiveEphemeral mLiveListEntry;
|
||||
JSDContext *mCx;
|
||||
JSDProperty *mProperty;
|
||||
};
|
||||
|
||||
class jsdScript : public jsdIScript
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_JSDISCRIPT
|
||||
NS_DECL_JSDIEPHEMERAL
|
||||
|
||||
/* you'll normally use use FromPtr() instead of directly constructing one */
|
||||
jsdScript (JSDContext *aCx, JSDScript *aScript);
|
||||
|
||||
static jsdIScript *FromPtr (JSDContext *aCx, JSDScript *aScript)
|
||||
{
|
||||
if (!aScript)
|
||||
return nullptr;
|
||||
|
||||
void *data = JSD_GetScriptPrivate (aScript);
|
||||
jsdIScript *rv;
|
||||
|
||||
if (data) {
|
||||
rv = static_cast<jsdIScript *>(data);
|
||||
} else {
|
||||
rv = new jsdScript (aCx, aScript);
|
||||
NS_IF_ADDREF(rv); /* addref for the SetScriptPrivate, released in
|
||||
* Invalidate() */
|
||||
JSD_SetScriptPrivate (aScript, static_cast<void *>(rv));
|
||||
}
|
||||
|
||||
NS_IF_ADDREF(rv); /* addref for return value */
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void InvalidateAll();
|
||||
|
||||
private:
|
||||
virtual ~jsdScript();
|
||||
|
||||
static uint32_t LastTag;
|
||||
|
||||
jsdScript(); /* no implementation */
|
||||
jsdScript (const jsdScript&); /* no implementation */
|
||||
PCMapEntry* CreatePPLineMap();
|
||||
uint32_t PPPcToLine(uint32_t aPC);
|
||||
uint32_t PPLineToPc(uint32_t aLine);
|
||||
|
||||
bool mValid;
|
||||
uint32_t mTag;
|
||||
JSDContext *mCx;
|
||||
JSDScript *mScript;
|
||||
nsCString *mFileName;
|
||||
nsCString *mFunctionName;
|
||||
uint32_t mBaseLineNumber, mLineExtent;
|
||||
PCMapEntry *mPPLineMap;
|
||||
uint32_t mPCMapSize;
|
||||
uintptr_t mFirstPC;
|
||||
};
|
||||
|
||||
uint32_t jsdScript::LastTag = 0;
|
||||
|
||||
class jsdContext : public jsdIContext
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_JSDICONTEXT
|
||||
NS_DECL_JSDIEPHEMERAL
|
||||
|
||||
jsdContext (JSDContext *aJSDCx, JSContext *aJSCx, nsISupports *aISCx);
|
||||
|
||||
static void InvalidateAll();
|
||||
static jsdIContext *FromPtr (JSDContext *aJSDCx, JSContext *aJSCx);
|
||||
private:
|
||||
static uint32_t LastTag;
|
||||
|
||||
jsdContext (); /* no implementation */
|
||||
jsdContext (const jsdContext&); /* no implementation */
|
||||
|
||||
virtual ~jsdContext();
|
||||
|
||||
bool mValid;
|
||||
// The API exposed by JSD here is problematic, because it allows for per-
|
||||
// JSContext script disabling, which no longer exists in the platform.
|
||||
// The only consumer here in practice is Firebug, which makes sure to re-
|
||||
// enable any disabled script before navigation. But if some other consumer
|
||||
// were to disable script, navigate, and try to re-enable it, we'd end up
|
||||
// with an unmatched UnblockScript call, which violates platform invariants.
|
||||
// So we make a half-hearted attempt to detect this by storing the Window ID
|
||||
// of the scope for which we disabled script.
|
||||
uint64_t mScriptDisabledForWindowWithID;
|
||||
bool IsScriptEnabled() { return !mScriptDisabledForWindowWithID; }
|
||||
LiveEphemeral mLiveListEntry;
|
||||
uint32_t mTag;
|
||||
JSDContext *mJSDCx;
|
||||
JSContext *mJSCx;
|
||||
nsCOMPtr<nsISupports> mISCx;
|
||||
};
|
||||
|
||||
uint32_t jsdContext::LastTag = 0;
|
||||
|
||||
class jsdStackFrame : public jsdIStackFrame
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_JSDISTACKFRAME
|
||||
NS_DECL_JSDIEPHEMERAL
|
||||
|
||||
/* you'll normally use use FromPtr() instead of directly constructing one */
|
||||
jsdStackFrame (JSDContext *aCx, JSDThreadState *aThreadState,
|
||||
JSDStackFrameInfo *aStackFrameInfo);
|
||||
|
||||
static void InvalidateAll();
|
||||
static jsdIStackFrame* FromPtr (JSDContext *aCx,
|
||||
JSDThreadState *aThreadState,
|
||||
JSDStackFrameInfo *aStackFrameInfo);
|
||||
|
||||
private:
|
||||
jsdStackFrame(); /* no implementation */
|
||||
jsdStackFrame(const jsdStackFrame&); /* no implementation */
|
||||
|
||||
virtual ~jsdStackFrame();
|
||||
|
||||
bool mValid;
|
||||
LiveEphemeral mLiveListEntry;
|
||||
JSDContext *mCx;
|
||||
JSDThreadState *mThreadState;
|
||||
JSDStackFrameInfo *mStackFrameInfo;
|
||||
};
|
||||
|
||||
class jsdValue : public jsdIValue
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_JSDIVALUE
|
||||
NS_DECL_JSDIEPHEMERAL
|
||||
|
||||
/* you'll normally use use FromPtr() instead of directly constructing one */
|
||||
jsdValue (JSDContext *aCx, JSDValue *aValue);
|
||||
|
||||
static jsdIValue *FromPtr (JSDContext *aCx, JSDValue *aValue);
|
||||
static void InvalidateAll();
|
||||
|
||||
private:
|
||||
virtual ~jsdValue();
|
||||
|
||||
jsdValue(); /* no implementation */
|
||||
jsdValue (const jsdScript&); /* no implementation */
|
||||
|
||||
bool mValid;
|
||||
LiveEphemeral mLiveListEntry;
|
||||
JSDContext *mCx;
|
||||
JSDValue *mValue;
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* debugger service
|
||||
******************************************************************************/
|
||||
|
||||
class jsdService : public jsdIDebuggerService
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_JSDIDEBUGGERSERVICE
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(jsdService)
|
||||
|
||||
jsdService() : mOn(false), mPauseLevel(0),
|
||||
mNestedLoopLevel(0), mCx(0), mRuntime(0), mErrorHook(0),
|
||||
mBreakpointHook(0), mDebugHook(0), mDebuggerHook(0),
|
||||
mInterruptHook(0), mScriptHook(0), mThrowHook(0),
|
||||
mTopLevelHook(0), mFunctionHook(0),
|
||||
mWarnedAboutDeprecation(false),
|
||||
mDeprecationAcknowledged(false)
|
||||
{
|
||||
}
|
||||
|
||||
static jsdService *GetService ();
|
||||
|
||||
bool CheckInterruptHook() { return !!mInterruptHook; }
|
||||
|
||||
nsresult DoPause(uint32_t *_rval, bool internalCall);
|
||||
nsresult DoUnPause(uint32_t *_rval, bool internalCall);
|
||||
|
||||
private:
|
||||
virtual ~jsdService();
|
||||
|
||||
bool mOn;
|
||||
uint32_t mPauseLevel;
|
||||
uint32_t mNestedLoopLevel;
|
||||
JSDContext *mCx;
|
||||
JSRuntime *mRuntime;
|
||||
|
||||
nsCOMPtr<jsdIErrorHook> mErrorHook;
|
||||
nsCOMPtr<jsdIExecutionHook> mBreakpointHook;
|
||||
nsCOMPtr<jsdIExecutionHook> mDebugHook;
|
||||
nsCOMPtr<jsdIExecutionHook> mDebuggerHook;
|
||||
nsCOMPtr<jsdIExecutionHook> mInterruptHook;
|
||||
nsCOMPtr<jsdIScriptHook> mScriptHook;
|
||||
nsCOMPtr<jsdIExecutionHook> mThrowHook;
|
||||
nsCOMPtr<jsdICallHook> mTopLevelHook;
|
||||
nsCOMPtr<jsdICallHook> mFunctionHook;
|
||||
nsCOMPtr<jsdIActivationCallback> mActivationCallback;
|
||||
|
||||
// True if we have ever printed a warning about JSD being deprecated.
|
||||
// We only ever print the warning once.
|
||||
bool mWarnedAboutDeprecation;
|
||||
|
||||
// True if the next call to asyncOn should not produce a warning,
|
||||
// because the consumer called jsdIDebuggerService::acknowledgeDeprecation.
|
||||
bool mDeprecationAcknowledged;
|
||||
};
|
||||
|
||||
#endif /* JSDSERVICE_H___ */
|
||||
|
||||
|
||||
/* graveyard */
|
||||
|
||||
#if 0
|
||||
|
||||
class jsdContext : public jsdIContext
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_JSDICONTEXT
|
||||
|
||||
/* you'll normally use use FromPtr() instead of directly constructing one */
|
||||
jsdContext (JSDContext *aCx) : mCx(aCx)
|
||||
{
|
||||
printf ("++++++ jsdContext\n");
|
||||
}
|
||||
|
||||
static jsdIContext *FromPtr (JSDContext *aCx)
|
||||
{
|
||||
if (!aCx)
|
||||
return nullptr;
|
||||
|
||||
void *data = JSD_GetContextPrivate (aCx);
|
||||
jsdIContext *rv;
|
||||
|
||||
if (data) {
|
||||
rv = static_cast<jsdIContext *>(data);
|
||||
} else {
|
||||
rv = new jsdContext (aCx);
|
||||
NS_IF_ADDREF(rv); // addref for the SetContextPrivate
|
||||
JSD_SetContextPrivate (aCx, static_cast<void *>(rv));
|
||||
}
|
||||
|
||||
NS_IF_ADDREF(rv); // addref for the return value
|
||||
return rv;
|
||||
}
|
||||
|
||||
virtual ~jsdContext() { printf ("------ ~jsdContext\n"); }
|
||||
private:
|
||||
jsdContext(); /* no implementation */
|
||||
jsdContext(const jsdContext&); /* no implementation */
|
||||
|
||||
JSDContext *mCx;
|
||||
};
|
||||
|
||||
class jsdThreadState : public jsdIThreadState
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_JSDITHREADSTATE
|
||||
|
||||
/* you'll normally use use FromPtr() instead of directly constructing one */
|
||||
jsdThreadState (JSDContext *aCx, JSDThreadState *aThreadState) :
|
||||
mCx(aCx), mThreadState(aThreadState)
|
||||
{
|
||||
}
|
||||
|
||||
/* XXX These things are only valid for a short period of time, they reflect
|
||||
* state in the js engine that will go away after stepping past wherever
|
||||
* we were stopped at when this was created. We could keep a list of every
|
||||
* instance of this we've created, and "invalidate" them before we let the
|
||||
* engine continue. The next time we need a threadstate, we can search the
|
||||
* list to find an invalidated one, and just reuse it.
|
||||
*/
|
||||
static jsdIThreadState *FromPtr (JSDContext *aCx,
|
||||
JSDThreadState *aThreadState)
|
||||
{
|
||||
if (!aThreadState)
|
||||
return nullptr;
|
||||
|
||||
jsdIThreadState *rv = new jsdThreadState (aCx, aThreadState);
|
||||
NS_IF_ADDREF(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
jsdThreadState(); /* no implementation */
|
||||
jsdThreadState(const jsdThreadState&); /* no implementation */
|
||||
|
||||
JSDContext *mCx;
|
||||
JSDThreadState *mThreadState;
|
||||
};
|
||||
|
||||
#endif
|
1320
js/jsd/jsdebug.cpp
1320
js/jsd/jsdebug.cpp
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
1524
js/jsd/jsdebug.h
1524
js/jsd/jsdebug.h
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,18 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/* this is all going away... replaced by code in js/jsd/java */
|
||||
|
||||
#if 0
|
||||
|
||||
#include "_stubs/netscape_jsdebug_Script.c"
|
||||
#include "_stubs/netscape_jsdebug_DebugController.c"
|
||||
#include "_stubs/netscape_jsdebug_JSThreadState.c"
|
||||
#include "_stubs/netscape_jsdebug_JSStackFrameInfo.c"
|
||||
#include "_stubs/netscape_jsdebug_JSPC.c"
|
||||
#include "_stubs/netscape_jsdebug_JSSourceTextProvider.c"
|
||||
|
||||
#endif
|
|
@ -1,453 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* PR hash table package.
|
||||
*/
|
||||
|
||||
#include "jshash.h"
|
||||
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "js/Utility.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
using mozilla::CeilingLog2Size;
|
||||
using mozilla::RotateLeft;
|
||||
|
||||
/* Compute the number of buckets in ht */
|
||||
#define NBUCKETS(ht) JS_BIT(JS_HASH_BITS - (ht)->shift)
|
||||
|
||||
/* The smallest table has 16 buckets */
|
||||
#define MINBUCKETSLOG2 4
|
||||
#define MINBUCKETS JS_BIT(MINBUCKETSLOG2)
|
||||
|
||||
/* Compute the maximum entries given n buckets that we will tolerate, ~90% */
|
||||
#define OVERLOADED(n) ((n) - ((n) >> 3))
|
||||
|
||||
/* Compute the number of entries below which we shrink the table by half */
|
||||
#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
|
||||
|
||||
/*
|
||||
** Stubs for default hash allocator ops.
|
||||
*/
|
||||
static void *
|
||||
DefaultAllocTable(void *pool, size_t size)
|
||||
{
|
||||
return js_malloc(size);
|
||||
}
|
||||
|
||||
static void
|
||||
DefaultFreeTable(void *pool, void *item, size_t size)
|
||||
{
|
||||
js_free(item);
|
||||
}
|
||||
|
||||
static JSHashEntry *
|
||||
DefaultAllocEntry(void *pool, const void *key)
|
||||
{
|
||||
return (JSHashEntry*) js_malloc(sizeof(JSHashEntry));
|
||||
}
|
||||
|
||||
static void
|
||||
DefaultFreeEntry(void *pool, JSHashEntry *he, unsigned flag)
|
||||
{
|
||||
if (flag == HT_FREE_ENTRY)
|
||||
js_free(he);
|
||||
}
|
||||
|
||||
static const JSHashAllocOps defaultHashAllocOps = {
|
||||
DefaultAllocTable, DefaultFreeTable,
|
||||
DefaultAllocEntry, DefaultFreeEntry
|
||||
};
|
||||
|
||||
JSHashTable *
|
||||
JS_NewHashTable(uint32_t n, JSHashFunction keyHash,
|
||||
JSHashComparator keyCompare, JSHashComparator valueCompare,
|
||||
const JSHashAllocOps *allocOps, void *allocPriv)
|
||||
{
|
||||
JSHashTable *ht;
|
||||
size_t nb;
|
||||
|
||||
if (n <= MINBUCKETS) {
|
||||
n = MINBUCKETSLOG2;
|
||||
} else {
|
||||
n = CeilingLog2Size(n);
|
||||
if (int32_t(n) < 0)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!allocOps) allocOps = &defaultHashAllocOps;
|
||||
|
||||
ht = (JSHashTable*) allocOps->allocTable(allocPriv, sizeof *ht);
|
||||
if (!ht)
|
||||
return nullptr;
|
||||
memset(ht, 0, sizeof *ht);
|
||||
ht->shift = JS_HASH_BITS - n;
|
||||
n = JS_BIT(n);
|
||||
nb = n * sizeof(JSHashEntry *);
|
||||
ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb);
|
||||
if (!ht->buckets) {
|
||||
allocOps->freeTable(allocPriv, ht, nb);
|
||||
return nullptr;
|
||||
}
|
||||
memset(ht->buckets, 0, nb);
|
||||
|
||||
ht->keyHash = keyHash;
|
||||
ht->keyCompare = keyCompare;
|
||||
ht->valueCompare = valueCompare;
|
||||
ht->allocOps = allocOps;
|
||||
ht->allocPriv = allocPriv;
|
||||
return ht;
|
||||
}
|
||||
|
||||
void
|
||||
JS_HashTableDestroy(JSHashTable *ht)
|
||||
{
|
||||
uint32_t i, n;
|
||||
JSHashEntry *he, **hep;
|
||||
const JSHashAllocOps *allocOps = ht->allocOps;
|
||||
void *allocPriv = ht->allocPriv;
|
||||
|
||||
n = NBUCKETS(ht);
|
||||
for (i = 0; i < n; i++) {
|
||||
hep = &ht->buckets[i];
|
||||
while ((he = *hep) != nullptr) {
|
||||
*hep = he->next;
|
||||
allocOps->freeEntry(allocPriv, he, HT_FREE_ENTRY);
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
|
||||
#endif
|
||||
allocOps->freeTable(allocPriv, ht->buckets, n * sizeof ht->buckets[0]);
|
||||
#ifdef DEBUG
|
||||
memset(ht, 0xDB, sizeof *ht);
|
||||
#endif
|
||||
allocOps->freeTable(allocPriv, ht, sizeof *ht);
|
||||
}
|
||||
|
||||
/*
|
||||
* Multiplicative hash, from Knuth 6.4.
|
||||
*/
|
||||
#define BUCKET_HEAD(ht, keyHash) \
|
||||
(&(ht)->buckets[((keyHash) * JS_GOLDEN_RATIO) >> (ht)->shift])
|
||||
|
||||
JSHashEntry **
|
||||
JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key)
|
||||
{
|
||||
JSHashEntry *he, **hep, **hep0;
|
||||
|
||||
#ifdef JS_HASHMETER
|
||||
ht->nlookups++;
|
||||
#endif
|
||||
hep = hep0 = BUCKET_HEAD(ht, keyHash);
|
||||
while ((he = *hep) != nullptr) {
|
||||
if (he->keyHash == keyHash && ht->keyCompare(key, he->key)) {
|
||||
/* Move to front of chain if not already there */
|
||||
if (hep != hep0) {
|
||||
*hep = he->next;
|
||||
he->next = *hep0;
|
||||
*hep0 = he;
|
||||
}
|
||||
return hep0;
|
||||
}
|
||||
hep = &he->next;
|
||||
#ifdef JS_HASHMETER
|
||||
ht->nsteps++;
|
||||
#endif
|
||||
}
|
||||
return hep;
|
||||
}
|
||||
|
||||
static bool
|
||||
Resize(JSHashTable *ht, uint32_t newshift)
|
||||
{
|
||||
size_t nb, nentries, i;
|
||||
JSHashEntry **oldbuckets, *he, *next, **hep;
|
||||
size_t nold = NBUCKETS(ht);
|
||||
|
||||
MOZ_ASSERT(newshift < JS_HASH_BITS);
|
||||
|
||||
nb = (size_t)1 << (JS_HASH_BITS - newshift);
|
||||
|
||||
/* Integer overflow protection. */
|
||||
if (nb > (size_t)-1 / sizeof(JSHashEntry*))
|
||||
return false;
|
||||
nb *= sizeof(JSHashEntry*);
|
||||
|
||||
oldbuckets = ht->buckets;
|
||||
ht->buckets = (JSHashEntry**)ht->allocOps->allocTable(ht->allocPriv, nb);
|
||||
if (!ht->buckets) {
|
||||
ht->buckets = oldbuckets;
|
||||
return false;
|
||||
}
|
||||
memset(ht->buckets, 0, nb);
|
||||
|
||||
ht->shift = newshift;
|
||||
nentries = ht->nentries;
|
||||
|
||||
for (i = 0; nentries != 0; i++) {
|
||||
for (he = oldbuckets[i]; he; he = next) {
|
||||
MOZ_ASSERT(nentries != 0);
|
||||
--nentries;
|
||||
next = he->next;
|
||||
hep = BUCKET_HEAD(ht, he->keyHash);
|
||||
|
||||
/*
|
||||
* We do not require unique entries, instead appending he to the
|
||||
* chain starting at hep.
|
||||
*/
|
||||
while (*hep)
|
||||
hep = &(*hep)->next;
|
||||
he->next = nullptr;
|
||||
*hep = he;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]);
|
||||
#endif
|
||||
ht->allocOps->freeTable(ht->allocPriv, oldbuckets,
|
||||
nold * sizeof oldbuckets[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSHashEntry *
|
||||
JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **&hep,
|
||||
JSHashNumber keyHash, const void *key, void *value)
|
||||
{
|
||||
uint32_t n;
|
||||
JSHashEntry *he;
|
||||
|
||||
/* Grow the table if it is overloaded */
|
||||
n = NBUCKETS(ht);
|
||||
if (ht->nentries >= OVERLOADED(n)) {
|
||||
if (!Resize(ht, ht->shift - 1))
|
||||
return nullptr;
|
||||
#ifdef JS_HASHMETER
|
||||
ht->ngrows++;
|
||||
#endif
|
||||
hep = JS_HashTableRawLookup(ht, keyHash, key);
|
||||
}
|
||||
|
||||
/* Make a new key value entry */
|
||||
he = ht->allocOps->allocEntry(ht->allocPriv, key);
|
||||
if (!he)
|
||||
return nullptr;
|
||||
he->keyHash = keyHash;
|
||||
he->key = key;
|
||||
he->value = value;
|
||||
he->next = *hep;
|
||||
*hep = he;
|
||||
ht->nentries++;
|
||||
return he;
|
||||
}
|
||||
|
||||
JSHashEntry *
|
||||
JS_HashTableAdd(JSHashTable *ht, const void *key, void *value)
|
||||
{
|
||||
JSHashNumber keyHash;
|
||||
JSHashEntry *he, **hep;
|
||||
|
||||
keyHash = ht->keyHash(key);
|
||||
hep = JS_HashTableRawLookup(ht, keyHash, key);
|
||||
if ((he = *hep) != nullptr) {
|
||||
/* Hit; see if values match */
|
||||
if (ht->valueCompare(he->value, value)) {
|
||||
/* key,value pair is already present in table */
|
||||
return he;
|
||||
}
|
||||
if (he->value)
|
||||
ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_VALUE);
|
||||
he->value = value;
|
||||
return he;
|
||||
}
|
||||
return JS_HashTableRawAdd(ht, hep, keyHash, key, value);
|
||||
}
|
||||
|
||||
void
|
||||
JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he)
|
||||
{
|
||||
uint32_t n;
|
||||
|
||||
*hep = he->next;
|
||||
ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY);
|
||||
|
||||
/* Shrink table if it's underloaded */
|
||||
n = NBUCKETS(ht);
|
||||
if (--ht->nentries < UNDERLOADED(n)) {
|
||||
Resize(ht, ht->shift + 1);
|
||||
#ifdef JS_HASHMETER
|
||||
ht->nshrinks++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
JS_HashTableRemove(JSHashTable *ht, const void *key)
|
||||
{
|
||||
JSHashNumber keyHash;
|
||||
JSHashEntry *he, **hep;
|
||||
|
||||
keyHash = ht->keyHash(key);
|
||||
hep = JS_HashTableRawLookup(ht, keyHash, key);
|
||||
if ((he = *hep) == nullptr)
|
||||
return false;
|
||||
|
||||
/* Hit; remove element */
|
||||
JS_HashTableRawRemove(ht, hep, he);
|
||||
return true;
|
||||
}
|
||||
|
||||
void *
|
||||
JS_HashTableLookup(JSHashTable *ht, const void *key)
|
||||
{
|
||||
JSHashNumber keyHash;
|
||||
JSHashEntry *he, **hep;
|
||||
|
||||
keyHash = ht->keyHash(key);
|
||||
hep = JS_HashTableRawLookup(ht, keyHash, key);
|
||||
if ((he = *hep) != nullptr) {
|
||||
return he->value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Iterate over the entries in the hash table calling func for each
|
||||
** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP).
|
||||
** Return a count of the number of elements scanned.
|
||||
*/
|
||||
int
|
||||
JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg)
|
||||
{
|
||||
JSHashEntry *he, **hep, **bucket;
|
||||
uint32_t nlimit, n, nbuckets, newlog2;
|
||||
int rv;
|
||||
|
||||
nlimit = ht->nentries;
|
||||
n = 0;
|
||||
for (bucket = ht->buckets; n != nlimit; ++bucket) {
|
||||
hep = bucket;
|
||||
while ((he = *hep) != nullptr) {
|
||||
MOZ_ASSERT(n < nlimit);
|
||||
rv = f(he, n, arg);
|
||||
n++;
|
||||
if (rv & HT_ENUMERATE_REMOVE) {
|
||||
*hep = he->next;
|
||||
ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY);
|
||||
--ht->nentries;
|
||||
} else {
|
||||
hep = &he->next;
|
||||
}
|
||||
if (rv & HT_ENUMERATE_STOP) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
/* Shrink table if removal of entries made it underloaded */
|
||||
if (ht->nentries != nlimit) {
|
||||
MOZ_ASSERT(ht->nentries < nlimit);
|
||||
nbuckets = NBUCKETS(ht);
|
||||
if (MINBUCKETS < nbuckets && ht->nentries < UNDERLOADED(nbuckets)) {
|
||||
newlog2 = CeilingLog2Size(ht->nentries);
|
||||
if (newlog2 < MINBUCKETSLOG2)
|
||||
newlog2 = MINBUCKETSLOG2;
|
||||
|
||||
/* Check that we really shrink the table. */
|
||||
MOZ_ASSERT(JS_HASH_BITS - ht->shift > newlog2);
|
||||
Resize(ht, JS_HASH_BITS - newlog2);
|
||||
}
|
||||
}
|
||||
return (int)n;
|
||||
}
|
||||
|
||||
#ifdef JS_HASHMETER
|
||||
#include <stdio.h>
|
||||
|
||||
void
|
||||
JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
|
||||
{
|
||||
double sqsum, mean, sigma;
|
||||
uint32_t nchains, nbuckets;
|
||||
uint32_t i, n, maxChain, maxChainLen;
|
||||
JSHashEntry *he;
|
||||
|
||||
sqsum = 0;
|
||||
nchains = 0;
|
||||
maxChain = maxChainLen = 0;
|
||||
nbuckets = NBUCKETS(ht);
|
||||
for (i = 0; i < nbuckets; i++) {
|
||||
he = ht->buckets[i];
|
||||
if (!he)
|
||||
continue;
|
||||
nchains++;
|
||||
for (n = 0; he; he = he->next)
|
||||
n++;
|
||||
sqsum += n * n;
|
||||
if (n > maxChainLen) {
|
||||
maxChainLen = n;
|
||||
maxChain = i;
|
||||
}
|
||||
}
|
||||
|
||||
mean = JS_MeanAndStdDev(nchains, ht->nentries, sqsum, &sigma);
|
||||
|
||||
fprintf(fp, "\nHash table statistics:\n");
|
||||
fprintf(fp, " number of lookups: %u\n", ht->nlookups);
|
||||
fprintf(fp, " number of entries: %u\n", ht->nentries);
|
||||
fprintf(fp, " number of grows: %u\n", ht->ngrows);
|
||||
fprintf(fp, " number of shrinks: %u\n", ht->nshrinks);
|
||||
fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps
|
||||
/ ht->nlookups);
|
||||
fprintf(fp, "mean hash chain length: %g\n", mean);
|
||||
fprintf(fp, " standard deviation: %g\n", sigma);
|
||||
fprintf(fp, " max hash chain length: %u\n", maxChainLen);
|
||||
fprintf(fp, " max hash chain: [%u]\n", maxChain);
|
||||
|
||||
for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
|
||||
if (dump(he, i, fp) != HT_ENUMERATE_NEXT)
|
||||
break;
|
||||
}
|
||||
#endif /* JS_HASHMETER */
|
||||
|
||||
int
|
||||
JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
|
||||
{
|
||||
int count;
|
||||
|
||||
count = JS_HashTableEnumerateEntries(ht, dump, fp);
|
||||
#ifdef JS_HASHMETER
|
||||
JS_HashTableDumpMeter(ht, dump, fp);
|
||||
#endif
|
||||
return count;
|
||||
}
|
||||
|
||||
JSHashNumber
|
||||
JS_HashString(const void *key)
|
||||
{
|
||||
JSHashNumber h;
|
||||
const unsigned char *s;
|
||||
|
||||
h = 0;
|
||||
for (s = (const unsigned char *)key; *s; s++)
|
||||
h = RotateLeft(h, 4) ^ *s;
|
||||
return h;
|
||||
}
|
||||
|
||||
int
|
||||
JS_CompareValues(const void *v1, const void *v2)
|
||||
{
|
||||
return v1 == v2;
|
||||
}
|
118
js/jsd/jshash.h
118
js/jsd/jshash.h
|
@ -1,118 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 jshash_h___
|
||||
#define jshash_h___
|
||||
|
||||
/*
|
||||
* API to portable hash table code.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
typedef uint32_t JSHashNumber;
|
||||
typedef struct JSHashEntry JSHashEntry;
|
||||
typedef struct JSHashTable JSHashTable;
|
||||
|
||||
#define JS_HASH_BITS 32
|
||||
#define JS_GOLDEN_RATIO 0x9E3779B9U
|
||||
|
||||
typedef JSHashNumber (* JSHashFunction)(const void *key);
|
||||
typedef int (* JSHashComparator)(const void *v1, const void *v2);
|
||||
typedef int (* JSHashEnumerator)(JSHashEntry *he, int i, void *arg);
|
||||
|
||||
/* Flag bits in JSHashEnumerator's return value */
|
||||
#define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */
|
||||
#define HT_ENUMERATE_STOP 1 /* stop enumerating entries */
|
||||
#define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */
|
||||
|
||||
typedef struct JSHashAllocOps {
|
||||
void * (*allocTable)(void *pool, size_t size);
|
||||
void (*freeTable)(void *pool, void *item, size_t size);
|
||||
JSHashEntry * (*allocEntry)(void *pool, const void *key);
|
||||
void (*freeEntry)(void *pool, JSHashEntry *he, unsigned flag);
|
||||
} JSHashAllocOps;
|
||||
|
||||
#define HT_FREE_VALUE 0 /* just free the entry's value */
|
||||
#define HT_FREE_ENTRY 1 /* free value and entire entry */
|
||||
|
||||
struct JSHashEntry {
|
||||
JSHashEntry *next; /* hash chain linkage */
|
||||
JSHashNumber keyHash; /* key hash function result */
|
||||
const void *key; /* ptr to opaque key */
|
||||
void *value; /* ptr to opaque value */
|
||||
};
|
||||
|
||||
struct JSHashTable {
|
||||
JSHashEntry **buckets; /* vector of hash buckets */
|
||||
uint32_t nentries; /* number of entries in table */
|
||||
uint32_t shift; /* multiplicative hash shift */
|
||||
JSHashFunction keyHash; /* key hash function */
|
||||
JSHashComparator keyCompare; /* key comparison function */
|
||||
JSHashComparator valueCompare; /* value comparison function */
|
||||
const JSHashAllocOps *allocOps; /* allocation operations */
|
||||
void *allocPriv; /* allocation private data */
|
||||
#ifdef JS_HASHMETER
|
||||
uint32_t nlookups; /* total number of lookups */
|
||||
uint32_t nsteps; /* number of hash chains traversed */
|
||||
uint32_t ngrows; /* number of table expansions */
|
||||
uint32_t nshrinks; /* number of table contractions */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Create a new hash table.
|
||||
* If allocOps is null, use default allocator ops built on top of malloc().
|
||||
*/
|
||||
extern JSHashTable *
|
||||
JS_NewHashTable(uint32_t n, JSHashFunction keyHash,
|
||||
JSHashComparator keyCompare, JSHashComparator valueCompare,
|
||||
const JSHashAllocOps *allocOps, void *allocPriv);
|
||||
|
||||
extern void
|
||||
JS_HashTableDestroy(JSHashTable *ht);
|
||||
|
||||
/* Low level access methods */
|
||||
extern JSHashEntry **
|
||||
JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key);
|
||||
|
||||
extern JSHashEntry *
|
||||
JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **&hep, JSHashNumber keyHash,
|
||||
const void *key, void *value);
|
||||
|
||||
extern void
|
||||
JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he);
|
||||
|
||||
/* Higher level access methods */
|
||||
extern JSHashEntry *
|
||||
JS_HashTableAdd(JSHashTable *ht, const void *key, void *value);
|
||||
|
||||
extern bool
|
||||
JS_HashTableRemove(JSHashTable *ht, const void *key);
|
||||
|
||||
extern int
|
||||
JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg);
|
||||
|
||||
extern void *
|
||||
JS_HashTableLookup(JSHashTable *ht, const void *key);
|
||||
|
||||
extern int
|
||||
JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp);
|
||||
|
||||
/* General-purpose C string hash function. */
|
||||
extern JSHashNumber
|
||||
JS_HashString(const void *key);
|
||||
|
||||
/* Stub function just returns v1 == v2 */
|
||||
extern int
|
||||
JS_CompareValues(const void *v1, const void *v2);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif /* jshash_h___ */
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче