home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / macfe / applevnt / mplugin.cp next >
Encoding:
Text File  |  1998-04-08  |  50.8 KB  |  1,811 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. // All the code-resource handling stuff has been adopted from:
  20. // Multi-Seg CR example in CW
  21. // Classes:
  22. // CPluginInstance - one per plugin instance. Needs to call the functions
  23. // CPluginHandler - one per plugin MIME type
  24.  
  25. #include "CHTMLView.h"
  26.  
  27. #include "macutil.h"
  28. #include "npapi.h"
  29. #include "mplugin.h"
  30. #include "uprefd.h"
  31. #include "ufilemgr.h"
  32. #include "uapp.h"    // might be unnecessary when we get rid of CFrontApp
  33. #include "resgui.h"
  34. #include "miconutils.h"
  35. #include "uerrmgr.h"
  36. #include "macgui.h"
  37. #include "resae.h"
  38. #include "edt.h"    // for EDT_RegisterPlugin()
  39. #include "CApplicationEventAttachment.h"
  40.  
  41. #include "java.h"    // for LJ_AddToClassPath
  42.  
  43. #include "np.h"
  44. #include "nppg.h"
  45. #include "prlink.h"
  46.  
  47. #include "npglue.h"
  48.  
  49. #ifdef LAYERS
  50. #include "layers.h"
  51. #include "mkutils.h"
  52. #endif // LAYERS
  53.  
  54. #include "MixedMode.h"
  55. #include "MoreMixedMode.h"
  56. #include <CodeFragments.h>
  57.  
  58. // On a powerPC, plugins depend on being able to find qd in our symbol table
  59. #ifdef powerc
  60. #pragma export qd
  61. #endif
  62.  
  63. const ResIDT nsPluginSig = 128;    // STR#, MIME type is #1
  64.  
  65. struct _np_handle;
  66.  
  67.  
  68. // *************************************************************************************
  69. //
  70. // class CPluginHandler
  71. //
  72. // *************************************************************************************
  73.  
  74. class CPluginHandler
  75. {
  76.         friend void FE_UnloadPlugin(void* plugin, struct _np_handle* handle);
  77.         friend NPPluginFuncs* FE_LoadPlugin(void* plugin, NPNetscapeFuncs *, struct _np_handle* handle);
  78.  
  79. public:
  80.  
  81. // ÑÑ constructors
  82.                                 CPluginHandler(FSSpec& plugFile);
  83.                                 ~CPluginHandler();
  84.  
  85. // ÑÑ xp->macfe
  86.     static    Boolean                CheckExistingHandlers(FSSpec& file);
  87.             NPPluginFuncs*         LoadPlugin(NPNetscapeFuncs * funcs, _np_handle* handle);
  88.             void                UnloadPlugin();
  89.  
  90. // plugin -> netscape
  91.     static    void                Status(const char * message);
  92.  
  93.     static    CPluginHandler*        FindHandlerForPlugin(const CStr255& pluginName);
  94.     
  95. private:
  96.  
  97. // ÑÑ funcs
  98.             OSErr                InitCodeResource(NPNetscapeFuncs * funcs, _np_handle* handle);
  99.             void                CloseCodeResource();
  100.             void                ResetPlugFuncTable();
  101.             void                OpenPluginResourceFork(Int16 inPrivileges);
  102.             
  103. // ÑÑ vars
  104.             short                fRefCount;
  105.             PRLibrary*            fLibrary;            // For PPC
  106.             Handle                fCode;                // For 68K
  107.             NPPluginFuncs        fPluginFuncs;        // xp->plugin
  108.             NPP_MainEntryUPP    fMainEntryFunc;
  109.             NPP_ShutdownUPP        fUnloadFunc;
  110.             LFile*                 fFile;                // File containing the plugin
  111.             CStr255                fPluginName;        // Name of plug-in
  112.             
  113.             CPluginHandler*        fNextHandler;        // Link to next handler in the global list
  114.     static    CPluginHandler*        sHandlerList;        // Global list of all handlers
  115. };
  116.  
  117. CPluginHandler* CPluginHandler::sHandlerList = NULL;
  118.  
  119. void* GetPluginDesc(const CStr255& pluginName)
  120. {
  121.     return CPluginHandler::FindHandlerForPlugin(pluginName);
  122. }
  123.     
  124. //
  125. // Find a handler given a plug-in name (currently only
  126. // called from CFrontApp:RegisterMimeType).
  127. //
  128. CPluginHandler*    CPluginHandler::FindHandlerForPlugin(const CStr255& pluginName)
  129. {
  130.     CPluginHandler* handler = CPluginHandler::sHandlerList;
  131.     while (handler != NULL)
  132.     {
  133.         if (handler->fPluginName == pluginName)
  134.             return handler;
  135.  
  136.         handler = handler->fNextHandler;
  137.     }
  138.     
  139.     return nil;
  140. }
  141.  
  142.  
  143. //
  144. // Check the handlers we already have registered against
  145. // the given file spec.  We don╒t want to redundantly
  146. // create handlers for the same plugin file.
  147. //
  148. Boolean CPluginHandler::CheckExistingHandlers(FSSpec& file)
  149. {
  150.     CPluginHandler* handler = CPluginHandler::sHandlerList;
  151.     while (handler != NULL)
  152.     {
  153.         if (handler->fFile)
  154.         {
  155.             FSSpec existingFile;
  156.             handler->fFile->GetSpecifier(existingFile);
  157.             if (existingFile.vRefNum == file.vRefNum && existingFile.parID == file.parID)
  158.             {
  159.                 if (EqualString(existingFile.name, file.name, true, true))
  160.                     return false;
  161.             }
  162.         }
  163.         handler = handler->fNextHandler;
  164.     }
  165.     return true;
  166. }
  167.  
  168.  
  169. // ÑÑ constructors
  170.  
  171. CPluginHandler::CPluginHandler(FSSpec& plugFile)
  172. {
  173.     // Variables
  174.     fRefCount = 0;
  175.     fCode = NULL;
  176.     fLibrary = NULL;
  177.     fFile = NULL;
  178.     fMainEntryFunc = NULL;
  179.     fUnloadFunc = NULL;
  180.     fNextHandler = NULL;
  181.     fPluginName = "";
  182.  
  183.     Try_
  184.     {
  185.         fFile = new LFile(plugFile);
  186.         fFile->OpenResourceFork(fsRdPerm);
  187.  
  188.         // Look for the plug-in description resource
  189.         CStringListRsrc descRsrc(126);
  190.         CStr255 pluginDescription;
  191.         
  192.         // Read plug-in description if available; if not, use blank description
  193.         short descCount = descRsrc.CountStrings();
  194.         if (descCount > 0)
  195.             descRsrc.GetString(1, pluginDescription);
  196.         if (descCount <= 0 || ResError())
  197.             pluginDescription = "";
  198.         
  199.         // Read plug-in name if available; if not, use file name
  200.         if (descCount > 1)
  201.             descRsrc.GetString(2, fPluginName);
  202.         if (descCount <= 1 || ResError())
  203.             fPluginName = plugFile.name;
  204.  
  205.         cstring fileName((const unsigned char*) plugFile.name);
  206.         NPError err = NPL_RegisterPluginFile(fPluginName, fileName, pluginDescription, this);
  207.         ThrowIfOSErr_(err);
  208.  
  209.         CStringListRsrc mimeList(128);
  210.         CStringListRsrc descList(127);
  211.         
  212.         UInt32 mimeTotal = mimeList.CountStrings() >> 1;        // 2 strings per entry
  213.         UInt32 descTotal = descList.CountStrings();
  214.         UInt32 count, mimeIndex, descIndex;
  215.         ThrowIf_(mimeTotal == 0);
  216.         
  217.         for (count = mimeIndex = descIndex = 1; count <= mimeTotal; count++, mimeIndex += 2, descIndex++)
  218.         {
  219.             CStr255 mimeType;
  220.             CStr255 extensions;
  221.             CStr255 description;
  222.             
  223.             mimeList.GetString(mimeIndex, mimeType);
  224.             if (ResError())
  225.                 continue;
  226.                 
  227.             mimeList.GetString(mimeIndex + 1, extensions);
  228.             if (ResError())
  229.                 continue;
  230.                 
  231.             //
  232.             // Get the description string from the plug-in.
  233.             // If they didn't specify one, use a pre-existing
  234.             // description if it exists (e.g. we know by default
  235.             // that video/quicktime is "Quicktime Video"). If
  236.             // we still didn't find a description, just use the
  237.             // MIME type.
  238.             //
  239.             Boolean gotDescription = false;
  240.             if (descIndex <= descTotal)
  241.             {
  242.                 descList.GetString(descIndex, description);
  243.                 gotDescription = (ResError() == noErr);
  244.             }
  245.             if (!gotDescription)
  246.             {
  247.                 NET_cdataStruct temp;
  248.                 NET_cdataStruct* cdata;
  249.                 char* mimetype = (char*) mimeType;
  250.                 
  251.                 memset(&temp, 0, sizeof(temp));
  252.                 temp.ci.type = mimetype;
  253.                 cdata = NET_cdataExist(&temp);
  254.                 if (cdata && cdata->ci.desc)
  255.                     description = cdata->ci.desc;
  256.                 else
  257.                     description = mimeType;
  258.             }
  259.  
  260.             //
  261.             // Look up this type in the MIME table.  If it's not found, then
  262.             // the user hasn't seen this plug-in type before, so make a new
  263.             // mapper for the table with the type defaulted to be handled by
  264.             // the plug-in.  If it was found, but was from an old prefs file
  265.             // (that didn't contain plug-in information), make sure the plug-
  266.             // in still gets priority so users don't freak out the first time
  267.             // they run a new Navigator and the plug-ins stop working.  If it
  268.             // was found but was latent (disabled because the plug-in was
  269.             // missing), re-enable it now because the plug-in is present again.
  270.             //
  271.             cstring type((const unsigned char*) mimeType);
  272.             CMimeMapper* mapper = CPrefs::sMimeTypes.FindMimeType(type);
  273.             if (mapper == NULL)
  274.             {
  275.                 mapper = new CMimeMapper(CMimeMapper::Plugin, mimeType, "", extensions, '????', '????');
  276.                 ThrowIfNil_(mapper);
  277.                 mapper->SetPluginName(fPluginName);
  278.                 mapper->SetDescription(description);
  279.                 CPrefs::sMimeTypes.InsertItemsAt(1, LArray::index_Last, &mapper);
  280.                 CPrefs::SetModified();
  281.                 mapper->WriteMimePrefs();            // convert mapper to xp prefs
  282.             }
  283.             else if (mapper->FromOldPrefs())
  284.             {
  285.                 mapper->SetPluginName(fPluginName);
  286.                 mapper->SetExtensions(extensions);
  287.                 mapper->SetDescription(description);
  288.                 mapper->SetLoadAction(CMimeMapper::Plugin);
  289.                 CPrefs::SetModified();
  290.                 mapper->WriteMimePrefs();            // convert mapper to xp prefs
  291.             }
  292.             else if (mapper->LatentPlugin())
  293.             {
  294.                 XP_ASSERT(mapper->GetLoadAction() == CMimeMapper::Unknown);
  295.                 mapper->SetLoadAction(CMimeMapper::Plugin);
  296.                 CPrefs::SetModified();
  297.             }
  298.             else
  299.             {
  300.                 //
  301.                 // Only update the description if (a) there╒s a description
  302.                 // from the plug-in itself (as opposed to a default description),
  303.                 // and (b) the current description is blank (we don╒t want to
  304.                 // overwrite a user-edited description).
  305.                 //
  306.                 if (gotDescription && mapper->GetDescription() == "")
  307.                 {
  308.                     mapper->SetDescription(description);
  309.                     CPrefs::SetModified();
  310.                 }
  311.             }
  312.             
  313.             // Now we know whether the plug-in should be enabled or not
  314.             Boolean enabled = ((mapper->GetLoadAction() == CMimeMapper::Plugin) &&
  315.                                (fPluginName == mapper->GetPluginName()));
  316.             NPL_RegisterPluginType(type,  (char*) mapper->GetExtensions(), (char*) mapper->GetDescription(), NULL, this, enabled);
  317.         }
  318.         
  319.         fFile->CloseResourceFork();
  320.         
  321.     }
  322.     Catch_(inErr)
  323.     {
  324.         if (fFile != NULL)
  325.             fFile->CloseResourceFork();
  326.             
  327.     }        
  328.     EndCatch_;
  329.     
  330.     ResetPlugFuncTable();
  331.  
  332.     // Link us into the global list of handlers
  333.     fNextHandler = CPluginHandler::sHandlerList;
  334.     CPluginHandler::sHandlerList = this;
  335. }
  336.  
  337.  
  338. CPluginHandler::~CPluginHandler()            // This code never gets called
  339. {
  340.     CloseCodeResource();
  341.     
  342.     if (fFile)
  343.         delete fFile;
  344.         
  345.     if (this == CPluginHandler::sHandlerList)
  346.         CPluginHandler::sHandlerList = NULL;
  347.     else
  348.     {
  349.         CPluginHandler* handler = CPluginHandler::sHandlerList;
  350.         while (handler != NULL)
  351.         {
  352.             if (this == handler->fNextHandler)
  353.             {
  354.                 handler->fNextHandler = this->fNextHandler;
  355.                 break;
  356.             }
  357.             handler = handler->fNextHandler;
  358.         }
  359.     }
  360. }
  361.  
  362.  
  363. void CPluginHandler::ResetPlugFuncTable()
  364. {
  365.     fMainEntryFunc = NULL;
  366.     fUnloadFunc = NULL;
  367.     memset(&fPluginFuncs, 0, sizeof(NPPluginFuncs));
  368. }
  369.  
  370.  
  371. NPPluginFuncs * CPluginHandler::LoadPlugin(NPNetscapeFuncs * funcs, _np_handle* handle)
  372. {
  373.     // XXX Needs to be fixed for C++ Plugin API
  374.     OSErr err = InitCodeResource(funcs, handle);
  375.     if (err == noErr)
  376.         return &fPluginFuncs;
  377.     
  378.     return NULL;
  379. }
  380.  
  381. OSErr CPluginHandler::InitCodeResource(NPNetscapeFuncs* funcs, _np_handle* handle)
  382. {
  383.     fRefCount++;
  384.     
  385.     if (fCode != NULL || fLibrary != 0)        // Already loaded
  386.         return noErr;
  387.         
  388.     OSErr err = noErr;
  389. #ifdef DEBUG
  390.     EDebugAction oldThrow = gDebugThrow;
  391.     EDebugAction oldSignal = gDebugSignal;
  392.     gDebugThrow = debugAction_Nothing;
  393.     gDebugSignal = debugAction_Nothing;
  394. #endif
  395.     Try_    
  396.     {
  397.         OpenPluginResourceFork(fsRdWrPerm);
  398.         Try_
  399.         {    // PPC initialization
  400.  
  401.             if (!UEnvironment::HasGestaltAttribute(gestaltCFMAttr, gestaltCFMPresent))
  402.                 Throw_((OSErr)cfragNoLibraryErr);    // No CFM
  403.  
  404.             FSSpec fileSpec;
  405.             fFile->GetSpecifier(fileSpec);
  406.             
  407.             char* cFullPath;
  408.             cFullPath = CFileMgr::PathNameFromFSSpec(fileSpec, TRUE);
  409.             ThrowIfNil_(cFullPath);
  410.             
  411.             fLibrary = PR_LoadLibrary(cFullPath);
  412.             ThrowIfNil_(fLibrary);
  413.  
  414.             // Try the new C++ interface plugin interface.
  415.             NP_CREATEPLUGIN npCreatePlugin = NULL;
  416. #ifndef NSPR20
  417.             npCreatePlugin = (NP_CREATEPLUGIN)PR_FindSymbol("NP_CreatePlugin", fLibrary);
  418. #else
  419.             npCreatePlugin = (NP_CREATEPLUGIN)PR_FindSymbol(fLibrary, "NP_CreatePlugin");
  420. #endif
  421.             if (npCreatePlugin != NULL) {
  422.                 if (thePluginManager == NULL) {
  423.                     static NS_DEFINE_IID(kIPluginManagerIID, NP_IPLUGINMANAGER_IID);
  424.                     if (nsPluginManager::Create(NULL, kIPluginManagerIID, (void**)&thePluginManager) != NS_OK)
  425.                         return NULL;
  426.                 }
  427.                 NPIPlugin* plugin = NULL;
  428.                 NPPluginError pluginErr = npCreatePlugin(thePluginManager, &plugin);
  429.                 handle->userPlugin = plugin;
  430.                 ThrowIf_(pluginErr != NPPluginError_NoError || plugin == NULL);
  431.             } else {
  432. #ifndef NSPR20
  433.                 fMainEntryFunc = (NPP_MainEntryUPP) PR_FindSymbol("mainRD", fLibrary);
  434. #else
  435.                 fMainEntryFunc = (NPP_MainEntryUPP) PR_FindSymbol(fLibrary, "mainRD");
  436. #endif
  437.                 ThrowIfNil_(fMainEntryFunc);
  438.             }
  439.         }
  440.         Catch_(inErr)
  441.         {    // 68K if PPC fails
  442.             fLibrary = NULL;    // No PPC
  443.             fCode = Get1Resource(emPluginFile, 128);
  444.             ThrowIfNil_(fCode);
  445.             ::MoveHHi(fCode);
  446.             ::HNoPurge(fCode);
  447.             ::HLock(fCode);
  448.  
  449.             //get the address of main and typecast it into a MainEntryProcUPP
  450.             fMainEntryFunc = (NPP_MainEntryUPP)(*fCode);
  451.         } EndCatch_
  452.         
  453.         if (fMainEntryFunc != NULL) {
  454.             fPluginFuncs.version = funcs->version;
  455.             fPluginFuncs.size = sizeof(NPPluginFuncs);
  456.             err = CallNPP_MainEntryProc(fMainEntryFunc, funcs, &fPluginFuncs, &fUnloadFunc);        
  457.             ThrowIfOSErr_(err);
  458.             if ((fPluginFuncs.version >> 8) < NP_VERSION_MAJOR)
  459.                 Throw_((OSErr)NPERR_INCOMPATIBLE_VERSION_ERROR);
  460.         }
  461.     }
  462.     Catch_(inErr)
  463.     {
  464.         CloseCodeResource();
  465.         err = inErr;
  466.     }
  467.     EndCatch_
  468.     CPrefs::UseApplicationResFile();    // Revert the resource chain
  469. #ifdef DEBUG
  470.     gDebugThrow = oldThrow;
  471.     gDebugSignal = oldSignal;
  472. #endif
  473.     return err;
  474. }
  475.  
  476. // Dispose of the code
  477. void CPluginHandler::CloseCodeResource()
  478. {
  479.     // Already unloaded?
  480.     if (fRefCount == 0)
  481.         return;
  482.         
  483.     // If there are still outstanding references, don't unload yet
  484.     fRefCount--;
  485.     if (fRefCount > 0)
  486.         return;
  487.     
  488.     if (fUnloadFunc != NULL)
  489.         CallNPP_ShutdownProc(fUnloadFunc);
  490.  
  491. // Dispose of the code
  492. // 68K, just release the resource
  493.     if (fCode != NULL)    
  494.     {
  495.         ::HUnlock(fCode);
  496.         ::HPurge(fCode);
  497.         ::ReleaseResource(fCode);
  498.         fCode = NULL;
  499.     }
  500. // PPC, close the connections
  501.     if (fLibrary != NULL)
  502.     {
  503.         int err = PR_UnloadLibrary(fLibrary);
  504.         Assert_(err == 0);
  505.         fLibrary = NULL;
  506.     }
  507.  
  508.     ResetPlugFuncTable();
  509.     
  510.     Try_
  511.     {
  512.         fFile->CloseResourceFork();
  513.     }
  514.     Catch_(inErr){} EndCatch_
  515.     Try_
  516.     {
  517.         fFile->CloseDataFork();
  518.     }
  519.     Catch_(inErr){} EndCatch_
  520. }
  521.  
  522.  
  523. typedef struct ResourceMapEntry ResourceMapEntry, **ResourceMapHandle;
  524.  
  525. // See More Macintosh Toolbox, pp. 1-122, 1-123.
  526. #if PRAGMA_ALIGN_SUPPORTED
  527. #pragma options align=mac68k
  528. #endif
  529. struct ResourceMapEntry
  530. {
  531.     UInt32                dataOffset;
  532.     UInt32                mapOffset;
  533.     UInt32                dataLength;
  534.     UInt32                mapLength;
  535.     ResourceMapHandle    nextMap;
  536.     UInt16                refNum;
  537. };
  538. #if PRAGMA_ALIGN_SUPPORTED
  539. #pragma options align=reset
  540. #endif
  541.  
  542. //
  543. // Open the plug-in╒s resource fork BELOW the the application╒s
  544. // resource fork.  If we just opened the plug-in above the
  545. // application but set the current res file to the app, the
  546. // plug-in will be made current by the Resource Manager if the
  547. // current res file is closed, which can happen when we close
  548. // our prefs file after writing something to it.  Once the plug-
  549. // in is the current res file, if there are any resource conflicts
  550. // between the two we╒ll get the plug-in╒s resource instead of
  551. // ours, causing woe.
  552. //
  553. void CPluginHandler::OpenPluginResourceFork(Int16 inPrivileges)
  554. {
  555.     Try_
  556.     {
  557.         Int16 plugRef = fFile->OpenResourceFork(inPrivileges);        // Open normally
  558.         
  559.         // The plug-in should now be at the top of the chain
  560.         ResourceMapHandle plugMap = (ResourceMapHandle) LMGetTopMapHndl();
  561.         ThrowIf_((**plugMap).refNum != plugRef);
  562.         
  563.         // Search down the chain to find the app╒s map
  564.         short appRef = LMGetCurApRefNum();
  565.         ResourceMapHandle appMap = (ResourceMapHandle) LMGetTopMapHndl();
  566.         while (appMap && (**appMap).refNum != appRef)
  567.             appMap = (**appMap).nextMap;
  568.         ThrowIfNil_(appMap);
  569.         
  570.         // Remove the plug-in from the top of the chain
  571.         ResourceMapHandle newTop = (**plugMap).nextMap;
  572.         ThrowIfNil_(newTop);
  573.         LMSetTopMapHndl((Handle)newTop);
  574.         
  575.         // Re-insert the plug-in below the application
  576.         (**plugMap).nextMap = (**appMap).nextMap;
  577.         (**appMap).nextMap = plugMap;
  578.     }
  579.     Catch_(inErr) 
  580.     {
  581.     }
  582.     EndCatch_
  583. }
  584.  
  585.  
  586.  
  587.  
  588.  
  589. void RegisterPluginsInFolder(FSSpec folder);
  590. // Recursive registration of all plugins
  591. void RegisterPluginsInFolder(FSSpec folder)
  592. {
  593.     // find all the files that are plugins
  594.     CFileIter iter(folder);
  595.     FSSpec plugFile;
  596.     FInfo finderInfo;
  597.     Boolean isFolder;
  598.     while (iter.Next(plugFile, finderInfo, isFolder))
  599.     {
  600.         if (isFolder)
  601.             RegisterPluginsInFolder(plugFile);
  602.         else if (finderInfo.fdType == emPluginFile)
  603.         {
  604.             //
  605.             // Check the handlers we already have registered against
  606.             // the given file spec.  We don╒t want to redundantly
  607.             // create handlers for the same plugin file.
  608.             //
  609.             if (CPluginHandler::CheckExistingHandlers(plugFile))
  610.             {
  611.                 Try_
  612.                 {
  613.                     CPluginHandler* plug = new CPluginHandler(plugFile);
  614.                 }
  615.                 Catch_(inErr){}
  616.                 EndCatch_
  617.             }
  618.         }
  619. #ifdef EDITOR
  620.         else {
  621.             unsigned char *name = &plugFile.name[0];
  622.             int    len = name[0];
  623.             
  624.             /* This checks to see if the name starts with cp and ends in '.zip' or '.jar'.
  625.             */
  626.             if (len >= 5) {
  627.                 if ((name[1] == 'c' || name[1] == 'C')
  628.                 && (name[2] == 'p' || name[2] == 'P')
  629.                 && (name[len - 3] == '.')
  630.                 && ((name[len - 2] == 'z' || name[len - 2] == 'Z')
  631.                 && (name[len - 1] == 'i' || name[len - 1] == 'I')
  632.                 && (name[len - 0] == 'p' || name[len - 0] == 'P'))
  633.                 || ((name[len - 2] == 'j' || name[len - 2] == 'J')
  634.                 && (name[len - 1] == 'a' || name[len - 1] == 'A')
  635.                 && (name[len - 0] == 'r' || name[len - 0] == 'R'))) {
  636.                 char *cFullPath = CFileMgr::PathNameFromFSSpec( plugFile, TRUE );
  637.                 ThrowIfNil_(cFullPath);
  638.                 char* cUnixFullPath = CFileMgr::EncodeMacPath(cFullPath);    // Frees cFullPath
  639.                 ThrowIfNil_(cUnixFullPath);
  640.                 (void) NET_UnEscape(cUnixFullPath);
  641.  
  642.                 EDT_RegisterPlugin(cUnixFullPath);
  643.                 XP_FREE(cUnixFullPath);
  644.                 }
  645.             }
  646.         }
  647. #endif // EDITOR
  648.     }
  649.     
  650.     //
  651.     // Tell Java to look in this directory for class files
  652.     // so LiveConnect-enabled plug-ins can store their classes
  653.     // with the plug-in.
  654.     //
  655.     Try_
  656.     {
  657.         char* cFullPath = CFileMgr::PathNameFromFSSpec(folder, TRUE);
  658.         ThrowIfNil_(cFullPath);
  659.         char* cUnixFullPath = CFileMgr::EncodeMacPath(cFullPath);    // Frees cFullPath
  660.         ThrowIfNil_(cUnixFullPath);
  661.         (void) NET_UnEscape(cUnixFullPath);
  662.         
  663.         // Tell Java about this path name
  664.         LJ_AddToClassPath(cUnixFullPath);
  665.         XP_FREE(cUnixFullPath);
  666.     }
  667.     Catch_(inErr) {}
  668.     EndCatch_
  669. }
  670.  
  671. //
  672. // Default handler registered below for formats FO_CACHE_AND_EMBED and
  673. // FO_CACHE, across all types.  This handler recovers the pointer to
  674. // our CPluginView and tells it that it does not have a valid plugin
  675. // instance.
  676. //
  677. NET_StreamClass* EmbedDefault(
  678.     int format_out, void* registration, URL_Struct* request, MWContext* context);
  679. NET_StreamClass* EmbedDefault(
  680.     int /*format_out*/, void* /*registration*/, URL_Struct* request, MWContext*)
  681. {
  682.     if (request != NULL)
  683.     {
  684.         NPEmbeddedApp* fe_data = (NPEmbeddedApp*) request->fe_data;
  685.         if (fe_data != NULL)
  686.         {
  687.             CPluginView* plugin = (CPluginView*) fe_data->fe_data;
  688.             if (plugin != NULL)
  689.                 plugin->SetBrokenPlugin();
  690.         }
  691.     }
  692.     return NULL;
  693. }
  694.  
  695. /* Returns the plugin folder spec */
  696. OSErr FindPluginFolder(FSSpec * plugFolder, Boolean create);
  697. OSErr FindPluginFolder(FSSpec * plugFolder, Boolean create)
  698. {
  699.     FSSpec    netscapeSpec = CPrefs::GetFolderSpec(CPrefs::NetscapeFolder);
  700.     long        netscape_dirID;
  701.  
  702.  
  703.     OSErr err;
  704.     if ( (err=CFileMgr::GetFolderID(netscapeSpec, netscape_dirID)) == noErr )
  705.         {
  706. #ifndef PLUGIN_FOLDER_NAME_MUST_BE_MOVED_TO_A_RESOURCE
  707.                 /*
  708.                     Yes! It's a bugus preprocessor symbol, but it got your attention, didn't it?
  709.  
  710.                     For localization, the plugin folder name should be moved to a resource, however there is no reported
  711.                     bug yet (and we're currently under change control).
  712.  
  713.                     ***Move only the preferred name into a resource.  Leave the old name as a constant string in the source
  714.                     (since the old name will not have been localized on any users disk).***
  715.  
  716.                     In the meantime...
  717.                 */
  718.  
  719.             Str255 pluginFolderName = "\pPlug-ins";
  720. #else
  721.                 // The preferred (localized) name of the plugins folder is in a resource...
  722.             Str255 pluginFolderName;
  723.             GetIndString(pluginFolderName, ..., ...);
  724. #endif
  725.  
  726.  
  727.             FSSpec    oldNameSpec;
  728.             Boolean    isFolder, wasAliased;
  729.  
  730.                 // first look for the preferences folder with the preferred name
  731.             if ( ((err=FSMakeFSSpec(netscapeSpec.vRefNum, netscape_dirID, pluginFolderName, plugFolder)) == noErr)        // ...if we made the FSSpec ok
  732.                 && ((err=ResolveAliasFile(plugFolder, true, &isFolder, &wasAliased)) == noErr) )                                                // ...and it leads to something
  733.                 {
  734.                     // the plugins folder already exists as "Plug-ins", nothing else to do
  735.                 }
  736.  
  737.                 // else, try the old name (using |oldNameSpec|, so that if not found, I can create a folder from the preferred spec created above)
  738.             else if ( ((err=FSMakeFSSpec(netscapeSpec.vRefNum, netscape_dirID, "\pPlugins", &oldNameSpec)) == noErr)    // ...if we made the FSSpec ok
  739.                          && ((err=ResolveAliasFile(&oldNameSpec, true, &isFolder, &wasAliased)) == noErr) )                                    // ...and it leads to something
  740.                 {
  741.                         // the plugins folder already exists as [deprecated] "Plugins", copy the spec we used to learn that to the caller
  742.                     FSMakeFSSpec(oldNameSpec.vRefNum, oldNameSpec.parID, oldNameSpec.name, plugFolder);
  743.                 }
  744.  
  745.                 // otherwise, we may need to create it
  746.             else if ( create )
  747.                 {
  748.                     long created_dirID;
  749.                     err = FSpDirCreate(plugFolder, smSystemScript, &created_dirID);
  750.                 }
  751.         }
  752.  
  753.     return err;
  754. }
  755.  
  756. // *************************************************************************************
  757. //
  758. // XP interface methods
  759. //
  760. // *************************************************************************************
  761.  
  762. void FE_RegisterPlugins();
  763. void FE_RegisterPlugins()
  764. {
  765.     //
  766.     // Register a wildcard handler for "embed" types before we scan the 
  767.     // plugins folder -- that way, if a "null plugin" is found that
  768.     // also registers "*"/FO_EMBED, it will be registered later than this
  769.     // handler and thus take precendence.
  770.     //
  771.     NET_RegisterContentTypeConverter("*", FO_EMBED, NULL, EmbedDefault);
  772.  
  773.     OSErr err;
  774.     FSSpec plugFolder;
  775.  
  776.     if (!IsThisKeyDown(kOptionKey))            // Skip loading plugins if option key is down
  777.         err = FindPluginFolder(&plugFolder, false);
  778.     else
  779.         err = -1;
  780.  
  781.     if (err != noErr)
  782.     {
  783.         CFrontApp::SplashProgress(GetPString(MAC_NO_PLUGIN));
  784.         return;
  785.     }
  786.     else
  787.     {
  788.         CFrontApp::SplashProgress(GetPString(MAC_REGISTER_PLUGINS));
  789.         RegisterPluginsInFolder(plugFolder);
  790.     }
  791.     
  792.     //
  793.     // Now that all plug-ins have been registered, look through the MIME table
  794.     // for types that are assigned to non-existant plug-ins or plug-in types,
  795.     // and change all these types to use "Unknown".
  796.     //
  797.     LArrayIterator iterator(CPrefs::sMimeTypes);
  798.     CMimeMapper* mapper;
  799.     while (iterator.Next(&mapper))
  800.     {
  801.         if (mapper->GetLoadAction() == CMimeMapper::Plugin)                // Handled by plug-in?
  802.         {
  803.             Boolean foundType = false;
  804.             char* otherPluginName = NULL;
  805.             
  806.             // Look for the desired plug-in
  807.             CStr255 mapperName = mapper->GetPluginName();
  808.             NPReference plugin = NPRefFromStart;
  809.             char* name;
  810.             while (NPL_IteratePluginFiles(&plugin, &name, NULL, NULL))
  811.             {
  812.                 // Look for the desired type
  813.                 CStr255 mapperMime = mapper->GetMimeName();
  814.                 NPReference mimetype = NPRefFromStart;
  815.                 char* type;
  816.                 while (NPL_IteratePluginTypes(&mimetype, plugin, &type, NULL, NULL, NULL))
  817.                 {
  818.                     if (mapperMime == type)
  819.                     {
  820.                         if (mapperName != name)
  821.                             otherPluginName = name;
  822.                         else
  823.                             foundType = true;
  824.                         break;
  825.                     }
  826.                 }
  827.                 
  828.             }
  829.  
  830.             if (!foundType)                                        // No plug-in, or no type?
  831.             {
  832.                 if (otherPluginName)
  833.                 {
  834.                     void* pdesc = GetPluginDesc(otherPluginName);
  835.                     if (pdesc)
  836.                         NPL_EnablePluginType((char*)mapper->GetMimeName(), pdesc, TRUE);
  837.                     mapper->SetPluginName(otherPluginName);
  838.                     CPrefs::SetModified();
  839.                 }
  840.                 else
  841.                 {
  842.                     mapper->SetLatentPlugin();                        // Mark the plug-in as ╥disabled because missing╙
  843.                     CPrefs::SetModified();                            // The prefs have changed
  844.                 }
  845.             }
  846.         }
  847.     }
  848. }
  849.  
  850.  
  851.  
  852. NPPluginFuncs * FE_LoadPlugin(void *plugin, NPNetscapeFuncs * funcs, struct _np_handle* handle)
  853. {
  854.     return ((CPluginHandler*)plugin)->LoadPlugin(funcs, handle);
  855. }
  856.  
  857. void FE_UnloadPlugin(void *plugin, struct _np_handle* handle)
  858. {
  859. #pragma unused(handle)    // PCB: for now.
  860.     ((CPluginHandler*)plugin)->CloseCodeResource();
  861. }
  862.  
  863. //
  864. // This exit routine is called when an embed stream completes.
  865. // We use that opportunity to see if something when wrong when
  866. // creating the initial stream for the plug-in so we can 
  867. // display some helpful UI.
  868. //
  869. void FE_EmbedURLExit(URL_Struct* /*urls*/, int /*status*/, MWContext* /*cx*/)
  870. {
  871. // bing: Don't do anything here until we figure out a 
  872. }
  873.  
  874.  
  875. //
  876. // This code is called by npn_status, which is called by the plug-in.
  877. // The plug-in may have manipulated the port before calling us, so
  878. // we have to assume that we're out of focus so we'll draw in the
  879. // right place.  We also need to save and restore the entire port
  880. // state so the plug-in's port settings are trashed.
  881. //
  882. void FE_PluginProgress(MWContext* cx, const char* message)
  883. {
  884.     GrafPtr currentPort;
  885.     GetPort(¤tPort);
  886.     StColorPortState portState(currentPort);
  887.     portState.Save(currentPort);
  888.     StUseResFile resFile(LMGetCurApRefNum());
  889.         
  890.     LView::OutOfFocus(nil);        // Make sure FE_Progress focuses the caption
  891.     FE_Progress(cx, message);
  892.     LView::OutOfFocus(nil);        // portState.Restore will screw up the focus again
  893.     
  894.     portState.Restore();
  895. }
  896.  
  897.  
  898. //
  899. // See comments in npglue.c in np_geturlinternal.
  900. //
  901. void FE_ResetRefreshURLTimer(MWContext* )
  902. {
  903. Assert_(false);
  904. //    if (NETSCAPEVIEW(context))
  905. //        NETSCAPEVIEW(context)->ResetRefreshURLTimer();
  906. }
  907.  
  908. // A generic way to query information. Nothing to do now.
  909. NPError FE_PluginGetValue(MWContext *, NPEmbeddedApp *, NPNVariable ,
  910.                           void */*r_value*/)
  911. {
  912.     return NPERR_NO_ERROR;
  913. }
  914.  
  915. void FE_RegisterWindow(void *plugin, void* window)
  916. {
  917.     ((CPluginView*)plugin)->RegisterWindow(window);
  918. }
  919.  
  920. void FE_UnregisterWindow(void *plugin, void* window)
  921. {
  922.     ((CPluginView*)plugin)->UnregisterWindow(window);
  923. }
  924.  
  925. // *************************************************************************************
  926. //
  927. // CPluginView methods
  928. //
  929. // *************************************************************************************
  930.  
  931.  
  932. CPluginView* CPluginView::sPluginTarget = NULL;
  933. LArray* CPluginView::sPluginList = NULL;
  934.  
  935. //
  936. // This static method lets the caller broadcast a mac event to
  937. // all existing plug-ins (regardless of context).  It╒s used by
  938. // CFrontApp::EventSuspendResume to broadcast suspend and resume
  939. // events to plug-ins.
  940. //
  941. void CPluginView::BroadcastPluginEvent(const EventRecord& event)
  942. {
  943.     if (sPluginList != NULL)
  944.     {
  945.         EventRecord eventCopy = event;        // event is const, but PassEvent isn╒t
  946.         LArrayIterator iterator(*sPluginList);
  947.         CPluginView* plugin = NULL;
  948.         while (iterator.Next(&plugin))
  949.             (void) plugin->PassEvent(eventCopy);
  950.     }
  951. }
  952.  
  953. // This gets called for every event - a performance analysis/review would be good
  954. Boolean CPluginView::PluginWindowEvent(const EventRecord& event)
  955. {
  956.     WindowPtr hitWindow;
  957.     
  958.     switch(event.what)
  959.     {
  960.         case mouseDown:
  961.         case mouseUp:
  962.             ::FindWindow(event.where, &hitWindow);
  963.             break;
  964.             
  965.         case autoKey:
  966.         case keyDown:
  967.         case keyUp:
  968.             hitWindow = ::FrontWindow();
  969.             break;
  970.  
  971.         case activateEvt:
  972.         case updateEvt:
  973.             hitWindow = (WindowPtr) event.message;
  974.             break;
  975.  
  976.         case nullEvent:
  977.         case diskEvt:
  978.         case osEvt:
  979.         case kHighLevelEvent:         
  980.         default:
  981.             return false;
  982.     }
  983.     
  984.     if(sPluginList != NULL)
  985.         {
  986.             // iterate through each plugin instance
  987.             LArrayIterator pluginIterator(*sPluginList);
  988.             CPluginView* plugin = NULL;
  989.             while (pluginIterator.Next(&plugin))
  990.                 {
  991.                     if(plugin->fWindowList != NULL)
  992.                         {
  993.                         // then iterate through each window for each instance
  994.                         LArrayIterator windowIterator(*(plugin->fWindowList));
  995.                         WindowPtr window = NULL;
  996.                         while(windowIterator.Next(&window))
  997.                             {
  998.                             if(window == hitWindow)
  999.                                 {
  1000.                                 EventRecord eventCopy = event;
  1001.                                 return plugin->PassWindowEvent(eventCopy, window);
  1002.                                 }
  1003.                             }
  1004.                         }
  1005.                 }
  1006.         }
  1007.  
  1008.     // we didn't find a match
  1009.     return false;
  1010. }
  1011.  
  1012. void CPluginView::RegisterWindow(void* window)
  1013. {
  1014.     if(fWindowList == NULL)
  1015.         fWindowList = new LArray;
  1016.     if(fWindowList != NULL)
  1017.         fWindowList->InsertItemsAt(1, 0, &window);
  1018. }
  1019.  
  1020. void CPluginView::UnregisterWindow(void* window)
  1021. {
  1022.     if (fWindowList != NULL)
  1023.     {
  1024.         Int32 index = fWindowList->FetchIndexOf(&window);
  1025.         if (index > 0)
  1026.             fWindowList->RemoveItemsAt(1, index);
  1027.                 
  1028.         if (fWindowList->GetCount() == 0)
  1029.         {
  1030.             delete fWindowList;
  1031.             fWindowList = NULL;
  1032.         }
  1033.     }
  1034. }
  1035.  
  1036. Boolean CPluginView::PassWindowEvent(EventRecord& inEvent, WindowPtr window)
  1037. {
  1038.     Boolean eventHandled = false;
  1039.     if (fApp) {
  1040.         // Treat update events specially, need to use BeginUpdate to clear the event.
  1041.         if (inEvent.what == updateEvt) {
  1042.             GrafPtr oldPort;
  1043.             ::GetPort(&oldPort);
  1044.             ::SetPort(window);
  1045.             ::BeginUpdate(window);
  1046.             eventHandled = NPL_HandleEvent(fApp, (NPEvent*)&inEvent, (void*) window);
  1047.             ::EndUpdate(window);
  1048.             ::SetPort(oldPort);
  1049.         } else
  1050.             eventHandled = NPL_HandleEvent(fApp, (NPEvent*)&inEvent, (void*) window);
  1051.     }
  1052.     return eventHandled;
  1053. }
  1054.  
  1055. CPluginView::CPluginView(LStream *inStream) : LView(inStream), LDragAndDrop(nil, this)
  1056. {
  1057.     fApp = NULL;
  1058.     fOriginalView = NULL;
  1059.     fBrokenPlugin = false;
  1060.     fPositioned = false;
  1061.     fHidden = false;
  1062.     fWindowed = true;
  1063.     fBrokenIcon = NULL;
  1064.     fIsPrinting = false;
  1065.     fWindowList = NULL;
  1066.         
  1067.     //
  1068.     // Add the new plug-in to a global list of all plug-ins.
  1069.     //
  1070.     if (sPluginList == NULL)
  1071.         sPluginList = new LArray;
  1072.     if (sPluginList != NULL)
  1073.         sPluginList->InsertItemsAt(1, 0, &this);
  1074.  
  1075.  
  1076.     //
  1077.     // Can╒t call ResetDrawRect yet because it needs a port
  1078.     // from our superview, but we don╒t have a superview yet!
  1079.     //
  1080. }
  1081.  
  1082.  
  1083. CPluginView::~CPluginView()
  1084. {
  1085.     if (fBrokenIcon != NULL)
  1086.         CIconList::ReturnIcon(fBrokenIcon);
  1087.         
  1088.     //
  1089.     // This static is parallel to LCommander::sTarget, but only for plug-ins.
  1090.     // The LCommander destructor takes care of resetting the LCommander::sTarget,
  1091.     // and so the CPluginView destructor should take care of the special plug-in target. 
  1092.     //
  1093.     if (CPluginView::sPluginTarget == this)
  1094.         CPluginView::sPluginTarget = NULL;
  1095.  
  1096.     // we're assuming the plugin has already killed the windows themselves
  1097.     if(fWindowList != NULL)
  1098.         delete fWindowList;
  1099.     //
  1100.     // Remove the deleted plug-in from the global list of all plug-ins.
  1101.     //
  1102.     if (sPluginList != NULL)
  1103.     {
  1104.         Int32 index = sPluginList->FetchIndexOf(&this);
  1105.         if (index > 0)
  1106.             sPluginList->RemoveItemsAt(1, index);
  1107.                 
  1108.         if (sPluginList->GetCount() == 0)
  1109.         {
  1110.             delete sPluginList;
  1111.             sPluginList = NULL;
  1112.         }
  1113.     }
  1114. };
  1115.  
  1116.  
  1117. void CPluginView::EmbedSize(LO_EmbedStruct* embed_struct, SDimension16 hyperSize)
  1118. {
  1119.     //
  1120.     // If the plug-in is hidden, set the width and height to zero and
  1121.     // set a flag indicating that we are hidden.
  1122.     //
  1123.     if (embed_struct->ele_attrmask & LO_ELE_HIDDEN)
  1124.     {
  1125.         embed_struct->width = 0;
  1126.         embed_struct->height = 0;
  1127.         fHidden = true;
  1128.         Hide();
  1129.         StartIdling();        // Visible plug-ins start idling in EmbedDisplay
  1130.     }
  1131.     
  1132.     //
  1133.     // If the embed src is internal-external-plugin, the plugin is
  1134.     // full-screen, and we should set up the plugin╒s real width
  1135.     // for layout since it doesn╒t know how big to make it.  Since a full-
  1136.     // screen plugin should resize when its enclosing view (the hyperview)
  1137.     // resizes, we bind the plugin view on all sides to its superview.
  1138.     //
  1139.     Boolean fullPage = false;
  1140.     if (embed_struct->embed_src)
  1141.     {
  1142.         char* theURL;
  1143.         PA_LOCK(theURL, char*, embed_struct->embed_src);
  1144.         XP_ASSERT(theURL);
  1145.         if (XP_STRCMP(theURL, "internal-external-plugin") == 0)
  1146.             fullPage = true;
  1147.         PA_UNLOCK(embed_struct->embed_src);
  1148.     }
  1149.     
  1150.     if (fullPage)
  1151.     {
  1152.         SBooleanRect binding = {true, true, true, true};
  1153.         SetFrameBinding(binding);
  1154.         
  1155.         embed_struct->width = hyperSize.width;
  1156.         embed_struct->height = hyperSize.height;
  1157.         
  1158.         //
  1159.         // Remember an offset for the view to
  1160.         // compensate for layout's default margins.
  1161.         //
  1162.         const short kLeftMargin = 8;                // ÑÑÑ These should be defined in mhyper.h!!!
  1163.         const short kTopMargin = 8;
  1164.         fHorizontalOffset = -kLeftMargin;
  1165.         fVerticalOffset = -kTopMargin;
  1166.     }
  1167.     else
  1168.     {
  1169.         fHorizontalOffset = 0;
  1170.         fVerticalOffset = 0;
  1171.     }
  1172.  
  1173.     ResizeImageTo(embed_struct->width, embed_struct->height, false);
  1174.     ResizeFrameTo(embed_struct->width, embed_struct->height, false);
  1175.  
  1176.     //
  1177.     // NOTE: The position set here is not really valid because the x and y in 
  1178.     // embed_struct are not valid yet (we have to wait until DisplayEmbed
  1179.     // (see below) is called the first time to get the real location).
  1180.     // We go ahead and position ourselves anyway just so we have a superview
  1181.     // and location initially.
  1182.     //
  1183.     Int32 x = embed_struct->x + embed_struct->x_offset + fHorizontalOffset;
  1184.     Int32 y = embed_struct->y + embed_struct->y_offset + fVerticalOffset;
  1185.     PlaceInSuperImageAt(x, y, false);
  1186.     
  1187.     //
  1188.     // Set up the NPWindow and NPPort for the first time.  No one should
  1189.     // have called NPL_EmbedSize before this point, or bogus values will
  1190.     // get passed to the plugin!
  1191.     //    
  1192.     ResetDrawRect();
  1193. }
  1194.  
  1195.  
  1196. void CPluginView::EmbedDisplay(LO_EmbedStruct* embed_struct, Boolean isPrinting)
  1197. {
  1198.     fWindowed = (Boolean)NPL_IsEmbedWindowed(fApp);
  1199.     fIsPrinting = isPrinting;
  1200.  
  1201.     //
  1202.     // If we╒re not positioned yet, place our instance at the
  1203.     // location specified by layout and make our view visible.
  1204.     //
  1205.     if (fPositioned == false)
  1206.     {
  1207.         Int32 x = embed_struct->x + embed_struct->x_offset + fHorizontalOffset;
  1208.         Int32 y = embed_struct->y + embed_struct->y_offset + fVerticalOffset;
  1209.         PlaceInSuperImageAt(x, y, false);
  1210.         if (fWindowed)
  1211.             Show();
  1212.         else if (!fBrokenPlugin) {
  1213.             fHidden = true;
  1214.             Hide();
  1215.         }
  1216.         StartIdling();
  1217.         fPositioned = true;
  1218.  
  1219.         XP_ASSERT(fApp);
  1220.         if (fApp != NULL)
  1221.             NPL_EmbedSize(fApp);    // Tell the plugin about its dimensions and location
  1222.         
  1223.         Refresh();
  1224.     }
  1225.     
  1226.     // If this is a windowed plug-in, this call indicates that we've moved or our visibility
  1227.     // changed.
  1228.     if (fWindowed || fBrokenPlugin)
  1229.     {
  1230.         Rect frameRect;
  1231.         SPoint32 imagePoint;
  1232.         Int32 x, y;
  1233.         
  1234.         if (IsVisible() && (embed_struct->ele_attrmask & LO_ELE_INVISIBLE))
  1235.             Hide();
  1236.             
  1237.         CalcPortFrameRect(frameRect);
  1238.         GetSuperView()->PortToLocalPoint(topLeft(frameRect));
  1239.         GetSuperView()->LocalToImagePoint(topLeft(frameRect), imagePoint);
  1240.         
  1241.         x = embed_struct->x + embed_struct->x_offset + fHorizontalOffset;
  1242.         y = embed_struct->y + embed_struct->y_offset + fVerticalOffset;
  1243.         if ((imagePoint.h != x) || (imagePoint.v != y))
  1244.             PlaceInSuperImageAt(x, y, true);
  1245.         
  1246.         if (!IsVisible() && !(embed_struct->ele_attrmask & LO_ELE_INVISIBLE))
  1247.             Show();
  1248.     }
  1249.     // For a windowless plug-in, this is where the plug-in actually draws.
  1250.     else 
  1251.     {
  1252.         EventRecord updateEvent;
  1253.         //
  1254.         // Always reset the drawing info before calling the plugin to draw.
  1255.         //
  1256.         ResetDrawRect();
  1257.  
  1258.         memset(&updateEvent, 0, sizeof(EventRecord));
  1259.         updateEvent.what = updateEvt;
  1260.         (void) PassEvent(updateEvent);
  1261.     }
  1262. }
  1263.  
  1264.  
  1265. void CPluginView::EmbedCreate(MWContext* context, LO_EmbedStruct* embed_struct)
  1266. {
  1267.     fEmbedStruct = embed_struct;
  1268.     fApp = (NPEmbeddedApp*) embed_struct->FE_Data;
  1269.     Boolean printing = (context->type == MWContextPrint);
  1270.     
  1271.     //
  1272.     // Set the references between the view, the app, and the layout
  1273.     // structure.  Also set the pointer to the new instance╒s
  1274.     // window data (NPWindow), which is stored as part of our object
  1275.     // (unless we are printing, in which case it already has a NPWindow).
  1276.     // Then start up the stream for the plug-in (if applicable).
  1277.     // If NPL_EmbedStart fails, it will delete the NPEmbeddedApp and
  1278.     // associated XP data structures, so be sure to remove the
  1279.     // embed_struct's reference to it.
  1280.     //
  1281.     if (fApp != NULL)
  1282.     {    
  1283.         if (printing)
  1284.             fOriginalView = (CPluginView*) fApp->fe_data;
  1285.         fApp->fe_data = this;
  1286.         embed_struct->FE_Data = fApp;
  1287.         if (!printing)
  1288.             fApp->wdata = GetNPWindow();
  1289.         NPError err = NPL_EmbedStart(context, embed_struct, fApp);
  1290.         if (err != NPERR_NO_ERROR)
  1291.             fApp = NULL;
  1292.     }
  1293.     
  1294.     //
  1295.     // Something went wrong? If we have no app, then layout can never
  1296.     // tell us to delete the view (since the app is in the FE_Data of
  1297.     // the layout structure), so we need to delete it here.
  1298.     //
  1299.     if (fApp == NULL)
  1300.     {
  1301.         delete this;
  1302.         embed_struct->FE_Data = NULL;
  1303.     }
  1304. }
  1305.  
  1306.  
  1307. void CPluginView::EmbedFree(MWContext* context, LO_EmbedStruct* embed_struct)
  1308. {
  1309.     //
  1310.     // Set our reference to the NPEmbeddedApp to NULL now, before
  1311.     // calling NPL_EmbedDelete.  NPL_EmbedDelete may re-call FE code
  1312.     // after deleting the NPEmbeddedApp and we don╒t want to have
  1313.     // a reference to a deleted app!
  1314.     //
  1315.     // XXX This is no longer necessary, as this is called in _response_
  1316.     // to NPL_EmbedDelete.
  1317.     NPEmbeddedApp* app = fApp;
  1318.     fApp = NULL;
  1319.     
  1320.     //
  1321.     // If our original view field was set above in EmbedCreate, the
  1322.     // the NPEmbeddedApp associated with this view was associated
  1323.     // only temporarily for printing, so we should switch its 
  1324.     // references to its view and window back to their original values.
  1325.     // If there is no original view, then we must be the original
  1326.     // creator of the NPEmbeddedApp, so we should delete it.
  1327.     //
  1328.     if (fOriginalView != NULL)
  1329.     {
  1330.         app->fe_data = fOriginalView;
  1331.         XP_ASSERT(app->wdata == fOriginalView->GetNPWindow());
  1332.         app->wdata = fOriginalView->GetNPWindow();
  1333.     }
  1334.  
  1335.     ThrowIfNil_(context);
  1336. }
  1337.  
  1338.  
  1339.  
  1340. // ÑÑ event processing
  1341.  
  1342. void CPluginView::ClickSelf(const SMouseDownEvent& inMouseDown)
  1343. {
  1344.     XP_ASSERT(fWindowed);
  1345.     LView::ClickSelf(inMouseDown);
  1346.     
  1347.     if (!fBrokenPlugin)
  1348.     {
  1349.         EventRecord mouseEvent = inMouseDown.macEvent;        // inMouseDown is const, but PassEvent isn╒t
  1350.         (void) PassEvent(mouseEvent);
  1351.         LCommander::SwitchTarget(this);                        // Once clicked, we can get keystrokes
  1352.     }
  1353. }
  1354.  
  1355. void CPluginView::EventMouseUp(const EventRecord& inMouseUp)
  1356. {
  1357.     XP_ASSERT(fWindowed);
  1358.     LView::EventMouseUp(inMouseUp);
  1359.     
  1360.     EventRecord mouseEvent = inMouseUp;                        // inMouseUp is const, but PassEvent isn╒t
  1361.     (void) PassEvent(mouseEvent);
  1362. }
  1363.  
  1364.  
  1365. Boolean    CPluginView::HandleKeyPress(const EventRecord& inKeyEvent)
  1366. {
  1367.     if (fWindowed) {
  1368.         EventRecord keyEvent = inKeyEvent;                        // inKeyEvent is const, but PassEvent isn╒t
  1369.         return PassEvent(keyEvent);
  1370.     }
  1371.     
  1372.     return false;
  1373. }
  1374.  
  1375.  
  1376. void CPluginView::DrawSelf()
  1377. {
  1378.     XP_ASSERT(!fHidden);
  1379.  
  1380.     if (fPositioned == false)        // Bail if layout hasn╒t positioned us yet
  1381.         return;
  1382.         
  1383.     if (fBrokenPlugin)
  1384.         DrawBroken(false);
  1385.     else
  1386.     {
  1387.         //
  1388.         // Always reset the drawing info before calling the plugin to draw.
  1389.         // Although the MoveBy and ResizeFrameBy methods ensure that the
  1390.         // drawing info is updated if the plugin is scrolled or its window
  1391.         // is resized, the clip still needs to be set up every time before
  1392.         // drawing because the area to draw will be different.
  1393.         //
  1394.         ResetDrawRect();
  1395.  
  1396.  
  1397.         if (fIsPrinting)
  1398.             {
  1399.             this->PrintEmbedded();
  1400.             fIsPrinting = false;
  1401.             }
  1402.         else
  1403.             {
  1404.             EventRecord updateEvent;
  1405.             memset(&updateEvent, 0, sizeof(EventRecord));
  1406.             updateEvent.what = updateEvt;
  1407.             (void) PassEvent(updateEvent);
  1408.             }
  1409.     }
  1410. }
  1411.  
  1412.  
  1413.  
  1414. void CPluginView::SpendTime(const EventRecord& inMacEvent)
  1415. {
  1416.     EventRecord macEvent = inMacEvent;        // inMacEvent is const, but PassEvent isn╒t
  1417.     (void) PassEvent(macEvent);
  1418.     
  1419.     //
  1420.     // If we╒re in SpendTime because of a non-null event, send a
  1421.     // null event too.  Some plug-ins (e.g. Shockwave) rely on null
  1422.     // events for animation, so it doesn╒t matter if we╒re giving
  1423.     // them lots of time with mouseMoved or other events -- if they
  1424.     // don╒t get null events, they don╒t animate fast.
  1425.     //
  1426.     if (macEvent.what != nullEvent)
  1427.     {
  1428.         macEvent.what = nullEvent;
  1429.         (void) PassEvent(macEvent);
  1430.     }
  1431. }
  1432.  
  1433.  
  1434. void CPluginView::ActivateSelf()
  1435. {
  1436.     EventRecord activateEvent;
  1437.     memset(&activateEvent, 0, sizeof(EventRecord));
  1438.     activateEvent.what = activateEvt;
  1439.     activateEvent.modifiers = activeFlag;
  1440.     (void) PassEvent(activateEvent);
  1441. }
  1442.  
  1443.  
  1444. void CPluginView::DeactivateSelf()
  1445. {
  1446.     EventRecord activateEvent;
  1447.     memset(&activateEvent, 0, sizeof(EventRecord));
  1448.     activateEvent.what = activateEvt;
  1449.     (void) PassEvent(activateEvent);
  1450. }
  1451.  
  1452. //
  1453. // These two methods are called when we are targeted or
  1454. // untargeted.  Since plugins want to know when they have
  1455. // the key focus, we create a new event type to pass to
  1456. // them.
  1457. //
  1458.  
  1459. void CPluginView::BeTarget()
  1460. {
  1461.     XP_ASSERT(!fHidden);
  1462.     CPluginView::sPluginTarget = this;                    // Parallel to LCommander::sTarget, except only for plug-ins
  1463.     EventRecord focusEvent;
  1464.     memset(&focusEvent, 0, sizeof(EventRecord));
  1465.     focusEvent.what = getFocusEvent;
  1466.     Boolean handled = PassEvent(focusEvent);
  1467.     if (!handled)                                        // If the plugin doesn╒t want the focus,
  1468.         SwitchTarget(GetSuperCommander());                //        switch the focus back to our super
  1469. }
  1470.  
  1471. void CPluginView::DontBeTarget()
  1472. {
  1473.     CPluginView::sPluginTarget = NULL;                    // Parallel to LCommander::sTarget, except only for plug-ins
  1474.     EventRecord focusEvent;
  1475.     memset(&focusEvent, 0, sizeof(EventRecord));
  1476.     focusEvent.what = loseFocusEvent;
  1477.     (void) PassEvent(focusEvent);
  1478. }
  1479.  
  1480.  
  1481. void CPluginView::AdjustCursorSelf(Point inPortPt, const EventRecord& inMacEvent)
  1482. {
  1483.     XP_ASSERT(!fHidden);
  1484.     EventRecord cursorEvent = inMacEvent;
  1485.     cursorEvent.what = adjustCursorEvent;
  1486.     
  1487.     Boolean handled = PassEvent(cursorEvent);
  1488.     if (!handled)
  1489.         LPane::AdjustCursorSelf(inPortPt, inMacEvent);
  1490. }
  1491.  
  1492.  
  1493.  
  1494. // ÑÑ positioning
  1495.  
  1496. void  CPluginView::AdaptToSuperFrameSize(Int32 inSurrWidthDelta, Int32 inSurrHeightDelta, Boolean inRefresh)
  1497. {
  1498.     LView::AdaptToSuperFrameSize(inSurrWidthDelta, inSurrHeightDelta, inRefresh);
  1499.  
  1500.     //
  1501.     // Our superview has changed size, so we could have changed size
  1502.     // (if we╒re bound to the superview) or changed clip (if we now
  1503.     // overlap the edges of the superview).
  1504.     //
  1505.     if (fWindowed)
  1506.         ResetDrawRect();
  1507.     
  1508.     //
  1509.     // If we are bound to our superview, we will change size as it
  1510.     // changes size.  Since the superview (the hyperview) is in turn
  1511.     // bound to the window, we should adjust our NPWindow structure
  1512.     // (done above in ResetDrawRect) and tell the plugin about the
  1513.     // change (by calling NPL_EmbedSize).  -bing 11/15/95
  1514.     //
  1515.     if (fApp != NULL)
  1516.     {
  1517.         SBooleanRect frameBinding;
  1518.         GetFrameBinding(frameBinding);
  1519.         if (frameBinding.top && frameBinding.bottom && frameBinding.left && frameBinding.right)
  1520.             NPL_EmbedSize(fApp);
  1521.     }
  1522. }
  1523.  
  1524. void CPluginView::MoveBy(Int32 inHorizDelta, Int32 inVertDelta, Boolean inRefresh)
  1525. {
  1526.     LView::MoveBy(inHorizDelta, inVertDelta, inRefresh);
  1527.     if (fWindowed)
  1528.         ResetDrawRect();
  1529. }
  1530.  
  1531.  
  1532.  
  1533. // ÑÑ╩dragging
  1534.  
  1535. void CPluginView::AdaptToNewSurroundings()
  1536. {
  1537.     LView::AdaptToNewSurroundings();
  1538.  
  1539.     //
  1540.     // Set the port for dragging here, since we need 
  1541.     // a superview to get the port, and we don't have
  1542.     // a superview until this method is called.
  1543.     //
  1544.     LDropArea::RemoveDropArea((LDropArea*)this, mDragWindow);
  1545.     mDragWindow = GetMacPort();
  1546.     LDropArea::AddDropArea((LDropArea*)this, mDragWindow);
  1547. }
  1548.  
  1549. Boolean    CPluginView::DragIsAcceptable(DragReference /*inDragRef*/)
  1550. {
  1551.     //
  1552.     // We claim to accept drops, so we won't get the
  1553.     // Navigator drag&drop behavior over the plug-in.
  1554.     // If the plug-in installs its own drag handlers,
  1555.     // it will get control after we're called and can
  1556.     // handle the drag as it sees fit.
  1557.     //
  1558.     return true;
  1559. }
  1560.  
  1561. void CPluginView::HiliteDropArea(DragReference /*inDragRef*/)
  1562. {
  1563.     // Don╒t hilite: let the plug-in do it if it accepts drags.
  1564. }
  1565.  
  1566. void CPluginView::UnhiliteDropArea(DragReference /*inDragRef*/)
  1567. {
  1568.     // Don╒t unhilite: let the plug-in do it if it accepts drags.
  1569. }
  1570.  
  1571.  
  1572.  
  1573.  
  1574. // ÑÑ╩events
  1575.  
  1576. // Passes the event to our plugin
  1577. Boolean CPluginView::PassEvent(EventRecord& inEvent)
  1578. {
  1579.     if (fApp)
  1580.         return NPL_HandleEvent(fApp, (NPEvent*)&inEvent, NULL);
  1581.     else
  1582.         return false;
  1583. }
  1584.  
  1585.  
  1586. void CPluginView::ResetDrawRect()
  1587. {
  1588.     fNPWindow.window = &fNPPort;
  1589.  
  1590.     if (fWindowed) {
  1591.  
  1592.         fNPPort.port = (CGrafPtr) GetMacPort();
  1593.         fNPWindow.x = mImageLocation.h;
  1594.         fNPWindow.y = mImageLocation.v;
  1595.         fNPWindow.width = mFrameSize.width;
  1596.         fNPWindow.height = mFrameSize.height;
  1597.     
  1598.         if (mRevealedRect.left < mRevealedRect.right)    // Code from FocusDraw
  1599.         {        // We are visible
  1600.             XP_ASSERT(!fHidden);
  1601.             fNPPort.portx = mPortOrigin.h;
  1602.             fNPPort.porty = mPortOrigin.v;
  1603.  
  1604.             fNPWindow.clipRect.top = mRevealedRect.top;
  1605.             fNPWindow.clipRect.left = mRevealedRect.left;
  1606.             fNPWindow.clipRect.right = mRevealedRect.right;
  1607.             fNPWindow.clipRect.bottom = mRevealedRect.bottom;
  1608.         }
  1609.         else    // We are invisible
  1610.         {
  1611.             fNPWindow.clipRect.top = 0;
  1612.             fNPWindow.clipRect.left = 0;
  1613.             fNPWindow.clipRect.right = 0;
  1614.             fNPWindow.clipRect.bottom = 0;
  1615.         }
  1616.     }
  1617.     else {
  1618.         Rect frame;
  1619.         Point localPoint, portPoint;
  1620.         SPoint32 imagePoint;
  1621.         Rect clipRect;
  1622.         Point portOrigin;
  1623.         
  1624.         // XXX This is gross. We're getting our parent and casting it down to a
  1625.         // CHTMLView so that we can get element position and port information from it.
  1626.         CHTMLView *parentView = (CHTMLView *)GetSuperView();
  1627.  
  1628.         // Get the parent's port and port origin. Note that this will be different
  1629.         // depending on whether it's onscreen or offscreen.
  1630.         fNPPort.port = (CGrafPtr) parentView->GetCurrentPort(portOrigin);
  1631.         
  1632.         // Get the local position from the hyperview. This is a function of the
  1633.         // location of the layout element, the scrolled position and the position
  1634.         // of the layer containing the embed.
  1635.         parentView->CalcElementPosition((LO_Element *)fEmbedStruct, frame);
  1636.         
  1637.         // Convert it into image coordinates
  1638.         localPoint.h = frame.left - (fEmbedStruct->x + fEmbedStruct->x_offset);
  1639.         localPoint.v = frame.top - (fEmbedStruct->y + fEmbedStruct->y_offset);
  1640.         portPoint = localPoint;
  1641.         localPoint.h -= portOrigin.h;
  1642.         localPoint.v -= portOrigin.v;
  1643.         parentView->LocalToImagePoint(localPoint, imagePoint);
  1644.         
  1645.         fNPWindow.x = imagePoint.h;
  1646.         fNPWindow.y = imagePoint.v;
  1647.         fNPWindow.width = frame.right - frame.left;
  1648.         fNPWindow.height = frame.bottom - frame.top;
  1649.         
  1650.         fNPPort.portx = -localPoint.h;
  1651.         fNPPort.porty = -localPoint.v;
  1652.         
  1653.         // The clipRect in this case is the bounding box of the clip region
  1654.         // set by the compositor. Convert to port coordinates.
  1655.         clipRect = (*fNPPort.port->clipRgn)->rgnBBox;
  1656.         topLeft(clipRect).h -= portOrigin.h;
  1657.         topLeft(clipRect).v -= portOrigin.v;
  1658.         botRight(clipRect).h -= portOrigin.h;
  1659.         botRight(clipRect).v -= portOrigin.v;
  1660.         
  1661.         fNPWindow.clipRect.top = clipRect.top;
  1662.         fNPWindow.clipRect.left = clipRect.left;
  1663.         fNPWindow.clipRect.right = clipRect.right;
  1664.         fNPWindow.clipRect.bottom = clipRect.bottom;
  1665.         
  1666.         // Keep the view's position in sync with that specified by layout
  1667.         parentView->LocalToPortPoint(portPoint);
  1668.         if ((portPoint.h != -mPortOrigin.h) || (portPoint.v != -mPortOrigin.v)) {
  1669.             MoveBy(mPortOrigin.h + portPoint.h, mPortOrigin.v + portPoint.v, false);
  1670.         }
  1671.     }
  1672. }
  1673.  
  1674.  
  1675. //
  1676. // This method is used for printing fullscreen plugins, which might
  1677. // want the opportunity to completely take over printing the page.
  1678. // We will pass them whether the user clicked on the print button
  1679. // or the Print menu (╥printOne╙), a handle to the print record,
  1680. // and a boolean that they can set to true if they actually want to
  1681. // handle the print.  We return this boolean to our caller (CHyperView:
  1682. // ObeyCommand) so they know whether they should print or not.
  1683. // If plugins do not implement the Print entry point at all, the 
  1684. // boolean ╥pluginPrinted╙ will still be at its default value (false),
  1685. // so we will still print.
  1686. //
  1687. Boolean CPluginView::PrintFullScreen(Boolean printOne, THPrint printRecHandle)
  1688. {
  1689.     if (fApp == NULL)
  1690.         return false;
  1691.  
  1692.     NPPrint npPrint;
  1693.     npPrint.mode = NP_FULL;
  1694.     npPrint.print.fullPrint.pluginPrinted = false;
  1695.     npPrint.print.fullPrint.printOne = printOne;
  1696.     npPrint.print.fullPrint.platformPrint = (void*) printRecHandle;
  1697.     
  1698.     NPL_Print(fApp, (void*) &npPrint);
  1699.         
  1700.     return npPrint.print.fullPrint.pluginPrinted;
  1701. }
  1702.  
  1703. void CPluginView::PrintEmbedded()
  1704. {
  1705.     if (fApp == NULL)
  1706.         return;
  1707.  
  1708.     NPPrint npPrint;
  1709.     npPrint.mode = NP_EMBED;
  1710.     npPrint.print.embedPrint.window = fNPWindow;
  1711.     npPrint.print.embedPrint.platformPrint = (void*) fNPWindow.window;     // Pass the NP_Port
  1712.     
  1713.     NPL_Print(fApp, (void*) &npPrint);
  1714. }
  1715.  
  1716.  
  1717.  
  1718.  
  1719. void CPluginView::SetBrokenPlugin()
  1720. {
  1721.     fBrokenPlugin = true;
  1722.     fBrokenIcon = CIconList::GetIcon(326);        // ÑÑÑ Need a constant
  1723.     // Tell layout that we're windowed so we can display an icon
  1724.     LO_SetEmbedType(fEmbedStruct, PR_TRUE);
  1725.     Refresh();                                    // Plugin will draw broken icon now that fBrokenPlugin is set
  1726. }
  1727.  
  1728.  
  1729. void CPluginView::DrawBroken(Boolean hilite)
  1730. {
  1731.     //
  1732.     // If the plugin is broken, we don╒t have a valid plugin instance
  1733.     // to ask to draw, so we should handle drawing ourselves.
  1734.     // Current behavior is to draw a box with a broken plugin icon
  1735.     // in it.
  1736.     //
  1737.     Rect drawRect;
  1738.     if (this->CalcLocalFrameRect(drawRect))        // We want a local rect since SetOrigin has been called
  1739.     {        
  1740.         short height = drawRect.bottom - drawRect.top;
  1741.         short width = drawRect.right - drawRect.left;
  1742.         
  1743.         if (height >= 4 && width >= 4)
  1744.         {
  1745.             UGraphics::SetFore(CPrefs::Anchor);
  1746.             FrameRect(&drawRect);
  1747.             
  1748.             if (height >= 32 && width >= 32 && fBrokenIcon != NULL)
  1749.             {
  1750.                 short centerX = (drawRect.right - drawRect.left) >> 1;
  1751.                 short centerY = (drawRect.bottom - drawRect.top) >> 1;
  1752.                 drawRect.top = centerY - 16;
  1753.                 drawRect.bottom = centerY + 16;
  1754.                 drawRect.left = centerX - 16;
  1755.                 drawRect.right = centerX + 16;
  1756.                 
  1757.                 ::PlotCIconHandle(&drawRect, atAbsoluteCenter, hilite ? ttSelected : ttNone, fBrokenIcon);
  1758.             }
  1759.         }
  1760.     }
  1761. }
  1762.  
  1763.  
  1764. #ifdef LAYERS
  1765. Boolean CPluginView::HandleEmbedEvent(CL_Event *event)
  1766. {
  1767.     fe_EventStruct *fe_event = (fe_EventStruct *)event->fe_event;
  1768.     EventRecord macEvent;
  1769.  
  1770.     // For windowless plugins, the last draw might have been to the offscreen port,
  1771.     // but for event handling, we want the current port to be the onscreen port (so
  1772.     // that mouse coordinate conversion and the like can happen correctly).
  1773.     if (!fWindowed)
  1774.         FocusDraw();
  1775.         
  1776.     switch (event->type) {
  1777.         case CL_EVENT_MOUSE_BUTTON_DOWN:
  1778.             {
  1779.                 //SMouseDownEvent *mouseDown = (SMouseDownEvent *)fe_event->event;
  1780.                 
  1781.                 // Now pass the mouse event
  1782.                 //macEvent = mouseDown->macEvent;
  1783.                 // modified for new fe_EventStruct 1997-02-22 mjc
  1784.                 SMouseDownEvent mouseDown = fe_event->event.mouseDownEvent;
  1785.                 macEvent = mouseDown.macEvent;
  1786.             }
  1787.             break;
  1788.         case CL_EVENT_MOUSE_BUTTON_UP:
  1789.         case CL_EVENT_KEY_DOWN:
  1790.             //macEvent = *(EventRecord *)fe_event->event;
  1791.             macEvent = fe_event->event.macEvent; // 1997-02-22 mjc
  1792.             break;
  1793.         case CL_EVENT_MOUSE_MOVE:
  1794.             //macEvent = *(EventRecord *)fe_event->event;
  1795.             macEvent = fe_event->event.macEvent; // 1997-02-22 mjc
  1796.             macEvent.what = adjustCursorEvent;
  1797.             break;
  1798.         case CL_EVENT_KEY_FOCUS_GAINED:
  1799.             macEvent.what = getFocusEvent;
  1800.             break;
  1801.         case CL_EVENT_KEY_FOCUS_LOST:
  1802.             macEvent.what = loseFocusEvent;
  1803.             break;
  1804.         default:
  1805.             return false;
  1806.     }
  1807.     
  1808.     return PassEvent(macEvent);
  1809. }
  1810. #endif // LAYERS
  1811.