Mozilla/mozilla/js2/src/plugin.cpp
gerv%gerv.net 912583b021 Bug 236613: change to MPL/LGPL/GPL tri-license.
git-svn-id: svn://10.0.0.236/trunk@155485 18797224-902f-48f8-a5cc-f745e15eee43
2004-04-25 15:37:13 +00:00

610 lines
20 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "mozilla-config.h"
#include <windows.h>
#include <windowsx.h>
#undef GetNextSibling // is defined in VC98/Include/Windowsx.
#include "nsIDOMDocument.h"
#include "nsIDocument.h"
#include <nsXPCOM.h>
#include <nsIXPConnect.h>
#include <nsIServiceManager.h>
#include "xpcprivate.h"
#include "epimetheus.h"
#include "plugin.h"
#include "nsIServiceManager.h"
#include "nsISupportsUtils.h" // some usefule macros are defined here
#include "nsGlobalWindow.h"
//////////////////////////////////////
//
// general initialization and shutdown
//
NPError NS_PluginInitialize()
{
FILE *log = fopen("pandora_log.txt", "a");
fprintf(log, "NS_PluginInitialize\n");
fclose(log);
return NPERR_NO_ERROR;
}
void NS_PluginShutdown()
{
}
//NP_Initialize
/////////////////////////////////////////////////////////////
//
// construction and destruction of our plugin instance object
//
nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct)
{
FILE *log = fopen("pandora_log.txt", "a");
fprintf(log, "NS_NewPluginInstance\n");
if(!aCreateDataStruct) {
fclose(log);
return NULL;
}
nsPluginInstance * plugin = new nsPluginInstance(aCreateDataStruct->instance);
fclose(log);
return plugin;
}
void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin)
{
if(aPlugin)
delete (nsPluginInstance *)aPlugin;
}
////////////////////////////////////////
//
// nsPluginInstance class implementation
//
nsPluginInstance::nsPluginInstance(NPP aInstance) : nsPluginInstanceBase(),
mInstance(aInstance),
mInitialized(FALSE),
mScriptablePeer(NULL)
{
FILE *log = fopen("pandora_log.txt", "a");
fprintf(log, "nsPluginInstance constructor\n");
fclose(log);
mhWnd = NULL;
mString[0] = '\0';
}
nsPluginInstance::~nsPluginInstance()
{
// mScriptablePeer may be also held by the browser
// so releasing it here does not guarantee that it is over
// we should take precaution in case it will be called later
// and zero its mPlugin member
if (mScriptablePeer) {
mScriptablePeer->SetInstance(NULL);
NS_IF_RELEASE(mScriptablePeer);
}
}
static LRESULT CALLBACK PluginWinProc(HWND, UINT, WPARAM, LPARAM);
static WNDPROC lpOldProc = NULL;
using namespace JavaScript;
using namespace MetaData;
void report(Exception x)
{
String s = x.fullMessage();
std::string str(s.length(), char());
std::transform(s.begin(), s.end(), str.begin(), narrow);
printf(str.c_str());
}
NPBool nsPluginInstance::init(NPWindow* aWindow)
{
FILE *log = fopen("pandora_log.txt", "a");
if(aWindow == NULL)
return FALSE;
mhWnd = (HWND)aWindow->window;
if(mhWnd == NULL)
return FALSE;
// subclass window so we can intercept window messages and
// do our drawing to it
lpOldProc = SubclassWindow(mhWnd, (WNDPROC)PluginWinProc);
// associate window with our nsPluginInstance object so we can access
// it in the window procedure
SetWindowLong(mhWnd, GWL_USERDATA, (LONG)this);
fprintf(log, "Pandora: starting up\n");
world = new World();
metadata = new JavaScript::MetaData::JS2Metadata(*world);
spiderMonkeyClass = new (metadata) JS2SpiderMonkeyClass(world->identifiers[widenCString("SpiderMonkey")]);
fprintf(log, "Pandora: done starting up\n");
fclose(log);
mInitialized = TRUE;
return TRUE;
}
void nsPluginInstance::shut()
{
// subclass it back
SubclassWindow(mhWnd, lpOldProc);
mhWnd = NULL;
mInitialized = FALSE;
try {
printf("Pandora: shutting down\n");
if (metadata) {
delete metadata;
metadata = NULL;
}
if (world) {
world->identifiers.clear();
delete world;
world = NULL;
}
printf("Pandora: done shutting down\n");
}
catch (Exception x) {
report(x);
}
}
NPBool nsPluginInstance::isInitialized()
{
return mInitialized;
}
void nsPluginInstance::invoke(const char *target, char **_retval)
{
js2val rval = metadata->invokeFunction(target);
const JavaScript::String *valstr = metadata->toString(rval);
std::string str(valstr->length(), char());
std::transform(valstr->begin(), valstr->end(), str.begin(), narrow);
*_retval = (char *)NPN_MemAlloc(strlen(str.c_str()) + 1);
strcpy(*_retval, str.c_str());
}
void nsPluginInstance::GetDocument(nsISupports * *aDocument)
{
*aDocument = doc;
}
void nsPluginInstance::SetDocument(nsISupports *aDocument)
{
printf("Pandora: setting document\n");
NS_ADDREF(aDocument);
doc = aDocument;
// stash the wrapped JSObject from the document as
// a SpiderMonkeyInstance named 'document' in the (JS2) global object
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if (xpc) {
nsIXPCNativeCallContext *aCurrentNativeCallContext;
if (NS_SUCCEEDED(xpc->GetCurrentNativeCallContext(&aCurrentNativeCallContext))) {
JSContext *aJSContext;
if (NS_SUCCEEDED(aCurrentNativeCallContext->GetJSContext(&aJSContext))) {
nsIXPConnectJSObjectHolder *wrappedDoc;
if (NS_SUCCEEDED(xpc->WrapNative(aJSContext, aJSContext->globalObject, doc, nsIDOMDocument::GetIID(), &wrappedDoc))) {
JSObject *obj;
if (NS_SUCCEEDED(wrappedDoc->GetJSObject(&obj))) {
SpiderMonkeyInstance *smInst = new (metadata) SpiderMonkeyInstance(metadata, metadata->objectClass->prototype, spiderMonkeyClass);
smInst->jsObject = obj;
smInst->pluginInstance = this;
metadata->createDynamicProperty(metadata->glob, "document", OBJECT_TO_JS2VAL(smInst), ReadWriteAccess, true, false);
printf("Pandora: done setting document\n");
}
}
NS_RELEASE(aCurrentNativeCallContext);
}
}
}
}
NPError nsPluginInstance::NewStream(NPMIMEType type, NPStream* stream,
NPBool seekable, uint16* stype)
{
*stype = NP_ASFILEONLY;
return NPERR_NO_ERROR;
}
void nsPluginInstance::StreamAsFile(NPStream* stream, const char* fname)
{
printf("Pandora: compiling source\n");
// compile the file, error reports need to go to the JS console
// if succesful, script level functions get proxies generated
try {
metadata->readEvalFile(fname);
for (LocalBindingIterator bi = metadata->glob->localBindings.begin(),
bend = metadata->glob->localBindings.end(); (bi != bend); bi++) {
LocalBindingEntry *lbe = *bi;
for (LocalBindingEntry::NS_Iterator i = lbe->begin(), end = lbe->end(); (i != end); i++) {
LocalBindingEntry::NamespaceBinding ns = *i;
LocalMember *m = checked_cast<LocalMember *>(ns.second->content);
switch (m->memberKind) {
case Member::FrameVariableMember:
{
FrameVariable *fv = checked_cast<FrameVariable *>(m);
js2val v = (*metadata->glob->frameSlots)[fv->frameSlot];
if (JS2VAL_IS_OBJECT(v)
&& (JS2VAL_TO_OBJECT(v)->kind == SimpleInstanceKind)
&& (checked_cast<SimpleInstance *>(JS2VAL_TO_OBJECT(v))->type == metadata->functionClass)
) {
FunctionInstance *fnInst = checked_cast<FunctionInstance *>(JS2VAL_TO_OBJECT(v));
String fnName = lbe->name;
std::string fstr(fnName.length(), char());
std::transform(fnName.begin(), fnName.end(), fstr.begin(), narrow);
printf("Pandora: adding wrapper for \"%s\"\n", fstr.c_str());
StringFormatter sf;
printFormat(sf, "JavaScript:function %s() { return document.js2.invoke(\"%s\"); }", fstr.c_str(), fstr.c_str());
std::string str(sf.getString().length(), char());
std::transform(sf.getString().begin(), sf.getString().end(), str.begin(), narrow);
NPN_GetURL(mInstance, str.c_str(), NULL);
}
}
break;
}
}
}
printf("Pandora: done compiling source\n");
}
catch (Exception x) {
report(x);
}
}
// ==============================
// ! Scriptability related code !
// ==============================
//
// here the plugin is asked by Mozilla to tell if it is scriptable
// we should return a valid interface id and a pointer to
// nsScriptablePeer interface which we should have implemented
// and which should be defined in the corressponding *.xpt file
// in the bin/components folder
NPError nsPluginInstance::GetValue(NPPVariable aVariable, void *aValue)
{
NPError rv = NPERR_NO_ERROR;
if (aVariable == NPPVpluginScriptableInstance) {
// addref happens in getter, so we don't addref here
nsIPandora * scriptablePeer = getScriptablePeer();
if (scriptablePeer) {
*(nsISupports **)aValue = scriptablePeer;
} else
rv = NPERR_OUT_OF_MEMORY_ERROR;
}
else if (aVariable == NPPVpluginScriptableIID) {
static nsIID scriptableIID = NS_IPANDORA_IID;
nsIID* ptr = (nsIID *)NPN_MemAlloc(sizeof(nsIID));
if (ptr) {
*ptr = scriptableIID;
*(nsIID **)aValue = ptr;
} else
rv = NPERR_OUT_OF_MEMORY_ERROR;
}
return rv;
}
// ==============================
// ! Scriptability related code !
// ==============================
//
// this method will return the scriptable object (and create it if necessary)
nsScriptablePeer* nsPluginInstance::getScriptablePeer()
{
if (!mScriptablePeer) {
mScriptablePeer = new nsScriptablePeer(this);
if(!mScriptablePeer)
return NULL;
NS_ADDREF(mScriptablePeer);
}
// add reference for the caller requesting the object
NS_ADDREF(mScriptablePeer);
return mScriptablePeer;
}
static LRESULT CALLBACK PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_PAINT:
{
/*
// draw a frame and display the string
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rc;
GetClientRect(hWnd, &rc);
FrameRect(hdc, &rc, GetStockBrush(BLACK_BRUSH));
// get our plugin instance object and ask it for the version string
nsPluginInstance *plugin = (nsPluginInstance *)GetWindowLong(hWnd, GWL_USERDATA);
if (plugin)
DrawText(hdc, plugin->mString, strlen(plugin->mString), &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
else {
char string[] = "Error occured";
DrawText(hdc, string, strlen(string), &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
EndPaint(hWnd, &ps);
*/
}
break;
default:
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
using namespace JavaScript;
using namespace MetaData;
js2val callJSFunction(JS2Metadata *meta, FunctionInstance *fnInst, const js2val thisValue, js2val *argv, uint32 argc)
{
printf("JS2SpiderMonkeyClass: callJSFunction \n");
SpiderMonkeyFunction *smFun = checked_cast<SpiderMonkeyFunction *>(fnInst);
nsPluginInstance *plug = smFun->pluginInstance;
jsval *outgoingArgs = NULL;
js2val result = JS2VAL_VOID;
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if (xpc) {
nsIXPCNativeCallContext *aCurrentNativeCallContext;
if (NS_SUCCEEDED(xpc->GetCurrentNativeCallContext(&aCurrentNativeCallContext))) {
JSContext *aJSContext;
if (NS_SUCCEEDED(aCurrentNativeCallContext->GetJSContext(&aJSContext))) {
if (argc) {
outgoingArgs = new jsval[argc];
for (uint32 i = 0; i < argc; i++) {
if (!plug->convertJS2ValueToJSValue(aJSContext, argv[i], &outgoingArgs[i]))
goto out;
}
}
jsval resultVal;
jsval thisVal;
if (!plug->convertJS2ValueToJSValue(aJSContext, thisValue, &thisVal))
goto out;
ASSERT(JSVAL_IS_OBJECT(thisVal));
if (JS_CallFunctionValue(aJSContext, JSVAL_TO_OBJECT(thisVal), OBJECT_TO_JSVAL(smFun->jsObject), argc, outgoingArgs, &resultVal))
plug->convertJSValueToJS2Value(aJSContext, resultVal, &result);
NS_RELEASE(aCurrentNativeCallContext);
}
}
}
out:
if (outgoingArgs)
delete [] outgoingArgs;
return result;
}
bool nsPluginInstance::convertJSValueToJS2Value(JSContext *cx, jsval v, js2val *rval)
{
bool result = true;
if (JSVAL_IS_INT(v))
*rval = INT_TO_JS2VAL(JSVAL_TO_INT(v));
else
if (JSVAL_IS_NUMBER(v))
*rval = metadata->engine->allocNumber(*JSVAL_TO_DOUBLE(v));
else
if (JSVAL_IS_STRING(v))
*rval = metadata->engine->allocString(JS_GetStringChars(JSVAL_TO_STRING(v)), JS_GetStringLength(JSVAL_TO_STRING(v)));
else
if (JSVAL_IS_BOOLEAN(v))
*rval = BOOLEAN_TO_JS2VAL(JSVAL_TO_BOOLEAN(v));
else
if (JSVAL_IS_NULL(v))
*rval = JS2VAL_NULL;
else
if (JSVAL_IS_VOID(v))
*rval = JS2VAL_VOID;
else
if (JSVAL_IS_OBJECT(v)) {
JSObject *jsObj = JSVAL_TO_OBJECT(v);
if (JS_ObjectIsFunction(cx, jsObj)) {
SpiderMonkeyFunction *smFun = new (metadata) SpiderMonkeyFunction(metadata);
JS2Object *rooter = smFun;
DEFINE_ROOTKEEPER(metadata, rk, rooter);
smFun->fWrap = new FunctionWrapper(metadata, true, new (metadata) ParameterFrame(JS2VAL_VOID, true), callJSFunction, metadata->env);
smFun->fWrap->length = 0;
smFun->jsObject = jsObj;
smFun->pluginInstance = this;
*rval = OBJECT_TO_JS2VAL(smFun);
}
else {
SpiderMonkeyInstance *smInst = new (metadata) SpiderMonkeyInstance(metadata, metadata->objectClass->prototype, spiderMonkeyClass);
smInst->jsObject = jsObj;
smInst->pluginInstance = this;
*rval = OBJECT_TO_JS2VAL(smInst);
}
}
else
result = false;
return result;
}
bool nsPluginInstance::convertJS2ValueToJSValue(JSContext *cx, js2val v, jsval *rval)
{
bool result = true;
if (JS2VAL_IS_INT(v))
*rval = INT_TO_JSVAL(JS2VAL_TO_INT(v));
else
if (JS2VAL_IS_NUMBER(v))
*rval = DOUBLE_TO_JSVAL(JS_NewDouble(cx, *JS2VAL_TO_DOUBLE(v)));
else
if (JS2VAL_IS_STRING(v)) {
String *str = JS2VAL_TO_STRING(v);
*rval = STRING_TO_JSVAL(JS_NewUCStringCopyN(cx, str->data(), str->length()));
}
else
if (JS2VAL_IS_BOOLEAN(v))
*rval = BOOLEAN_TO_JSVAL(JS2VAL_TO_BOOLEAN(v));
else
if (JS2VAL_IS_NULL(v))
*rval = JSVAL_NULL;
else
if (JS2VAL_IS_VOID(v))
*rval = JSVAL_VOID;
else
if (JS2VAL_IS_OBJECT(v)) {
JS2Object *js2Obj = JS2VAL_TO_OBJECT(v);
ASSERT(js2Obj->kind == SimpleInstanceKind);
ASSERT(checked_cast<SimpleInstance *>(js2Obj)->type == spiderMonkeyClass);
SpiderMonkeyInstance *smInst = checked_cast<SpiderMonkeyInstance *>(js2Obj);
*rval = OBJECT_TO_JSVAL(smInst->jsObject);
}
else
result = false;
return result;
}
bool JS2SpiderMonkeyClass::Read(JS2Metadata *meta, js2val *base, Multiname *multiname, Environment *env, Phase phase, js2val *rval)
{
std::string str(multiname->name.length(), char());
std::transform(multiname->name.begin(), multiname->name.end(), str.begin(), narrow);
printf("JS2SpiderMonkeyClass: Reading property \"%s\"\n", str.c_str());
ASSERT(JS2VAL_IS_OBJECT(*base) && !JS2VAL_IS_NULL(*base));
JS2Object *obj = JS2VAL_TO_OBJECT(*base);
ASSERT(obj->kind == SimpleInstanceKind);
SpiderMonkeyInstance *smInst = checked_cast<SpiderMonkeyInstance *>(obj);
nsPluginInstance *plug = smInst->pluginInstance;
bool result = false;
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if (xpc) {
nsIXPCNativeCallContext *aCurrentNativeCallContext;
if (NS_SUCCEEDED(xpc->GetCurrentNativeCallContext(&aCurrentNativeCallContext))) {
JSContext *aJSContext;
if (NS_SUCCEEDED(aCurrentNativeCallContext->GetJSContext(&aJSContext))) {
jsval v;
if (JS_GetProperty(aJSContext, smInst->jsObject, str.c_str(), &v))
result = plug->convertJSValueToJS2Value(aJSContext, v, rval);
NS_RELEASE(aCurrentNativeCallContext);
}
}
}
return result;
}
bool JS2SpiderMonkeyClass::BracketRead(JS2Metadata *meta, js2val *base, js2val indexVal, Phase phase, js2val *rval)
{
return false;
}
bool JS2SpiderMonkeyClass::Write(JS2Metadata *meta, js2val base, Multiname *multiname, Environment *env, bool createIfMissing, js2val newValue, bool initFlag)
{
std::string str(multiname->name.length(), char());
std::transform(multiname->name.begin(), multiname->name.end(), str.begin(), narrow);
printf("JS2SpiderMonkeyClass: Writing property \"%s\"\n", str.c_str());
ASSERT(JS2VAL_IS_OBJECT(base) && !JS2VAL_IS_NULL(base));
JS2Object *obj = JS2VAL_TO_OBJECT(base);
ASSERT(obj->kind == SimpleInstanceKind);
SpiderMonkeyInstance *smInst = checked_cast<SpiderMonkeyInstance *>(obj);
nsPluginInstance *plug = smInst->pluginInstance;
bool result = false;
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if (xpc) {
nsIXPCNativeCallContext *aCurrentNativeCallContext;
if (NS_SUCCEEDED(xpc->GetCurrentNativeCallContext(&aCurrentNativeCallContext))) {
JSContext *aJSContext;
if (NS_SUCCEEDED(aCurrentNativeCallContext->GetJSContext(&aJSContext))) {
jsval v;
if (plug->convertJS2ValueToJSValue(aJSContext, newValue, &v)) {
if (JS_SetProperty(aJSContext, smInst->jsObject, str.c_str(), &v))
result = true;
}
NS_RELEASE(aCurrentNativeCallContext);
}
}
}
return result;
}
bool JS2SpiderMonkeyClass::BracketWrite(JS2Metadata *meta, js2val base, js2val indexVal, js2val newValue)
{
return false;
}
bool JS2SpiderMonkeyClass::Delete(JS2Metadata *meta, js2val base, Multiname *multiname, Environment *env, bool *result)
{
return false;
}
bool JS2SpiderMonkeyClass::BracketDelete(JS2Metadata *meta, js2val base, js2val indexVal, bool *result)
{
return false;
}