2191 lines
60 KiB
C++
2191 lines
60 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
// All the code-resource handling stuff has been adopted from:
|
|
// Multi-Seg CR example in CW
|
|
// Classes:
|
|
// CPluginInstance - one per plugin instance. Needs to call the functions
|
|
// CPluginHandler - one per plugin MIME type
|
|
|
|
#include "CHTMLView.h"
|
|
|
|
#include "macutil.h"
|
|
#include "npapi.h"
|
|
#include "mplugin.h"
|
|
#include "uprefd.h"
|
|
#include "ufilemgr.h"
|
|
#include "uapp.h" // might be unnecessary when we get rid of CFrontApp
|
|
#include "resgui.h"
|
|
#include "miconutils.h"
|
|
#include "uerrmgr.h"
|
|
#include "macgui.h"
|
|
#include "resae.h"
|
|
#include "edt.h" // for EDT_RegisterPlugin()
|
|
#include "CApplicationEventAttachment.h"
|
|
|
|
#if defined(JAVA)
|
|
#include "java.h" // for LJ_AddToClassPath
|
|
#endif
|
|
|
|
#include "np.h"
|
|
#include "nppg.h"
|
|
#include "prlink.h"
|
|
|
|
#include "npglue.h"
|
|
#include "nsIEventHandler.h"
|
|
|
|
#ifdef LAYERS
|
|
#include "layers.h"
|
|
#include "mkutils.h"
|
|
#endif // LAYERS
|
|
|
|
#include "MixedMode.h"
|
|
#include "MoreMixedMode.h"
|
|
#include <CodeFragments.h>
|
|
|
|
#include "LMenuSharing.h"
|
|
#include "CBrowserWindow.h"
|
|
|
|
// On a powerPC, plugins depend on being able to find qd in our symbol table
|
|
#ifdef powerc
|
|
// #pragma export qd
|
|
// beard: this seems to be an illegal pragma
|
|
#endif
|
|
|
|
const ResIDT nsPluginSig = 128; // STR#, MIME type is #1
|
|
|
|
struct _np_handle;
|
|
|
|
|
|
// *************************************************************************************
|
|
//
|
|
// class CPluginHandler
|
|
//
|
|
// *************************************************************************************
|
|
|
|
class CPluginHandler
|
|
{
|
|
friend void FE_UnloadPlugin(void* plugin, struct _np_handle* handle);
|
|
friend NPPluginFuncs* FE_LoadPlugin(void* plugin, NPNetscapeFuncs *, struct _np_handle* handle);
|
|
|
|
public:
|
|
|
|
// ¥¥ constructors
|
|
CPluginHandler(FSSpec& plugFile);
|
|
~CPluginHandler();
|
|
|
|
// ¥¥ xp->macfe
|
|
static Boolean CheckExistingHandlers(FSSpec& file);
|
|
NPPluginFuncs* LoadPlugin(NPNetscapeFuncs * funcs, _np_handle* handle);
|
|
void UnloadPlugin();
|
|
|
|
// plugin -> netscape
|
|
static void Status(const char * message);
|
|
|
|
static CPluginHandler* FindHandlerForPlugin(const CStr255& pluginName);
|
|
|
|
private:
|
|
|
|
// ¥¥ funcs
|
|
OSErr InitCodeResource(NPNetscapeFuncs * funcs, _np_handle* handle);
|
|
void CloseCodeResource(_np_handle* handle = NULL);
|
|
void ResetPlugFuncTable();
|
|
void OpenPluginResourceFork(Int16 inPrivileges);
|
|
|
|
// ¥¥ vars
|
|
short fRefCount;
|
|
PRLibrary* fLibrary; // For PPC
|
|
Handle fCode; // For 68K
|
|
NPPluginFuncs fPluginFuncs; // xp->plugin
|
|
NPP_MainEntryUPP fMainEntryFunc;
|
|
NPP_ShutdownUPP fUnloadFunc;
|
|
LFile* fFile; // File containing the plugin
|
|
CStr255 fPluginName; // Name of plug-in
|
|
|
|
CPluginHandler* fNextHandler; // Link to next handler in the global list
|
|
static CPluginHandler* sHandlerList; // Global list of all handlers
|
|
};
|
|
|
|
CPluginHandler* CPluginHandler::sHandlerList = NULL;
|
|
|
|
void* GetPluginDesc(const CStr255& pluginName)
|
|
{
|
|
return CPluginHandler::FindHandlerForPlugin(pluginName);
|
|
}
|
|
|
|
//
|
|
// Find a handler given a plug-in name (currently only
|
|
// called from CFrontApp:RegisterMimeType).
|
|
//
|
|
CPluginHandler* CPluginHandler::FindHandlerForPlugin(const CStr255& pluginName)
|
|
{
|
|
CPluginHandler* handler = CPluginHandler::sHandlerList;
|
|
while (handler != NULL)
|
|
{
|
|
if (handler->fPluginName == pluginName)
|
|
return handler;
|
|
|
|
handler = handler->fNextHandler;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
|
|
//
|
|
// Check the handlers we already have registered against
|
|
// the given file spec. We donÕt want to redundantly
|
|
// create handlers for the same plugin file.
|
|
//
|
|
Boolean CPluginHandler::CheckExistingHandlers(FSSpec& file)
|
|
{
|
|
CPluginHandler* handler = CPluginHandler::sHandlerList;
|
|
while (handler != NULL)
|
|
{
|
|
if (handler->fFile)
|
|
{
|
|
FSSpec existingFile;
|
|
handler->fFile->GetSpecifier(existingFile);
|
|
if (existingFile.vRefNum == file.vRefNum && existingFile.parID == file.parID)
|
|
{
|
|
if (EqualString(existingFile.name, file.name, true, true))
|
|
return false;
|
|
}
|
|
}
|
|
handler = handler->fNextHandler;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// ¥¥ constructors
|
|
|
|
CPluginHandler::CPluginHandler(FSSpec& plugFile)
|
|
{
|
|
// Variables
|
|
fRefCount = 0;
|
|
fCode = NULL;
|
|
fLibrary = NULL;
|
|
fFile = NULL;
|
|
fMainEntryFunc = NULL;
|
|
fUnloadFunc = NULL;
|
|
fNextHandler = NULL;
|
|
fPluginName = "";
|
|
|
|
Try_
|
|
{
|
|
fFile = new LFile(plugFile);
|
|
fFile->OpenResourceFork(fsRdPerm);
|
|
|
|
// Look for the plug-in description resource
|
|
CStringListRsrc descRsrc(126);
|
|
CStr255 pluginDescription;
|
|
|
|
// Read plug-in description if available; if not, use blank description
|
|
short descCount = descRsrc.CountStrings();
|
|
if (descCount > 0)
|
|
descRsrc.GetString(1, pluginDescription);
|
|
if (descCount <= 0 || ResError())
|
|
pluginDescription = "";
|
|
|
|
// Read plug-in name if available; if not, use file name
|
|
if (descCount > 1)
|
|
descRsrc.GetString(2, fPluginName);
|
|
if (descCount <= 1 || ResError())
|
|
fPluginName = plugFile.name;
|
|
|
|
// cstring fileName((const unsigned char*) plugFile.name);
|
|
char* fileName = CFileMgr::EncodedPathNameFromFSSpec(plugFile, TRUE);
|
|
ThrowIfNil_(fileName);
|
|
fileName = NET_UnEscape(fileName);
|
|
NPError err = NPL_RegisterPluginFile(fPluginName, fileName, pluginDescription, this);
|
|
XP_FREE(fileName);
|
|
ThrowIfOSErr_(err);
|
|
|
|
CStringListRsrc mimeList(128);
|
|
CStringListRsrc descList(127);
|
|
|
|
UInt32 mimeTotal = mimeList.CountStrings() >> 1; // 2 strings per entry
|
|
UInt32 descTotal = descList.CountStrings();
|
|
UInt32 count, mimeIndex, descIndex;
|
|
ThrowIf_(mimeTotal == 0);
|
|
|
|
for (count = mimeIndex = descIndex = 1; count <= mimeTotal; count++, mimeIndex += 2, descIndex++)
|
|
{
|
|
CStr255 mimeType;
|
|
CStr255 extensions;
|
|
CStr255 description;
|
|
|
|
mimeList.GetString(mimeIndex, mimeType);
|
|
if (ResError())
|
|
continue;
|
|
|
|
mimeList.GetString(mimeIndex + 1, extensions);
|
|
if (ResError())
|
|
continue;
|
|
|
|
//
|
|
// Get the description string from the plug-in.
|
|
// If they didn't specify one, use a pre-existing
|
|
// description if it exists (e.g. we know by default
|
|
// that video/quicktime is "Quicktime Video"). If
|
|
// we still didn't find a description, just use the
|
|
// MIME type.
|
|
//
|
|
Boolean gotDescription = false;
|
|
if (descIndex <= descTotal)
|
|
{
|
|
descList.GetString(descIndex, description);
|
|
gotDescription = (ResError() == noErr);
|
|
}
|
|
if (!gotDescription)
|
|
{
|
|
NET_cdataStruct temp;
|
|
NET_cdataStruct* cdata;
|
|
char* mimetype = (char*) mimeType;
|
|
|
|
memset(&temp, 0, sizeof(temp));
|
|
temp.ci.type = mimetype;
|
|
cdata = NET_cdataExist(&temp);
|
|
if (cdata && cdata->ci.desc)
|
|
description = cdata->ci.desc;
|
|
else
|
|
description = mimeType;
|
|
}
|
|
|
|
//
|
|
// Look up this type in the MIME table. If it's not found, then
|
|
// the user hasn't seen this plug-in type before, so make a new
|
|
// mapper for the table with the type defaulted to be handled by
|
|
// the plug-in. If it was found, but was from an old prefs file
|
|
// (that didn't contain plug-in information), make sure the plug-
|
|
// in still gets priority so users don't freak out the first time
|
|
// they run a new Navigator and the plug-ins stop working. If it
|
|
// was found but was latent (disabled because the plug-in was
|
|
// missing), re-enable it now because the plug-in is present again.
|
|
//
|
|
cstring type((const unsigned char*) mimeType);
|
|
CMimeMapper* mapper = CPrefs::sMimeTypes.FindMimeType(type);
|
|
if (mapper == NULL)
|
|
{
|
|
mapper = new CMimeMapper(CMimeMapper::Plugin, mimeType, "", extensions, '????', '????');
|
|
ThrowIfNil_(mapper);
|
|
mapper->SetPluginName(fPluginName);
|
|
mapper->SetDescription(description);
|
|
CPrefs::sMimeTypes.InsertItemsAt(1, LArray::index_Last, &mapper);
|
|
CPrefs::SetModified();
|
|
mapper->WriteMimePrefs(); // convert mapper to xp prefs
|
|
}
|
|
else if (mapper->FromOldPrefs())
|
|
{
|
|
mapper->SetPluginName(fPluginName);
|
|
mapper->SetExtensions(extensions);
|
|
mapper->SetDescription(description);
|
|
mapper->SetLoadAction(CMimeMapper::Plugin);
|
|
CPrefs::SetModified();
|
|
mapper->WriteMimePrefs(); // convert mapper to xp prefs
|
|
}
|
|
else if (mapper->LatentPlugin())
|
|
{
|
|
XP_ASSERT(mapper->GetLoadAction() == CMimeMapper::Unknown);
|
|
mapper->SetLoadAction(CMimeMapper::Plugin);
|
|
CPrefs::SetModified();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Only update the description if (a) thereÕs a description
|
|
// from the plug-in itself (as opposed to a default description),
|
|
// and (b) the current description is blank (we donÕt want to
|
|
// overwrite a user-edited description).
|
|
//
|
|
if (gotDescription && mapper->GetDescription() == "")
|
|
{
|
|
mapper->SetDescription(description);
|
|
CPrefs::SetModified();
|
|
}
|
|
}
|
|
|
|
// Now we know whether the plug-in should be enabled or not
|
|
Boolean enabled = ((mapper->GetLoadAction() == CMimeMapper::Plugin) &&
|
|
(fPluginName == mapper->GetPluginName()));
|
|
NPL_RegisterPluginType(type, (char*) mapper->GetExtensions(), (char*) mapper->GetDescription(), NULL, this, enabled);
|
|
}
|
|
|
|
fFile->CloseResourceFork();
|
|
|
|
}
|
|
Catch_(inErr)
|
|
{
|
|
if (fFile != NULL)
|
|
fFile->CloseResourceFork();
|
|
|
|
}
|
|
EndCatch_;
|
|
|
|
ResetPlugFuncTable();
|
|
|
|
// Link us into the global list of handlers
|
|
fNextHandler = CPluginHandler::sHandlerList;
|
|
CPluginHandler::sHandlerList = this;
|
|
}
|
|
|
|
|
|
CPluginHandler::~CPluginHandler() // This code never gets called
|
|
{
|
|
// CloseCodeResource();
|
|
|
|
if (fFile)
|
|
delete fFile;
|
|
|
|
if (this == CPluginHandler::sHandlerList)
|
|
CPluginHandler::sHandlerList = NULL;
|
|
else
|
|
{
|
|
CPluginHandler* handler = CPluginHandler::sHandlerList;
|
|
while (handler != NULL)
|
|
{
|
|
if (this == handler->fNextHandler)
|
|
{
|
|
handler->fNextHandler = this->fNextHandler;
|
|
break;
|
|
}
|
|
handler = handler->fNextHandler;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CPluginHandler::ResetPlugFuncTable()
|
|
{
|
|
fMainEntryFunc = NULL;
|
|
fUnloadFunc = NULL;
|
|
memset(&fPluginFuncs, 0, sizeof(NPPluginFuncs));
|
|
}
|
|
|
|
|
|
NPPluginFuncs * CPluginHandler::LoadPlugin(NPNetscapeFuncs * funcs, _np_handle* handle)
|
|
{
|
|
// XXX Needs to be fixed for C++ Plugin API
|
|
OSErr err = InitCodeResource(funcs, handle);
|
|
if (err == noErr)
|
|
return &fPluginFuncs;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
OSErr CPluginHandler::InitCodeResource(NPNetscapeFuncs* funcs, _np_handle* handle)
|
|
{
|
|
fRefCount++;
|
|
|
|
if (fCode != NULL || fLibrary != 0) // Already loaded
|
|
return noErr;
|
|
|
|
OSErr err = noErr;
|
|
#ifdef DEBUG
|
|
EDebugAction oldThrow = UDebugging::gDebugThrow;
|
|
EDebugAction oldSignal = UDebugging::gDebugSignal;
|
|
UDebugging::gDebugThrow = debugAction_Nothing;
|
|
UDebugging::gDebugSignal = debugAction_Nothing;
|
|
#endif
|
|
Try_
|
|
{
|
|
OpenPluginResourceFork(fsRdWrPerm);
|
|
Try_
|
|
{ // PPC initialization
|
|
|
|
if (!UEnvironment::HasGestaltAttribute(gestaltCFMAttr, gestaltCFMPresent))
|
|
Throw_((OSErr)cfragNoLibraryErr); // No CFM
|
|
|
|
FSSpec fileSpec;
|
|
fFile->GetSpecifier(fileSpec);
|
|
|
|
char* cFullPath = CFileMgr::PathNameFromFSSpec(fileSpec, TRUE);
|
|
ThrowIfNil_(cFullPath);
|
|
|
|
fLibrary = PR_LoadLibrary(cFullPath);
|
|
ThrowIfNil_(fLibrary);
|
|
|
|
// PCB: Let's factor this into an XP function, please!
|
|
nsFactoryProc nsGetFactory = (nsFactoryProc) PR_FindSymbol(fLibrary, "NSGetFactory");
|
|
if (nsGetFactory != NULL) {
|
|
nsresult res = NS_OK;
|
|
if (thePluginManager == NULL) {
|
|
// For now, create the plugin manager on demand.
|
|
static NS_DEFINE_IID(kIPluginManagerIID, NS_IPLUGINMANAGER_IID);
|
|
res = nsPluginManager::Create(NULL, kIPluginManagerIID, (void**)&thePluginManager);
|
|
ThrowIf_(res != NS_OK || thePluginManager == NULL);
|
|
}
|
|
static NS_DEFINE_IID(kIPluginIID, NS_IPLUGIN_IID);
|
|
nsIPlugin* plugin = NULL;
|
|
res = nsGetFactory(kIPluginIID, (nsIFactory**)&plugin);
|
|
ThrowIf_(res != NS_OK || plugin == NULL);
|
|
// beard: establish the primary reference.
|
|
plugin->AddRef();
|
|
res = plugin->Initialize((nsIPluginManager2*)thePluginManager);
|
|
ThrowIf_(res != NS_OK);
|
|
handle->userPlugin = plugin;
|
|
} else {
|
|
fMainEntryFunc = (NPP_MainEntryUPP) PR_FindSymbol(fLibrary, "mainRD");
|
|
ThrowIfNil_(fMainEntryFunc);
|
|
}
|
|
}
|
|
Catch_(inErr)
|
|
{ // 68K if PPC fails
|
|
fLibrary = NULL; // No PPC
|
|
fCode = Get1Resource(emPluginFile, 128);
|
|
ThrowIfNil_(fCode);
|
|
::MoveHHi(fCode);
|
|
::HNoPurge(fCode);
|
|
::HLock(fCode);
|
|
|
|
//get the address of main and typecast it into a MainEntryProcUPP
|
|
fMainEntryFunc = (NPP_MainEntryUPP)(*fCode);
|
|
} EndCatch_
|
|
|
|
if (fMainEntryFunc != NULL) {
|
|
fPluginFuncs.version = funcs->version;
|
|
fPluginFuncs.size = sizeof(NPPluginFuncs);
|
|
err = CallNPP_MainEntryProc(fMainEntryFunc, funcs, &fPluginFuncs, &fUnloadFunc);
|
|
ThrowIfOSErr_(err);
|
|
if ((fPluginFuncs.version >> 8) < NP_VERSION_MAJOR)
|
|
Throw_((OSErr)NPERR_INCOMPATIBLE_VERSION_ERROR);
|
|
}
|
|
}
|
|
Catch_(inErr)
|
|
{
|
|
CloseCodeResource();
|
|
err = inErr;
|
|
}
|
|
EndCatch_
|
|
CPrefs::UseApplicationResFile(); // Revert the resource chain
|
|
#ifdef DEBUG
|
|
UDebugging::gDebugThrow = oldThrow;
|
|
UDebugging::gDebugSignal = oldSignal;
|
|
#endif
|
|
return err;
|
|
}
|
|
|
|
// Dispose of the code
|
|
void CPluginHandler::CloseCodeResource(_np_handle* handle)
|
|
{
|
|
// Already unloaded?
|
|
if (fRefCount == 0)
|
|
return;
|
|
|
|
// If there are still outstanding references, don't unload yet
|
|
fRefCount--;
|
|
if (fRefCount > 0)
|
|
return;
|
|
|
|
if (handle != NULL) {
|
|
nsIPlugin* userPlugin = handle->userPlugin;
|
|
if (userPlugin != NULL) {
|
|
userPlugin->Release();
|
|
handle->userPlugin = NULL;
|
|
}
|
|
} else
|
|
if (fUnloadFunc != NULL)
|
|
CallNPP_ShutdownProc(fUnloadFunc);
|
|
|
|
// Dispose of the code
|
|
// 68K, just release the resource
|
|
if (fCode != NULL)
|
|
{
|
|
::HUnlock(fCode);
|
|
::HPurge(fCode);
|
|
::ReleaseResource(fCode);
|
|
fCode = NULL;
|
|
}
|
|
// PPC, close the connections
|
|
if (fLibrary != NULL)
|
|
{
|
|
int err = PR_UnloadLibrary(fLibrary);
|
|
Assert_(err == 0);
|
|
fLibrary = NULL;
|
|
}
|
|
|
|
ResetPlugFuncTable();
|
|
|
|
Try_
|
|
{
|
|
fFile->CloseResourceFork();
|
|
}
|
|
Catch_(inErr){} EndCatch_
|
|
Try_
|
|
{
|
|
fFile->CloseDataFork();
|
|
}
|
|
Catch_(inErr){} EndCatch_
|
|
}
|
|
|
|
|
|
typedef struct ResourceMapEntry ResourceMapEntry, **ResourceMapHandle;
|
|
|
|
// See More Macintosh Toolbox, pp. 1-122, 1-123.
|
|
#if PRAGMA_ALIGN_SUPPORTED
|
|
#pragma options align=mac68k
|
|
#endif
|
|
struct ResourceMapEntry
|
|
{
|
|
UInt32 dataOffset;
|
|
UInt32 mapOffset;
|
|
UInt32 dataLength;
|
|
UInt32 mapLength;
|
|
ResourceMapHandle nextMap;
|
|
UInt16 refNum;
|
|
};
|
|
#if PRAGMA_ALIGN_SUPPORTED
|
|
#pragma options align=reset
|
|
#endif
|
|
|
|
//
|
|
// Open the plug-inÕs resource fork BELOW the the applicationÕs
|
|
// resource fork. If we just opened the plug-in above the
|
|
// application but set the current res file to the app, the
|
|
// plug-in will be made current by the Resource Manager if the
|
|
// current res file is closed, which can happen when we close
|
|
// our prefs file after writing something to it. Once the plug-
|
|
// in is the current res file, if there are any resource conflicts
|
|
// between the two weÕll get the plug-inÕs resource instead of
|
|
// ours, causing woe.
|
|
//
|
|
void CPluginHandler::OpenPluginResourceFork(Int16 inPrivileges)
|
|
{
|
|
Try_
|
|
{
|
|
Int16 plugRef = fFile->OpenResourceFork(inPrivileges); // Open normally
|
|
|
|
// The plug-in should now be at the top of the chain
|
|
ResourceMapHandle plugMap = (ResourceMapHandle) LMGetTopMapHndl();
|
|
ThrowIf_((**plugMap).refNum != plugRef);
|
|
|
|
// Search down the chain to find the appÕs map
|
|
short appRef = LMGetCurApRefNum();
|
|
ResourceMapHandle appMap = (ResourceMapHandle) LMGetTopMapHndl();
|
|
while (appMap && (**appMap).refNum != appRef)
|
|
appMap = (**appMap).nextMap;
|
|
ThrowIfNil_(appMap);
|
|
|
|
// Remove the plug-in from the top of the chain
|
|
ResourceMapHandle newTop = (**plugMap).nextMap;
|
|
ThrowIfNil_(newTop);
|
|
LMSetTopMapHndl((Handle)newTop);
|
|
|
|
// Re-insert the plug-in below the application
|
|
(**plugMap).nextMap = (**appMap).nextMap;
|
|
(**appMap).nextMap = plugMap;
|
|
}
|
|
Catch_(inErr)
|
|
{
|
|
}
|
|
EndCatch_
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegisterPluginsInFolder(FSSpec folder);
|
|
// Recursive registration of all plugins
|
|
void RegisterPluginsInFolder(FSSpec folder)
|
|
{
|
|
// find all the files that are plugins
|
|
CFileIter iter(folder);
|
|
FSSpec plugFile;
|
|
FInfo finderInfo;
|
|
Boolean isFolder;
|
|
while (iter.Next(plugFile, finderInfo, isFolder))
|
|
{
|
|
if (isFolder)
|
|
RegisterPluginsInFolder(plugFile);
|
|
else if (finderInfo.fdType == emPluginFile)
|
|
{
|
|
//
|
|
// Check the handlers we already have registered against
|
|
// the given file spec. We donÕt want to redundantly
|
|
// create handlers for the same plugin file.
|
|
//
|
|
if (CPluginHandler::CheckExistingHandlers(plugFile))
|
|
{
|
|
Try_
|
|
{
|
|
CPluginHandler* plug = new CPluginHandler(plugFile);
|
|
}
|
|
Catch_(inErr){}
|
|
EndCatch_
|
|
}
|
|
}
|
|
#ifdef EDITOR
|
|
else {
|
|
unsigned char *name = &plugFile.name[0];
|
|
int len = name[0];
|
|
|
|
/* This checks to see if the name starts with cp and ends in '.zip' or '.jar'.
|
|
*/
|
|
if (len >= 5) {
|
|
if ((name[1] == 'c' || name[1] == 'C')
|
|
&& (name[2] == 'p' || name[2] == 'P')
|
|
&& (name[len - 3] == '.')
|
|
&& ((name[len - 2] == 'z' || name[len - 2] == 'Z')
|
|
&& (name[len - 1] == 'i' || name[len - 1] == 'I')
|
|
&& (name[len - 0] == 'p' || name[len - 0] == 'P'))
|
|
|| ((name[len - 2] == 'j' || name[len - 2] == 'J')
|
|
&& (name[len - 1] == 'a' || name[len - 1] == 'A')
|
|
&& (name[len - 0] == 'r' || name[len - 0] == 'R'))) {
|
|
char *cFullPath = CFileMgr::PathNameFromFSSpec( plugFile, TRUE );
|
|
ThrowIfNil_(cFullPath);
|
|
char* cUnixFullPath = CFileMgr::EncodeMacPath(cFullPath); // Frees cFullPath
|
|
ThrowIfNil_(cUnixFullPath);
|
|
(void) NET_UnEscape(cUnixFullPath);
|
|
|
|
EDT_RegisterPlugin(cUnixFullPath);
|
|
XP_FREE(cUnixFullPath);
|
|
}
|
|
}
|
|
}
|
|
#endif // EDITOR
|
|
}
|
|
|
|
//
|
|
// Tell Java to look in this directory for class files
|
|
// so LiveConnect-enabled plug-ins can store their classes
|
|
// with the plug-in.
|
|
//
|
|
Try_
|
|
{
|
|
char* cFullPath = CFileMgr::PathNameFromFSSpec(folder, TRUE);
|
|
ThrowIfNil_(cFullPath);
|
|
char* cUnixFullPath = CFileMgr::EncodeMacPath(cFullPath); // Frees cFullPath
|
|
ThrowIfNil_(cUnixFullPath);
|
|
(void) NET_UnEscape(cUnixFullPath);
|
|
|
|
#if defined(JAVA)
|
|
// Tell Java about this path name
|
|
LJ_AddToClassPath(cUnixFullPath);
|
|
#elif defined(OJI)
|
|
// What, tell the current Java plugin about this class path?
|
|
#endif
|
|
XP_FREE(cUnixFullPath);
|
|
}
|
|
Catch_(inErr) {}
|
|
EndCatch_
|
|
}
|
|
|
|
//
|
|
// Default handler registered below for formats FO_CACHE_AND_EMBED and
|
|
// FO_CACHE, across all types. This handler recovers the pointer to
|
|
// our CPluginView and tells it that it does not have a valid plugin
|
|
// instance.
|
|
//
|
|
NET_StreamClass* EmbedDefault(
|
|
int format_out, void* registration, URL_Struct* request, MWContext* context);
|
|
NET_StreamClass* EmbedDefault(
|
|
int /*format_out*/, void* /*registration*/, URL_Struct* request, MWContext*)
|
|
{
|
|
if (request != NULL)
|
|
{
|
|
NPEmbeddedApp* fe_data = (NPEmbeddedApp*) request->fe_data;
|
|
if (fe_data != NULL)
|
|
{
|
|
CPluginView* plugin = (CPluginView*) fe_data->fe_data;
|
|
if (plugin != NULL)
|
|
plugin->SetBrokenPlugin();
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Returns the plugin folder spec */
|
|
OSErr FindPluginFolder(FSSpec * plugFolder, Boolean create);
|
|
OSErr FindPluginFolder(FSSpec * plugFolder, Boolean create)
|
|
{
|
|
FSSpec netscapeSpec = CPrefs::GetFolderSpec(CPrefs::NetscapeFolder);
|
|
long netscape_dirID;
|
|
|
|
|
|
OSErr err;
|
|
if ( (err=CFileMgr::GetFolderID(netscapeSpec, netscape_dirID)) == noErr )
|
|
{
|
|
#ifndef PLUGIN_FOLDER_NAME_MUST_BE_MOVED_TO_A_RESOURCE
|
|
/*
|
|
Yes! It's a bugus preprocessor symbol, but it got your attention, didn't it?
|
|
|
|
For localization, the plugin folder name should be moved to a resource, however there is no reported
|
|
bug yet (and we're currently under change control).
|
|
|
|
***Move only the preferred name into a resource. Leave the old name as a constant string in the source
|
|
(since the old name will not have been localized on any users disk).***
|
|
|
|
In the meantime...
|
|
*/
|
|
|
|
Str255 pluginFolderName = "\pPlug-ins";
|
|
#else
|
|
// The preferred (localized) name of the plugins folder is in a resource...
|
|
Str255 pluginFolderName;
|
|
GetIndString(pluginFolderName, ..., ...);
|
|
#endif
|
|
|
|
|
|
FSSpec oldNameSpec;
|
|
Boolean isFolder, wasAliased;
|
|
|
|
// first look for the preferences folder with the preferred name
|
|
if ( ((err=FSMakeFSSpec(netscapeSpec.vRefNum, netscape_dirID, pluginFolderName, plugFolder)) == noErr) // ...if we made the FSSpec ok
|
|
&& ((err=ResolveAliasFile(plugFolder, true, &isFolder, &wasAliased)) == noErr) ) // ...and it leads to something
|
|
{
|
|
// the plugins folder already exists as "Plug-ins", nothing else to do
|
|
}
|
|
|
|
// else, try the old name (using |oldNameSpec|, so that if not found, I can create a folder from the preferred spec created above)
|
|
else if ( ((err=FSMakeFSSpec(netscapeSpec.vRefNum, netscape_dirID, "\pPlugins", &oldNameSpec)) == noErr) // ...if we made the FSSpec ok
|
|
&& ((err=ResolveAliasFile(&oldNameSpec, true, &isFolder, &wasAliased)) == noErr) ) // ...and it leads to something
|
|
{
|
|
// the plugins folder already exists as [deprecated] "Plugins", copy the spec we used to learn that to the caller
|
|
FSMakeFSSpec(oldNameSpec.vRefNum, oldNameSpec.parID, oldNameSpec.name, plugFolder);
|
|
}
|
|
|
|
// otherwise, we may need to create it
|
|
else if ( create )
|
|
{
|
|
long created_dirID;
|
|
err = FSpDirCreate(plugFolder, smSystemScript, &created_dirID);
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
// *************************************************************************************
|
|
//
|
|
// XP interface methods
|
|
//
|
|
// *************************************************************************************
|
|
|
|
void FE_RegisterPlugins();
|
|
void FE_RegisterPlugins()
|
|
{
|
|
//
|
|
// Register a wildcard handler for "embed" types before we scan the
|
|
// plugins folder -- that way, if a "null plugin" is found that
|
|
// also registers "*"/FO_EMBED, it will be registered later than this
|
|
// handler and thus take precendence.
|
|
//
|
|
NET_RegisterContentTypeConverter("*", FO_EMBED, NULL, EmbedDefault);
|
|
|
|
OSErr err;
|
|
FSSpec plugFolder;
|
|
|
|
if (!IsThisKeyDown(kOptionKey)) // Skip loading plugins if option key is down
|
|
err = FindPluginFolder(&plugFolder, false);
|
|
else
|
|
err = -1;
|
|
|
|
if (err != noErr)
|
|
{
|
|
CFrontApp::SplashProgress(GetPString(MAC_NO_PLUGIN));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
CFrontApp::SplashProgress(GetPString(MAC_REGISTER_PLUGINS));
|
|
RegisterPluginsInFolder(plugFolder);
|
|
}
|
|
|
|
//
|
|
// Now that all plug-ins have been registered, look through the MIME table
|
|
// for types that are assigned to non-existant plug-ins or plug-in types,
|
|
// and change all these types to use "Unknown".
|
|
//
|
|
LArrayIterator iterator(CPrefs::sMimeTypes);
|
|
CMimeMapper* mapper;
|
|
while (iterator.Next(&mapper))
|
|
{
|
|
if (mapper->GetLoadAction() == CMimeMapper::Plugin) // Handled by plug-in?
|
|
{
|
|
Boolean foundType = false;
|
|
char* otherPluginName = NULL;
|
|
|
|
// Look for the desired plug-in
|
|
CStr255 mapperName = mapper->GetPluginName();
|
|
NPReference plugin = NPRefFromStart;
|
|
char* name;
|
|
while (NPL_IteratePluginFiles(&plugin, &name, NULL, NULL))
|
|
{
|
|
// Look for the desired type
|
|
CStr255 mapperMime = mapper->GetMimeName();
|
|
NPReference mimetype = NPRefFromStart;
|
|
char* type;
|
|
while (NPL_IteratePluginTypes(&mimetype, plugin, &type, NULL, NULL, NULL))
|
|
{
|
|
if (mapperMime == type)
|
|
{
|
|
if (mapperName != name)
|
|
otherPluginName = name;
|
|
else
|
|
foundType = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (!foundType) // No plug-in, or no type?
|
|
{
|
|
if (otherPluginName)
|
|
{
|
|
void* pdesc = GetPluginDesc(otherPluginName);
|
|
if (pdesc)
|
|
NPL_EnablePluginType((char*)mapper->GetMimeName(), pdesc, TRUE);
|
|
mapper->SetPluginName(otherPluginName);
|
|
CPrefs::SetModified();
|
|
}
|
|
else
|
|
{
|
|
mapper->SetLatentPlugin(); // Mark the plug-in as Òdisabled because missingÓ
|
|
CPrefs::SetModified(); // The prefs have changed
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
NPPluginFuncs * FE_LoadPlugin(void *plugin, NPNetscapeFuncs * funcs, struct _np_handle* handle)
|
|
{
|
|
return ((CPluginHandler*)plugin)->LoadPlugin(funcs, handle);
|
|
}
|
|
|
|
void FE_UnloadPlugin(void *plugin, struct _np_handle* handle)
|
|
{
|
|
((CPluginHandler*)plugin)->CloseCodeResource(handle);
|
|
}
|
|
|
|
//
|
|
// This exit routine is called when an embed stream completes.
|
|
// We use that opportunity to see if something when wrong when
|
|
// creating the initial stream for the plug-in so we can
|
|
// display some helpful UI.
|
|
//
|
|
void FE_EmbedURLExit(URL_Struct* /*urls*/, int /*status*/, MWContext* /*cx*/)
|
|
{
|
|
// bing: Don't do anything here until we figure out a
|
|
}
|
|
|
|
|
|
//
|
|
// This code is called by npn_status, which is called by the plug-in.
|
|
// The plug-in may have manipulated the port before calling us, so
|
|
// we have to assume that we're out of focus so we'll draw in the
|
|
// right place. We also need to save and restore the entire port
|
|
// state so the plug-in's port settings are trashed.
|
|
//
|
|
void FE_PluginProgress(MWContext* cx, const char* message)
|
|
{
|
|
GrafPtr currentPort;
|
|
GetPort(¤tPort);
|
|
StColorPortState portState(currentPort);
|
|
portState.Save(currentPort);
|
|
StUseResFile resFile(LMGetCurApRefNum());
|
|
|
|
LView::OutOfFocus(nil); // Make sure FE_Progress focuses the caption
|
|
FE_Progress(cx, message);
|
|
LView::OutOfFocus(nil); // portState.Restore will screw up the focus again
|
|
|
|
portState.Restore();
|
|
}
|
|
|
|
|
|
//
|
|
// See comments in npglue.c in np_geturlinternal.
|
|
//
|
|
void FE_ResetRefreshURLTimer(MWContext* )
|
|
{
|
|
Assert_(false);
|
|
// if (NETSCAPEVIEW(context))
|
|
// NETSCAPEVIEW(context)->ResetRefreshURLTimer();
|
|
}
|
|
|
|
// A generic way to query information. Nothing to do now.
|
|
NPError FE_PluginGetValue(MWContext *, NPEmbeddedApp *, NPNVariable ,
|
|
void */*r_value*/)
|
|
{
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Rather than having to scan an explicit list of windows each time an event
|
|
// comes in, why not use a sub-class of LWindow, and let PowerPlant do the work
|
|
// for us?
|
|
//
|
|
|
|
#pragma mark ### CPluginWindow ###
|
|
|
|
class CPluginWindow : public LWindow, public LPeriodical {
|
|
public:
|
|
CPluginWindow(nsIEventHandler* eventHandler, WindowRef window);
|
|
virtual ~CPluginWindow();
|
|
|
|
virtual void HandleClick(const EventRecord& inMacEvent, Int16 inPart);
|
|
virtual void EventMouseUp(const EventRecord &inMacEvent);
|
|
virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam);
|
|
virtual Boolean HandleKeyPress(const EventRecord& inKeyEvent);
|
|
virtual void DrawSelf();
|
|
virtual void SpendTime(const EventRecord& inMacEvent);
|
|
virtual void ActivateSelf();
|
|
virtual void DeactivateSelf();
|
|
virtual void AdjustCursorSelf(Point inPortPt, const EventRecord& inMacEvent);
|
|
|
|
Boolean IsPluginCommand(CommandT inCommand);
|
|
Boolean PassPluginEvent(EventRecord& event);
|
|
|
|
private:
|
|
nsIEventHandler* mEventHandler;
|
|
SInt16 mKind;
|
|
};
|
|
|
|
void FE_RegisterWindow(nsIEventHandler* handler, void* window)
|
|
{
|
|
#if 1
|
|
LCommander::LCommander::SetDefaultCommander(LCommander::GetTopCommander());
|
|
CPluginWindow* pluginWindow = new CPluginWindow(handler, WindowRef(window));
|
|
XP_ASSERT(pluginWindow != NULL);
|
|
if (pluginWindow != NULL) {
|
|
pluginWindow->Show();
|
|
pluginWindow->Select();
|
|
}
|
|
#else
|
|
((CPluginView*)plugin)->RegisterWindow(window);
|
|
#endif
|
|
}
|
|
|
|
void FE_UnregisterWindow(nsIEventHandler* handler, void* window)
|
|
{
|
|
#if 1
|
|
// Toss the pluginWindow itself.
|
|
LWindow* pluginWindow = LWindow::FetchWindowObject(WindowPtr(window));
|
|
if (pluginWindow != NULL) {
|
|
// Hide the window.
|
|
pluginWindow->Hide();
|
|
// Notify PowerPlant that the window is no longer active.
|
|
// pluginWindow->Deactivate();
|
|
delete pluginWindow;
|
|
}
|
|
#else
|
|
((CPluginView*)plugin)->UnregisterWindow(window);
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
SInt16 FE_AllocateMenuID(void *plugin, XP_Bool isSubmenu)
|
|
{
|
|
return ((CPluginView*)plugin)->AllocateMenuID(isSubmenu);
|
|
}
|
|
#endif
|
|
|
|
CPluginWindow::CPluginWindow(nsIEventHandler* eventHandler, WindowRef window)
|
|
{
|
|
mEventHandler = eventHandler;
|
|
mMacWindowP = window;
|
|
|
|
// The following is cribbed from one of the LWindow constructors.
|
|
|
|
// "bless" the plugin window so it will be considered to be a first-class PowerPlant window.
|
|
mKind = ::GetWindowKind(window);
|
|
::SetWindowKind(window, PP_Window_Kind);
|
|
::SetWRefCon(window, long(this));
|
|
|
|
// Set some default attributes.
|
|
SetAttribute(windAttr_CloseBox | windAttr_TitleBar | windAttr_Resizable
|
|
| windAttr_SizeBox | windAttr_Targetable | windAttr_Enabled);
|
|
|
|
// Window Frame and Image are the same as its portRect.
|
|
short width = mMacWindowP->portRect.right - mMacWindowP->portRect.left;
|
|
short height = mMacWindowP->portRect.bottom - mMacWindowP->portRect.top;
|
|
|
|
ResizeFrameTo(width, height, false);
|
|
ResizeImageTo(width, height, false);
|
|
|
|
CalcRevealedRect();
|
|
|
|
// Initial size and location are the "user" state for zooming.
|
|
CalcPortFrameRect(mUserBounds);
|
|
PortToGlobalPoint(topLeft(mUserBounds));
|
|
PortToGlobalPoint(botRight(mUserBounds));
|
|
|
|
mVisible = triState_Off;
|
|
mActive = triState_Off;
|
|
mEnabled = triState_Off;
|
|
if (HasAttribute(windAttr_Enabled)) {
|
|
mEnabled = triState_On;
|
|
}
|
|
|
|
FocusDraw();
|
|
::GetForeColor(&mForeColor);
|
|
::GetBackColor(&mBackColor);
|
|
|
|
StartIdling();
|
|
}
|
|
|
|
CPluginWindow::~CPluginWindow()
|
|
{
|
|
// prevent the LWindow destructor from clobbering the window.
|
|
if (mMacWindowP != NULL) {
|
|
WindowPtr window = mMacWindowP;
|
|
if (IsWindowVisible(window))
|
|
HideSelf();
|
|
|
|
// Restore kind and refCon to original values.
|
|
::SetWindowKind(window, mKind);
|
|
::SetWRefCon(window, 0);
|
|
|
|
mMacWindowP = NULL;
|
|
}
|
|
}
|
|
|
|
void CPluginWindow::HandleClick(const EventRecord& inMacEvent, Int16 inPart)
|
|
{
|
|
EventRecord mouseEvent = inMacEvent;
|
|
if (!PassPluginEvent(mouseEvent))
|
|
LWindow::HandleClick(inMacEvent, inPart);
|
|
}
|
|
|
|
void CPluginWindow::EventMouseUp(const EventRecord& inMouseUp)
|
|
{
|
|
EventRecord mouseEvent = inMouseUp;
|
|
PassPluginEvent(mouseEvent);
|
|
}
|
|
|
|
Boolean CPluginWindow::ObeyCommand(CommandT inCommand, void *ioParam)
|
|
{
|
|
if (IsPluginCommand(inCommand)) {
|
|
EventRecord menuEvent;
|
|
::OSEventAvail(0, &menuEvent);
|
|
menuEvent.what = nsPluginEventType_MenuCommandEvent;
|
|
menuEvent.message = -inCommand; // PowerPlant encodes a raw menu selection as the negation of the selection.
|
|
return PassPluginEvent(menuEvent);
|
|
}
|
|
return LWindow::ObeyCommand(inCommand, ioParam);
|
|
}
|
|
|
|
Boolean CPluginWindow::HandleKeyPress(const EventRecord& inKeyEvent)
|
|
{
|
|
EventRecord keyEvent = inKeyEvent;
|
|
return PassPluginEvent(keyEvent);
|
|
}
|
|
|
|
void CPluginWindow::DrawSelf()
|
|
{
|
|
EventRecord updateEvent;
|
|
::OSEventAvail(0, &updateEvent);
|
|
updateEvent.what = updateEvt;
|
|
updateEvent.message = UInt32(mMacWindowP);
|
|
PassPluginEvent(updateEvent);
|
|
}
|
|
|
|
void CPluginWindow::SpendTime(const EventRecord& inMacEvent)
|
|
{
|
|
// Need to poll various states, because the plugin can change things like
|
|
// the window's visibility, size, etc.
|
|
|
|
// Put the visible state in synch with the window's visibility.
|
|
mVisible = (IsWindowVisible(mMacWindowP) ? triState_On : triState_Off);
|
|
|
|
// Make sure the window's size hasn't changed (what about location?)
|
|
short width = mMacWindowP->portRect.right - mMacWindowP->portRect.left;
|
|
short height = mMacWindowP->portRect.bottom - mMacWindowP->portRect.top;
|
|
if (mFrameSize.width != width || mFrameSize.height != height) {
|
|
ResizeFrameTo(width, height, false);
|
|
ResizeImageTo(width, height, false);
|
|
CalcRevealedRect();
|
|
}
|
|
|
|
EventRecord macEvent = inMacEvent;
|
|
PassPluginEvent(macEvent);
|
|
|
|
//
|
|
// If weÕre in SpendTime because of a non-null event, send a
|
|
// null event too. Some plug-ins (e.g. Shockwave) rely on null
|
|
// events for animation, so it doesnÕt matter if weÕre giving
|
|
// them lots of time with mouseMoved or other events -- if they
|
|
// donÕt get null events, they donÕt animate fast.
|
|
//
|
|
if (macEvent.what != nullEvent) {
|
|
macEvent.what = nullEvent;
|
|
PassPluginEvent(macEvent);
|
|
}
|
|
}
|
|
|
|
void CPluginWindow::ActivateSelf()
|
|
{
|
|
LWindow::ActivateSelf();
|
|
|
|
EventRecord activateEvent;
|
|
::OSEventAvail(0, &activateEvent);
|
|
activateEvent.what = activateEvt;
|
|
activateEvent.modifiers |= activeFlag;
|
|
activateEvent.message = UInt32(mMacWindowP);
|
|
PassPluginEvent(activateEvent);
|
|
}
|
|
|
|
|
|
void CPluginWindow::DeactivateSelf()
|
|
{
|
|
LWindow::DeactivateSelf();
|
|
|
|
EventRecord activateEvent;
|
|
::OSEventAvail(0, &activateEvent);
|
|
activateEvent.what = activateEvt;
|
|
activateEvent.modifiers &= ~activeFlag;
|
|
activateEvent.message = UInt32(mMacWindowP);
|
|
PassPluginEvent(activateEvent);
|
|
}
|
|
|
|
void CPluginWindow::AdjustCursorSelf(Point inPortPt, const EventRecord& inMacEvent)
|
|
{
|
|
EventRecord cursorEvent = inMacEvent;
|
|
cursorEvent.what = nsPluginEventType_AdjustCursorEvent;
|
|
if (!PassPluginEvent(cursorEvent))
|
|
LWindow::AdjustCursorSelf(inPortPt, inMacEvent);
|
|
}
|
|
|
|
Boolean CPluginWindow::IsPluginCommand(CommandT inCommand)
|
|
{
|
|
#if 1
|
|
// Since only one plugin can have menus in the menu bar at a time,
|
|
// the test only checks to see if this plugin has any menus, and
|
|
// whether the command is synthetic and is from one of the plugin's menus.
|
|
if (thePluginManager != NULL) {
|
|
short menuID, menuItem;
|
|
if (LCommander::IsSyntheticCommand(inCommand, menuID, menuItem)) {
|
|
PRBool hasAllocated = PR_FALSE;
|
|
if (thePluginManager->HasAllocatedMenuID(mEventHandler, menuID, &hasAllocated) == NS_OK)
|
|
return hasAllocated;
|
|
}
|
|
}
|
|
return false;
|
|
#else
|
|
return mPlugin->IsPluginCommand(inCommand);
|
|
#endif
|
|
}
|
|
|
|
Boolean CPluginWindow::PassPluginEvent(EventRecord& event)
|
|
{
|
|
#if 1
|
|
nsPluginEvent pluginEvent = { &event, mMacWindowP };
|
|
PRBool eventHandled = PR_FALSE;
|
|
mEventHandler->HandleEvent(&pluginEvent, &eventHandled);
|
|
return eventHandled;
|
|
#else
|
|
return mPlugin->PassWindowEvent(event, mMacWindowP))
|
|
#endif
|
|
}
|
|
|
|
|
|
// *************************************************************************************
|
|
//
|
|
// CPluginView methods
|
|
//
|
|
// *************************************************************************************
|
|
|
|
#pragma mark ### CPluginView ###
|
|
|
|
CPluginView* CPluginView::sPluginTarget = NULL;
|
|
LArray* CPluginView::sPluginList = NULL;
|
|
|
|
//
|
|
// This static method lets the caller broadcast a mac event to
|
|
// all existing plug-ins (regardless of context). ItÕs used by
|
|
// CFrontApp::EventSuspendResume to broadcast suspend and resume
|
|
// events to plug-ins.
|
|
//
|
|
void CPluginView::BroadcastPluginEvent(const EventRecord& event)
|
|
{
|
|
if (sPluginList != NULL)
|
|
{
|
|
EventRecord eventCopy = event; // event is const, but PassEvent isnÕt
|
|
LArrayIterator iterator(*sPluginList);
|
|
CPluginView* plugin = NULL;
|
|
while (iterator.Next(&plugin))
|
|
(void) plugin->PassEvent(eventCopy);
|
|
}
|
|
}
|
|
|
|
// XXX The following two methods are obsolete -- CPluginWindow now does the work.
|
|
|
|
// This gets called for every event - a performance analysis/review would be good
|
|
Boolean CPluginView::PluginWindowEvent(const EventRecord& event)
|
|
{
|
|
WindowPtr hitWindow;
|
|
|
|
switch(event.what)
|
|
{
|
|
case mouseDown:
|
|
case mouseUp:
|
|
::FindWindow(event.where, &hitWindow);
|
|
break;
|
|
|
|
case autoKey:
|
|
case keyDown:
|
|
case keyUp:
|
|
hitWindow = ::FrontWindow();
|
|
break;
|
|
|
|
case activateEvt:
|
|
case updateEvt:
|
|
hitWindow = (WindowPtr) event.message;
|
|
break;
|
|
|
|
case nullEvent:
|
|
case diskEvt:
|
|
case osEvt:
|
|
case kHighLevelEvent:
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
#if 1
|
|
// Determine which plugin owns this window.
|
|
Boolean isActivateEvent = (event.what == activateEvt && (event.modifiers & activeFlag));
|
|
CPluginView* owningPlugin = FindPlugin(hitWindow);
|
|
if (owningPlugin != NULL) {
|
|
EventRecord eventCopy = event;
|
|
return owningPlugin->PassWindowEvent(eventCopy, hitWindow);
|
|
}
|
|
#else
|
|
if (sPluginList != NULL) {
|
|
// iterate through each plugin instance
|
|
LArrayIterator pluginIterator(*sPluginList);
|
|
CPluginView* plugin = NULL;
|
|
while (pluginIterator.Next(&plugin)) {
|
|
if (plugin->fWindowList != NULL) {
|
|
// then iterate through each window for each instance
|
|
LArrayIterator windowIterator(*(plugin->fWindowList));
|
|
WindowPtr window = NULL;
|
|
while (windowIterator.Next(&window)) {
|
|
if (window == hitWindow) {
|
|
EventRecord eventCopy = event;
|
|
return plugin->PassWindowEvent(eventCopy, window);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// we didn't find a match
|
|
return false;
|
|
}
|
|
|
|
// Determines which plugin owns the specified window.
|
|
|
|
CPluginView* CPluginView::FindPlugin(WindowPtr window)
|
|
{
|
|
// Make sure the window isn't a PowerPlant window.
|
|
LWindow* windowObj = LWindow::FetchWindowObject(window);
|
|
if (windowObj == NULL && sPluginList != NULL) {
|
|
// iterate through each plugin instance
|
|
LArrayIterator pluginIterator(*sPluginList);
|
|
CPluginView* plugin = NULL;
|
|
while (pluginIterator.Next(&plugin)) {
|
|
if (plugin->fWindowList != NULL) {
|
|
// then iterate through each window for each instance
|
|
LArrayIterator windowIterator(*(plugin->fWindowList));
|
|
WindowPtr pluginWindow = NULL;
|
|
while (windowIterator.Next(&pluginWindow)) {
|
|
if (window == pluginWindow)
|
|
return plugin;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CPluginView::RegisterWindow(void* window)
|
|
{
|
|
// Register the pluginWindow with PowerPlant.
|
|
|
|
// Set the default commander to the application, to limit the depth of the chain of command.
|
|
// I've seen this get ridiculously deep.
|
|
LCommander::LCommander::SetDefaultCommander(LCommander::GetTopCommander());
|
|
|
|
#if 0
|
|
CPluginWindow* pluginWindow = new CPluginWindow(this, WindowPtr(window));
|
|
|
|
if (fWindowList == NULL)
|
|
fWindowList = new LArray;
|
|
if (fWindowList != NULL)
|
|
fWindowList->InsertItemsAt(1, 0, &window);
|
|
#endif
|
|
}
|
|
|
|
void CPluginView::UnregisterWindow(void* window)
|
|
{
|
|
// Toss the pluginWindow itself.
|
|
LWindow* pluginWindow = LWindow::FetchWindowObject(WindowPtr(window));
|
|
if (pluginWindow != NULL) {
|
|
// Notify PowerPlant that the window is no longer active.
|
|
pluginWindow->Deactivate();
|
|
delete pluginWindow;
|
|
}
|
|
|
|
#if 0
|
|
if (fWindowList != NULL) {
|
|
Int32 index = fWindowList->FetchIndexOf(&window);
|
|
if (index > 0)
|
|
fWindowList->RemoveItemsAt(1, index);
|
|
|
|
if (fWindowList->GetCount() == 0) {
|
|
delete fWindowList;
|
|
fWindowList = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
SInt16 CPluginView::AllocateMenuID(Boolean isSubmenu)
|
|
{
|
|
SInt16 menuID = LMenuSharingAttachment::AllocatePluginMenuID(isSubmenu);
|
|
|
|
if (fMenuList == NULL)
|
|
fMenuList = new TArray<SInt16>;
|
|
if (fMenuList != NULL)
|
|
fMenuList->AddItem(menuID);
|
|
|
|
return menuID;
|
|
}
|
|
|
|
Boolean CPluginView::IsPluginCommand(CommandT inCommand)
|
|
{
|
|
// Since only one plugin can have menus in the menu bar at a time,
|
|
// the test only checks to see if this plugin has any menus, and
|
|
// whether the command is synthetic and is from one of the plugin's menus.
|
|
if (fMenuList != NULL) {
|
|
short menuId, menuItem;
|
|
if (LCommander::IsSyntheticCommand(inCommand, menuId, menuItem)) {
|
|
TArray<SInt16>& menus = *fMenuList;
|
|
UInt32 count = menus.GetCount();
|
|
for (UInt32 i = count; i > 0; --i)
|
|
if (menus[i] == menuId)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Boolean CPluginView::PassWindowEvent(EventRecord& inEvent, WindowPtr window)
|
|
{
|
|
Boolean eventHandled = false;
|
|
if (fApp) {
|
|
eventHandled = NPL_HandleEvent(fApp, (NPEvent*)&inEvent, (void*) window);
|
|
}
|
|
return eventHandled;
|
|
}
|
|
|
|
CPluginView::CPluginView(LStream *inStream) : LView(inStream), LDragAndDrop(nil, this)
|
|
{
|
|
fApp = NULL;
|
|
fOriginalView = NULL;
|
|
fBrokenPlugin = false;
|
|
fPositioned = false;
|
|
fHidden = false;
|
|
fWindowed = true;
|
|
fBrokenIcon = NULL;
|
|
fIsPrinting = false;
|
|
fWindowList = NULL;
|
|
fMenuList = NULL;
|
|
|
|
//
|
|
// Add the new plug-in to a global list of all plug-ins.
|
|
//
|
|
if (sPluginList == NULL)
|
|
sPluginList = new LArray;
|
|
if (sPluginList != NULL)
|
|
sPluginList->InsertItemsAt(1, 0, &this);
|
|
|
|
|
|
//
|
|
// CanÕt call ResetDrawRect yet because it needs a port
|
|
// from our superview, but we donÕt have a superview yet!
|
|
//
|
|
}
|
|
|
|
|
|
CPluginView::~CPluginView()
|
|
{
|
|
if (fBrokenIcon != NULL)
|
|
CIconList::ReturnIcon(fBrokenIcon);
|
|
|
|
//
|
|
// This static is parallel to LCommander::sTarget, but only for plug-ins.
|
|
// The LCommander destructor takes care of resetting the LCommander::sTarget,
|
|
// and so the CPluginView destructor should take care of the special plug-in target.
|
|
//
|
|
if (CPluginView::sPluginTarget == this)
|
|
CPluginView::sPluginTarget = NULL;
|
|
|
|
// we're assuming the plugin has already killed the windows themselves
|
|
if (fWindowList != NULL)
|
|
delete fWindowList;
|
|
|
|
// release the menu IDs used by this plugin?
|
|
if (fMenuList != NULL)
|
|
delete fMenuList;
|
|
|
|
//
|
|
// Remove the deleted plug-in from the global list of all plug-ins.
|
|
//
|
|
if (sPluginList != NULL) {
|
|
Int32 index = sPluginList->FetchIndexOf(&this);
|
|
if (index > 0)
|
|
sPluginList->RemoveItemsAt(1, index);
|
|
|
|
if (sPluginList->GetCount() == 0)
|
|
{
|
|
delete sPluginList;
|
|
sPluginList = NULL;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
void CPluginView::EmbedSize(LO_EmbedStruct* embed_struct, SDimension16 hyperSize)
|
|
{
|
|
//
|
|
// If the plug-in is hidden, set the width and height to zero and
|
|
// set a flag indicating that we are hidden.
|
|
//
|
|
if (embed_struct->objTag.ele_attrmask & LO_ELE_HIDDEN)
|
|
{
|
|
embed_struct->objTag.width = 0;
|
|
embed_struct->objTag.height = 0;
|
|
fHidden = true;
|
|
Hide();
|
|
StartIdling(); // Visible plug-ins start idling in EmbedDisplay
|
|
}
|
|
|
|
//
|
|
// If the embed src is internal-external-plugin, the plugin is
|
|
// full-screen, and we should set up the pluginÕs real width
|
|
// for layout since it doesnÕt know how big to make it. Since a full-
|
|
// screen plugin should resize when its enclosing view (the hyperview)
|
|
// resizes, we bind the plugin view on all sides to its superview.
|
|
//
|
|
Boolean fullPage = false;
|
|
if (embed_struct->embed_src)
|
|
{
|
|
char* theURL;
|
|
PA_LOCK(theURL, char*, embed_struct->embed_src);
|
|
XP_ASSERT(theURL);
|
|
if (XP_STRCMP(theURL, "internal-external-plugin") == 0)
|
|
fullPage = true;
|
|
PA_UNLOCK(embed_struct->embed_src);
|
|
}
|
|
|
|
if (fullPage)
|
|
{
|
|
SBooleanRect binding = {true, true, true, true};
|
|
SetFrameBinding(binding);
|
|
|
|
embed_struct->objTag.width = hyperSize.width;
|
|
embed_struct->objTag.height = hyperSize.height;
|
|
|
|
//
|
|
// Remember an offset for the view to
|
|
// compensate for layout's default margins.
|
|
//
|
|
const short kLeftMargin = 8; // ¥¥¥ These should be defined in mhyper.h!!!
|
|
const short kTopMargin = 8;
|
|
fHorizontalOffset = -kLeftMargin;
|
|
fVerticalOffset = -kTopMargin;
|
|
}
|
|
else
|
|
{
|
|
fHorizontalOffset = 0;
|
|
fVerticalOffset = 0;
|
|
}
|
|
|
|
ResizeImageTo(embed_struct->objTag.width, embed_struct->objTag.height, false);
|
|
ResizeFrameTo(embed_struct->objTag.width, embed_struct->objTag.height, false);
|
|
|
|
//
|
|
// NOTE: The position set here is not really valid because the x and y in
|
|
// embed_struct are not valid yet (we have to wait until DisplayEmbed
|
|
// (see below) is called the first time to get the real location).
|
|
// We go ahead and position ourselves anyway just so we have a superview
|
|
// and location initially.
|
|
//
|
|
Int32 x = embed_struct->objTag.x + embed_struct->objTag.x_offset + fHorizontalOffset;
|
|
Int32 y = embed_struct->objTag.y + embed_struct->objTag.y_offset + fVerticalOffset;
|
|
PlaceInSuperImageAt(x, y, false);
|
|
|
|
//
|
|
// Set up the NPWindow and NPPort for the first time. No one should
|
|
// have called NPL_EmbedSize before this point, or bogus values will
|
|
// get passed to the plugin!
|
|
//
|
|
ResetDrawRect();
|
|
}
|
|
|
|
|
|
void CPluginView::EmbedDisplay(LO_EmbedStruct* embed_struct, Boolean isPrinting)
|
|
{
|
|
fWindowed = (Boolean)NPL_IsEmbedWindowed(fApp);
|
|
fIsPrinting = isPrinting;
|
|
|
|
//
|
|
// If weÕre not positioned yet, place our instance at the
|
|
// location specified by layout and make our view visible.
|
|
//
|
|
if (fPositioned == false)
|
|
{
|
|
Int32 x = embed_struct->objTag.x + embed_struct->objTag.x_offset + fHorizontalOffset;
|
|
Int32 y = embed_struct->objTag.y + embed_struct->objTag.y_offset + fVerticalOffset;
|
|
PlaceInSuperImageAt(x, y, false);
|
|
if (fWindowed)
|
|
Show();
|
|
else if (!fBrokenPlugin) {
|
|
fHidden = true;
|
|
Hide();
|
|
}
|
|
StartIdling();
|
|
fPositioned = true;
|
|
|
|
XP_ASSERT(fApp);
|
|
if (fApp != NULL)
|
|
NPL_EmbedSize(fApp); // Tell the plugin about its dimensions and location
|
|
|
|
Refresh();
|
|
}
|
|
|
|
// If this is a windowed plug-in, this call indicates that we've moved or our visibility
|
|
// changed.
|
|
if (fWindowed || fBrokenPlugin)
|
|
{
|
|
Rect frameRect;
|
|
SPoint32 imagePoint;
|
|
Int32 x, y;
|
|
|
|
if (IsVisible() && (embed_struct->objTag.ele_attrmask & LO_ELE_INVISIBLE))
|
|
Hide();
|
|
|
|
CalcPortFrameRect(frameRect);
|
|
GetSuperView()->PortToLocalPoint(topLeft(frameRect));
|
|
GetSuperView()->LocalToImagePoint(topLeft(frameRect), imagePoint);
|
|
|
|
x = embed_struct->objTag.x + embed_struct->objTag.x_offset + fHorizontalOffset;
|
|
y = embed_struct->objTag.y + embed_struct->objTag.y_offset + fVerticalOffset;
|
|
if ((imagePoint.h != x) || (imagePoint.v != y))
|
|
PlaceInSuperImageAt(x, y, true);
|
|
|
|
if (!IsVisible() && !(embed_struct->objTag.ele_attrmask & LO_ELE_INVISIBLE))
|
|
Show();
|
|
}
|
|
// For a windowless plug-in, this is where the plug-in actually draws.
|
|
else
|
|
{
|
|
EventRecord updateEvent;
|
|
//
|
|
// Always reset the drawing info before calling the plugin to draw.
|
|
//
|
|
ResetDrawRect();
|
|
|
|
::OSEventAvail(0, &updateEvent);
|
|
updateEvent.what = updateEvt;
|
|
(void) PassEvent(updateEvent);
|
|
}
|
|
}
|
|
|
|
|
|
void CPluginView::EmbedCreate(MWContext* context, LO_EmbedStruct* embed_struct)
|
|
{
|
|
fEmbedStruct = embed_struct;
|
|
fApp = (NPEmbeddedApp*) embed_struct->objTag.FE_Data;
|
|
Boolean printing = (context->type == MWContextPrint);
|
|
|
|
//
|
|
// Set the references between the view, the app, and the layout
|
|
// structure. Also set the pointer to the new instanceÕs
|
|
// window data (NPWindow), which is stored as part of our object
|
|
// (unless we are printing, in which case it already has a NPWindow).
|
|
// Then start up the stream for the plug-in (if applicable).
|
|
// If NPL_EmbedStart fails, it will delete the NPEmbeddedApp and
|
|
// associated XP data structures, so be sure to remove the
|
|
// embed_struct's reference to it.
|
|
//
|
|
if (fApp != NULL)
|
|
{
|
|
if (printing)
|
|
fOriginalView = (CPluginView*) fApp->fe_data;
|
|
fApp->fe_data = this;
|
|
embed_struct->objTag.FE_Data = fApp;
|
|
if (!printing)
|
|
fApp->wdata = GetNPWindow();
|
|
NPError err = NPL_EmbedStart(context, embed_struct, fApp);
|
|
if (err != NPERR_NO_ERROR)
|
|
fApp = NULL;
|
|
}
|
|
|
|
//
|
|
// Something went wrong? If we have no app, then layout can never
|
|
// tell us to delete the view (since the app is in the FE_Data of
|
|
// the layout structure), so we need to delete it here.
|
|
//
|
|
if (fApp == NULL)
|
|
{
|
|
delete this;
|
|
embed_struct->objTag.FE_Data = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void CPluginView::EmbedFree(MWContext* context, LO_EmbedStruct* embed_struct)
|
|
{
|
|
//
|
|
// Set our reference to the NPEmbeddedApp to NULL now, before
|
|
// calling NPL_EmbedDelete. NPL_EmbedDelete may re-call FE code
|
|
// after deleting the NPEmbeddedApp and we donÕt want to have
|
|
// a reference to a deleted app!
|
|
//
|
|
// XXX This is no longer necessary, as this is called in _response_
|
|
// to NPL_EmbedDelete.
|
|
NPEmbeddedApp* app = fApp;
|
|
fApp = NULL;
|
|
|
|
//
|
|
// If our original view field was set above in EmbedCreate, the
|
|
// the NPEmbeddedApp associated with this view was associated
|
|
// only temporarily for printing, so we should switch its
|
|
// references to its view and window back to their original values.
|
|
// If there is no original view, then we must be the original
|
|
// creator of the NPEmbeddedApp, so we should delete it.
|
|
//
|
|
if (fOriginalView != NULL)
|
|
{
|
|
app->fe_data = fOriginalView;
|
|
XP_ASSERT(app->wdata == fOriginalView->GetNPWindow());
|
|
app->wdata = fOriginalView->GetNPWindow();
|
|
}
|
|
|
|
ThrowIfNil_(context);
|
|
}
|
|
|
|
|
|
|
|
// ¥¥ event processing
|
|
|
|
void CPluginView::ClickSelf(const SMouseDownEvent& inMouseDown)
|
|
{
|
|
XP_ASSERT(fWindowed);
|
|
LView::ClickSelf(inMouseDown);
|
|
|
|
if (!fBrokenPlugin)
|
|
{
|
|
EventRecord mouseEvent = inMouseDown.macEvent; // inMouseDown is const, but PassEvent isnÕt
|
|
(void) PassEvent(mouseEvent);
|
|
LCommander::SwitchTarget(this); // Once clicked, we can get keystrokes
|
|
}
|
|
}
|
|
|
|
void CPluginView::EventMouseUp(const EventRecord& inMouseUp)
|
|
{
|
|
XP_ASSERT(fWindowed);
|
|
LView::EventMouseUp(inMouseUp);
|
|
|
|
EventRecord mouseEvent = inMouseUp; // inMouseUp is const, but PassEvent isnÕt
|
|
(void) PassEvent(mouseEvent);
|
|
}
|
|
|
|
|
|
Boolean CPluginView::ObeyCommand(CommandT inCommand, void *ioParam)
|
|
{
|
|
if (IsPluginCommand(inCommand)) {
|
|
// assume this is a plugin menu item, since menusharing didn't handle it.
|
|
EventRecord menuEvent;
|
|
::OSEventAvail(0, &menuEvent);
|
|
menuEvent.what = nsPluginEventType_MenuCommandEvent;
|
|
menuEvent.message = -inCommand; // PowerPlant encodes a raw menu selection as the negation of the selection.
|
|
return PassEvent(menuEvent);
|
|
}
|
|
return LCommander::ObeyCommand(inCommand, ioParam);
|
|
}
|
|
|
|
Boolean CPluginView::HandleKeyPress(const EventRecord& inKeyEvent)
|
|
{
|
|
if (fWindowed) {
|
|
EventRecord keyEvent = inKeyEvent; // inKeyEvent is const, but PassEvent isnÕt
|
|
return PassEvent(keyEvent);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Boolean CPluginView::FocusDraw(LPane *inSubPane)
|
|
{
|
|
Boolean revealed = LView::FocusDraw(inSubPane);
|
|
|
|
//
|
|
// Make sure that any exposed popdown view is clipped out before drawing occurs.
|
|
//
|
|
CBrowserWindow::ClipOutPopdown(this);
|
|
|
|
return revealed;
|
|
}
|
|
|
|
void CPluginView::DrawSelf()
|
|
{
|
|
XP_ASSERT(!fHidden);
|
|
|
|
if (fPositioned == false) // Bail if layout hasnÕt positioned us yet
|
|
return;
|
|
|
|
if (fBrokenPlugin)
|
|
DrawBroken(false);
|
|
else
|
|
{
|
|
//
|
|
// Always reset the drawing info before calling the plugin to draw.
|
|
// Although the MoveBy and ResizeFrameBy methods ensure that the
|
|
// drawing info is updated if the plugin is scrolled or its window
|
|
// is resized, the clip still needs to be set up every time before
|
|
// drawing because the area to draw will be different.
|
|
//
|
|
ResetDrawRect();
|
|
|
|
|
|
if (fIsPrinting)
|
|
{
|
|
this->PrintEmbedded();
|
|
fIsPrinting = false;
|
|
}
|
|
else
|
|
{
|
|
EventRecord updateEvent;
|
|
::OSEventAvail(0, &updateEvent);
|
|
updateEvent.what = updateEvt;
|
|
(void) PassEvent(updateEvent);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CPluginView::SpendTime(const EventRecord& inMacEvent)
|
|
{
|
|
EventRecord macEvent = inMacEvent; // inMacEvent is const, but PassEvent isnÕt
|
|
(void) PassEvent(macEvent);
|
|
|
|
//
|
|
// If weÕre in SpendTime because of a non-null event, send a
|
|
// null event too. Some plug-ins (e.g. Shockwave) rely on null
|
|
// events for animation, so it doesnÕt matter if weÕre giving
|
|
// them lots of time with mouseMoved or other events -- if they
|
|
// donÕt get null events, they donÕt animate fast.
|
|
//
|
|
if (macEvent.what != nullEvent)
|
|
{
|
|
macEvent.what = nullEvent;
|
|
(void) PassEvent(macEvent);
|
|
}
|
|
}
|
|
|
|
|
|
void CPluginView::ActivateSelf()
|
|
{
|
|
EventRecord activateEvent;
|
|
::OSEventAvail(0, &activateEvent);
|
|
activateEvent.what = activateEvt;
|
|
activateEvent.modifiers = activeFlag;
|
|
(void) PassEvent(activateEvent);
|
|
}
|
|
|
|
|
|
void CPluginView::DeactivateSelf()
|
|
{
|
|
EventRecord activateEvent;
|
|
::OSEventAvail(0, &activateEvent);
|
|
activateEvent.what = activateEvt;
|
|
(void) PassEvent(activateEvent);
|
|
}
|
|
|
|
//
|
|
// These two methods are called when we are targeted or
|
|
// untargeted. Since plugins want to know when they have
|
|
// the key focus, we create a new event type to pass to
|
|
// them.
|
|
//
|
|
|
|
void CPluginView::BeTarget()
|
|
{
|
|
XP_ASSERT(!fHidden);
|
|
CPluginView::sPluginTarget = this; // Parallel to LCommander::sTarget, except only for plug-ins
|
|
EventRecord focusEvent;
|
|
::OSEventAvail(0, &focusEvent);
|
|
focusEvent.what = nsPluginEventType_GetFocusEvent;
|
|
Boolean handled = PassEvent(focusEvent);
|
|
if (!handled) // If the plugin doesnÕt want the focus,
|
|
SwitchTarget(GetSuperCommander()); // switch the focus back to our super
|
|
}
|
|
|
|
void CPluginView::DontBeTarget()
|
|
{
|
|
CPluginView::sPluginTarget = NULL; // Parallel to LCommander::sTarget, except only for plug-ins
|
|
EventRecord focusEvent;
|
|
::OSEventAvail(0, &focusEvent);
|
|
focusEvent.what = nsPluginEventType_LoseFocusEvent;
|
|
(void) PassEvent(focusEvent);
|
|
}
|
|
|
|
|
|
void CPluginView::AdjustCursorSelf(Point inPortPt, const EventRecord& inMacEvent)
|
|
{
|
|
XP_ASSERT(!fHidden);
|
|
EventRecord cursorEvent = inMacEvent;
|
|
cursorEvent.what = nsPluginEventType_AdjustCursorEvent;
|
|
|
|
Boolean handled = PassEvent(cursorEvent);
|
|
if (!handled)
|
|
LPane::AdjustCursorSelf(inPortPt, inMacEvent);
|
|
}
|
|
|
|
|
|
|
|
// ¥¥ positioning
|
|
|
|
void CPluginView::AdaptToSuperFrameSize(Int32 inSurrWidthDelta, Int32 inSurrHeightDelta, Boolean inRefresh)
|
|
{
|
|
LView::AdaptToSuperFrameSize(inSurrWidthDelta, inSurrHeightDelta, inRefresh);
|
|
|
|
//
|
|
// Our superview has changed size, so we could have changed size
|
|
// (if weÕre bound to the superview) or changed clip (if we now
|
|
// overlap the edges of the superview).
|
|
//
|
|
if (fWindowed)
|
|
ResetDrawRect();
|
|
|
|
//
|
|
// If we are bound to our superview, we will change size as it
|
|
// changes size. Since the superview (the hyperview) is in turn
|
|
// bound to the window, we should adjust our NPWindow structure
|
|
// (done above in ResetDrawRect) and tell the plugin about the
|
|
// change (by calling NPL_EmbedSize). -bing 11/15/95
|
|
//
|
|
if (fApp != NULL)
|
|
{
|
|
SBooleanRect frameBinding;
|
|
GetFrameBinding(frameBinding);
|
|
if (frameBinding.top && frameBinding.bottom && frameBinding.left && frameBinding.right)
|
|
NPL_EmbedSize(fApp);
|
|
}
|
|
}
|
|
|
|
void CPluginView::MoveBy(Int32 inHorizDelta, Int32 inVertDelta, Boolean inRefresh)
|
|
{
|
|
LView::MoveBy(inHorizDelta, inVertDelta, inRefresh);
|
|
if (fWindowed)
|
|
ResetDrawRect();
|
|
}
|
|
|
|
|
|
|
|
// ¥¥Êdragging
|
|
|
|
void CPluginView::AdaptToNewSurroundings()
|
|
{
|
|
LView::AdaptToNewSurroundings();
|
|
|
|
//
|
|
// Set the port for dragging here, since we need
|
|
// a superview to get the port, and we don't have
|
|
// a superview until this method is called.
|
|
//
|
|
LDropArea::RemoveDropArea((LDropArea*)this, mDragWindow);
|
|
mDragWindow = GetMacPort();
|
|
LDropArea::AddDropArea((LDropArea*)this, mDragWindow);
|
|
}
|
|
|
|
Boolean CPluginView::DragIsAcceptable(DragReference /*inDragRef*/)
|
|
{
|
|
//
|
|
// We claim to accept drops, so we won't get the
|
|
// Navigator drag&drop behavior over the plug-in.
|
|
// If the plug-in installs its own drag handlers,
|
|
// it will get control after we're called and can
|
|
// handle the drag as it sees fit.
|
|
//
|
|
return true;
|
|
}
|
|
|
|
void CPluginView::HiliteDropArea(DragReference /*inDragRef*/)
|
|
{
|
|
// DonÕt hilite: let the plug-in do it if it accepts drags.
|
|
}
|
|
|
|
void CPluginView::UnhiliteDropArea(DragReference /*inDragRef*/)
|
|
{
|
|
// DonÕt unhilite: let the plug-in do it if it accepts drags.
|
|
}
|
|
|
|
|
|
|
|
|
|
// ¥¥Êevents
|
|
|
|
// Passes the event to our plugin
|
|
Boolean CPluginView::PassEvent(EventRecord& inEvent)
|
|
{
|
|
if (fApp)
|
|
return NPL_HandleEvent(fApp, (NPEvent*)&inEvent, NULL);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
void CPluginView::ResetDrawRect()
|
|
{
|
|
fNPWindow.window = &fNPPort;
|
|
|
|
if (fWindowed) {
|
|
|
|
fNPPort.port = (CGrafPtr) GetMacPort();
|
|
fNPWindow.x = mImageLocation.h;
|
|
fNPWindow.y = mImageLocation.v;
|
|
fNPWindow.width = mFrameSize.width;
|
|
fNPWindow.height = mFrameSize.height;
|
|
|
|
if (mRevealedRect.left < mRevealedRect.right) // Code from FocusDraw
|
|
{ // We are visible
|
|
XP_ASSERT(!fHidden);
|
|
fNPPort.portx = mPortOrigin.h;
|
|
fNPPort.porty = mPortOrigin.v;
|
|
|
|
fNPWindow.clipRect.top = mRevealedRect.top;
|
|
fNPWindow.clipRect.left = mRevealedRect.left;
|
|
fNPWindow.clipRect.right = mRevealedRect.right;
|
|
fNPWindow.clipRect.bottom = mRevealedRect.bottom;
|
|
}
|
|
else // We are invisible
|
|
{
|
|
fNPWindow.clipRect.top = 0;
|
|
fNPWindow.clipRect.left = 0;
|
|
fNPWindow.clipRect.right = 0;
|
|
fNPWindow.clipRect.bottom = 0;
|
|
}
|
|
}
|
|
else {
|
|
Rect frame;
|
|
Point localPoint, portPoint;
|
|
SPoint32 imagePoint;
|
|
Rect clipRect;
|
|
Point portOrigin;
|
|
|
|
// XXX This is gross. We're getting our parent and casting it down to a
|
|
// CHTMLView so that we can get element position and port information from it.
|
|
CHTMLView *parentView = (CHTMLView *)GetSuperView();
|
|
|
|
// Get the parent's port and port origin. Note that this will be different
|
|
// depending on whether it's onscreen or offscreen.
|
|
fNPPort.port = (CGrafPtr) parentView->GetCurrentPort(portOrigin);
|
|
|
|
// Get the local position from the hyperview. This is a function of the
|
|
// location of the layout element, the scrolled position and the position
|
|
// of the layer containing the embed.
|
|
parentView->CalcElementPosition((LO_Element *)fEmbedStruct, frame);
|
|
|
|
// Convert it into image coordinates
|
|
localPoint.h = frame.left - (fEmbedStruct->objTag.x + fEmbedStruct->objTag.x_offset);
|
|
localPoint.v = frame.top - (fEmbedStruct->objTag.y + fEmbedStruct->objTag.y_offset);
|
|
portPoint = localPoint;
|
|
localPoint.h -= portOrigin.h;
|
|
localPoint.v -= portOrigin.v;
|
|
parentView->LocalToImagePoint(localPoint, imagePoint);
|
|
|
|
fNPWindow.x = imagePoint.h;
|
|
fNPWindow.y = imagePoint.v;
|
|
fNPWindow.width = frame.right - frame.left;
|
|
fNPWindow.height = frame.bottom - frame.top;
|
|
|
|
fNPPort.portx = -localPoint.h;
|
|
fNPPort.porty = -localPoint.v;
|
|
|
|
// The clipRect in this case is the bounding box of the clip region
|
|
// set by the compositor. Convert to port coordinates.
|
|
clipRect = (*fNPPort.port->clipRgn)->rgnBBox;
|
|
topLeft(clipRect).h -= portOrigin.h;
|
|
topLeft(clipRect).v -= portOrigin.v;
|
|
botRight(clipRect).h -= portOrigin.h;
|
|
botRight(clipRect).v -= portOrigin.v;
|
|
|
|
fNPWindow.clipRect.top = clipRect.top;
|
|
fNPWindow.clipRect.left = clipRect.left;
|
|
fNPWindow.clipRect.right = clipRect.right;
|
|
fNPWindow.clipRect.bottom = clipRect.bottom;
|
|
|
|
// Keep the view's position in sync with that specified by layout
|
|
parentView->LocalToPortPoint(portPoint);
|
|
if ((portPoint.h != -mPortOrigin.h) || (portPoint.v != -mPortOrigin.v)) {
|
|
MoveBy(mPortOrigin.h + portPoint.h, mPortOrigin.v + portPoint.v, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// This method is used for printing fullscreen plugins, which might
|
|
// want the opportunity to completely take over printing the page.
|
|
// We will pass them whether the user clicked on the print button
|
|
// or the Print menu (ÒprintOneÓ), a handle to the print record,
|
|
// and a boolean that they can set to true if they actually want to
|
|
// handle the print. We return this boolean to our caller (CHyperView:
|
|
// ObeyCommand) so they know whether they should print or not.
|
|
// If plugins do not implement the Print entry point at all, the
|
|
// boolean ÒpluginPrintedÓ will still be at its default value (false),
|
|
// so we will still print.
|
|
//
|
|
Boolean CPluginView::PrintFullScreen(Boolean printOne, THPrint printRecHandle)
|
|
{
|
|
if (fApp == NULL)
|
|
return false;
|
|
|
|
NPPrint npPrint;
|
|
npPrint.mode = NP_FULL;
|
|
npPrint.print.fullPrint.pluginPrinted = false;
|
|
npPrint.print.fullPrint.printOne = printOne;
|
|
npPrint.print.fullPrint.platformPrint = (void*) printRecHandle;
|
|
|
|
NPL_Print(fApp, (void*) &npPrint);
|
|
|
|
return npPrint.print.fullPrint.pluginPrinted;
|
|
}
|
|
|
|
void CPluginView::PrintEmbedded()
|
|
{
|
|
if (fApp == NULL)
|
|
return;
|
|
|
|
NPPrint npPrint;
|
|
npPrint.mode = NP_EMBED;
|
|
npPrint.print.embedPrint.window = fNPWindow;
|
|
npPrint.print.embedPrint.platformPrint = (void*) fNPWindow.window; // Pass the NP_Port
|
|
|
|
NPL_Print(fApp, (void*) &npPrint);
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPluginView::SetBrokenPlugin()
|
|
{
|
|
fBrokenPlugin = true;
|
|
fBrokenIcon = CIconList::GetIcon(326); // ¥¥¥ Need a constant
|
|
// Tell layout that we're windowed so we can display an icon
|
|
LO_SetEmbedType(fEmbedStruct, PR_TRUE);
|
|
Refresh(); // Plugin will draw broken icon now that fBrokenPlugin is set
|
|
}
|
|
|
|
|
|
void CPluginView::DrawBroken(Boolean hilite)
|
|
{
|
|
//
|
|
// If the plugin is broken, we donÕt have a valid plugin instance
|
|
// to ask to draw, so we should handle drawing ourselves.
|
|
// Current behavior is to draw a box with a broken plugin icon
|
|
// in it.
|
|
//
|
|
Rect drawRect;
|
|
if (this->CalcLocalFrameRect(drawRect)) // We want a local rect since SetOrigin has been called
|
|
{
|
|
short height = drawRect.bottom - drawRect.top;
|
|
short width = drawRect.right - drawRect.left;
|
|
|
|
if (height >= 4 && width >= 4)
|
|
{
|
|
UGraphics::SetFore(CPrefs::Anchor);
|
|
FrameRect(&drawRect);
|
|
|
|
if (height >= 32 && width >= 32 && fBrokenIcon != NULL)
|
|
{
|
|
short centerX = (drawRect.right - drawRect.left) >> 1;
|
|
short centerY = (drawRect.bottom - drawRect.top) >> 1;
|
|
drawRect.top = centerY - 16;
|
|
drawRect.bottom = centerY + 16;
|
|
drawRect.left = centerX - 16;
|
|
drawRect.right = centerX + 16;
|
|
|
|
::PlotCIconHandle(&drawRect, atAbsoluteCenter, hilite ? ttSelected : ttNone, fBrokenIcon);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef LAYERS
|
|
Boolean CPluginView::HandleEmbedEvent(CL_Event *event)
|
|
{
|
|
fe_EventStruct *fe_event = (fe_EventStruct *)event->fe_event;
|
|
|
|
EventRecord macEvent;
|
|
::OSEventAvail(0, &macEvent);
|
|
|
|
// For windowless plugins, the last draw might have been to the offscreen port,
|
|
// but for event handling, we want the current port to be the onscreen port (so
|
|
// that mouse coordinate conversion and the like can happen correctly).
|
|
if (!fWindowed)
|
|
FocusDraw();
|
|
|
|
switch (event->type) {
|
|
case CL_EVENT_MOUSE_BUTTON_DOWN:
|
|
{
|
|
//SMouseDownEvent *mouseDown = (SMouseDownEvent *)fe_event->event;
|
|
|
|
// Now pass the mouse event
|
|
//macEvent = mouseDown->macEvent;
|
|
// modified for new fe_EventStruct 1997-02-22 mjc
|
|
SMouseDownEvent mouseDown = fe_event->event.mouseDownEvent;
|
|
macEvent = mouseDown.macEvent;
|
|
}
|
|
break;
|
|
case CL_EVENT_MOUSE_BUTTON_UP:
|
|
case CL_EVENT_KEY_DOWN:
|
|
//macEvent = *(EventRecord *)fe_event->event;
|
|
macEvent = fe_event->event.macEvent; // 1997-02-22 mjc
|
|
break;
|
|
case CL_EVENT_MOUSE_MOVE:
|
|
//macEvent = *(EventRecord *)fe_event->event;
|
|
macEvent = fe_event->event.macEvent; // 1997-02-22 mjc
|
|
macEvent.what = nsPluginEventType_AdjustCursorEvent;
|
|
break;
|
|
case CL_EVENT_KEY_FOCUS_GAINED:
|
|
macEvent.what = nsPluginEventType_GetFocusEvent;
|
|
break;
|
|
case CL_EVENT_KEY_FOCUS_LOST:
|
|
macEvent.what = nsPluginEventType_LoseFocusEvent;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return PassEvent(macEvent);
|
|
}
|
|
#endif // LAYERS
|