Changes between Initial Version and Version 1 of NetscapePluginsOnMacNotes


Ignore:
Timestamp:
Oct 16, 2009 3:53:23 AM (13 years ago)
Author:
vestbo@webkit.org
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • NetscapePluginsOnMacNotes

    v1 v1  
     1[[PageOutline]]
     2
     3= Notes on Netscape plugins on Mac =
     4
     5Safari uses the NPAPI implementation in WebKit/mac/Plugins, not the stuff in WebCore/plugins.
     6
     7This page is a brain dump of my notes when doing the WebCore NAPI implementation for Mac. The theme is basically: figure out what the WebKit implementation does, try to document/make sense of it, then implement the same behavior.
     8
     9There are likely many mistakes and wrong assumptions in these notes, but they may be useful for someone else, hence the dump.
     10
     11== Stuff that Safari does ==
     12
     13When Safari encounters errors during loading of a plugin, such as NPP_New
     14failing, or a bad drawing or event model combination, it cleans up by
     15calling _destroyPlugin and _pluginPackage.close(). The former is equivalent
     16to us calling stop() (for calling NP_Destroy), and then destructing the
     17plugin view (which will clean up script opjects and unload the plugin package).
     18We never unload/close the plugin package, except for in the destructor, and
     19we don't do cleanupScriptObjectsForPlugin().
     20
     21Safari wraps each call to NPP-functions in a willCallPlugInFunction() and
     22didCallPlugInFunction(), which basically ensures that any stop() when there
     23are plugin-stack-frames will result in shouldStopSoon being set, and the
     24real stop() being called after the last unwind from a NPP-function.
     25
     26== Initialization and shutdown ==
     27
     28PluginView is a Widget subclass, which contains all the other plugin
     29members, such as the PluginPackage, and the NPP instance. The view is
     30created in the factory function PluginView::create(), which uses the
     31PluginDatabase to fetch the PluginPacakge, which is then passed on when
     32constructing the PluginView.
     33
     34PluginView has the following methods:
     35
     36 - PluginView() constructor: called from PluginView::create(), from FrameLoaderClient, from FrameLoader::loadPlugin()
     37    - Does not call any further init methods (init or start)
     38 - ~PluginView() destructor: called from RenderWidget::clearWidget(), from HTMLPluginElement::detach()
     39    - Calls stop()
     40 - init(): called from PluginView::setParent(), from ScrollView::addChild(), from RenderWidget::addWidget(), from FrameLoader::loadPlugin()
     41    - Calls load() on the PluginPackage
     42    - Calls start()
     43 - start(): called from init()
     44    - Calls NP_New
     45 - stop(): called from the PluginView destructor
     46    - Calls NP_Destroy
     47
     48PluginView has the following state members:
     49
     50 - m_haveInitialized (boolean)
     51    - Used to guard against consecutive calls to init()
     52    - So if setParent() is called due to a reparent, init() will not be called
     53    - This means anything done in init should be done only once
     54 - m_isStarted (boolean)
     55    - Set to true after a successfull call to NP_New
     56    - Guards against consecutive calls to start()
     57    - Guards against calling stop() when not started
     58    - Set to false in stop(), before actually calling NP_Destroy
     59    - The Safari implementation has the same concept (as _isStarted)
     60 - m_status (PluginStatus[CanNotFindPlugin|CanNotLoadPlugin|LoadedSuccessfully]
     61    - Does not seem to be used by the Safari implementation
     62
     63=== Steps in initialization ===
     64
     65 1. PluginView constructor sets up data members
     66 2. PluginView::setParent() is called, which calls init()
     67 3. Each platform has its own init(), which does the following
     68    1. Check that we haven't initialized before
     69    2. Check that we have a plugin, and in that case load the plugin package
     70    3. Call start() and check that it succeeded. If not, set status to PluginStatusCanNotLoadPlugin (m_isStarted is not set at this point)
     71    4. Do platform-spesific stuff
     72        - Win registeres the plugin window class, sets up offscreen painting hooks, some encoding stuff, etc
     73        - Qt (really X11), checks if the plugin is windowed (NPPVpluginNeedsXEmbed),  and fails if its windowless by setting PluginStatusCanNotLoadPlugin
     74        - Mac determines the drawing and event models, and fails if not compatiable by setting the status and calling stop()
     75            - It also starts the null timer, and calls show() for some reason
     76        - GTK also checks NPPVpluginNeedsXEmbed, and calls show()
     77    5. Finally, status is set to PluginStatusLoadedSuccessfully
     78 
     79
     80=== Steps in shutdown ===
     81
     82Stuff we are doing in PluginViewMac:
     83
     84 - Stopping and disconnecting all PluginStreams
     85 - Setting m_started to false
     86 - Unregister the plugin from the PluginMainThreadScheduler
     87 - Call NPP_Destroy on the plugin
     88 - Clear m_instance's pdata
     89 - Delete all plugin-requests
     90 - Free parameter names and values
     91 - Clean up script objects for the plugin
     92 - Unload the plugin package (unless there's a quirk)
     93 - Set the NPWindow to 0
     94
     95Stuff that Safari does in WebNetscapePluginView
     96
     97 - Free the plugin instance and set to 0
     98 - Cancel pending frame-loads (or somethign like that)
     99 - Set the last-NPWindow.type to 0 to reschedule a NPP_SetWindow
     100
     101Stuff that Safari does in WebBaseNetscapePluginView
     102
     103 - Stop timers
     104
     105Stuff Safari does as part of unloading the plugin package:
     106
     107 - Call NPP_Shutdown on the plugin (we do this too in PluginPackage)
     108
     109
     110== Window handling and drawing ==
     111
     112
     113The "window state" is stored in multiple locations:
     114
     115  - NPWindow window, which is a normal NPWindow
     116    This is the same as our m_npWindow
     117
     118  - PluginPort nPort, which for GC has one member, a NP_CGContext cgPort
     119    The cgPort is the same as our m_npCgContext
     120
     121  - PortState, which is a NP_CGContext on CG, but without the window
     122
     123PortState saveAndSetNewPortStateForUpdate(bool forUpdate)
     124{
     125    1. Compute bounds and visible rect in top level window coordinates (top-left-relative)
     126    2. Get the WindowRef of the current window
     127    3. Set NP_Window's x,y,width,height and type based on bounds
     128    4. Inspect the bounds and top-level-window state to determine clipping
     129        1. If completely obscured, set NPWindow's height and width to specified size
     130           and use the NPWindow.clipRect to set the size to 0  (bottom = top, etc)
     131        2. Otherwise set the NPWindow.clipRect based on the visible rect
     132    5. Construct and build new "PortState", which for CG is just a NP_CGContext,
     133       so the PortState is basically the NPWindow's window attribute
     134        1. Check that we can draw (probably means that we're in a paint event, INVESTIGATE)
     135        2. Get the CGContextRef for the current context and window
     136        3. If using the Cocoa event model, update the NP_Windows's window struct
     137            1. Setting the windowRef and context from above in the nPort.cgPort (our m_npCgContext)
     138        4. Save current graphics context's state using CGContextSaveGState()
     139        5. Clip to the dirty region if drawing to a window (do we need this?)
     140    6. Return the new PortState
     141
     142    Note: The forUpdate argument has no effect and is only used for QuickDraw
     143}
     144
     145PortState saveAndSetNewPortState()
     146{
     147    1. return saveAndSetNewPortStateForUpdate(false)
     148
     149    This method is identical to saveAndSetNewPortStateForUpdate() for CoreGraphics
     150}
     151
     152void restorePortState(PortState portState)
     153{
     154    1. Get the CGContextRef out of the portState
     155    2. Verify that the nPort.cgPort context is not set, _or_ if it's
     156       set, that it's the same as the portState's context
     157    3. Restore the current graphics context's state using CGContextRestoreGState()
     158}
     159
     160void updateAndSetWindow()
     161{
     162    1. Ensure that the plugin is started (and not stopped)
     163    2. Ensure that we can paint
     164    3. Do some NSView locking (not relevant for us?)
     165    4. Get the portState though saveAndSetNewPortState()
     166       This effectivly updates the NSWindow and npPort
     167    5. If we have a valid port state:
     168        1. Call NPP_SetWindow though setWindowIfNecessary()
     169        2. Restore the portState though restorePortState()
     170        3. Free the port state
     171    6. Else, call setWindowIfNecessary() directly
     172    7. Unlock stuff we locked earlier
     173}
     174
     175void setWindowIfNecessary()
     176{
     177    1. Ensure that the plugin is started
     178    2. Check if the NPWindow or other structs have changed, by a
     179       call to isNewWindowEqualToOldWindow()
     180    3. If so, call NPP_SetWindow and save structs to lastSet-structs
     181
     182    This is basically a wrapper for NPP_SetWindow
     183}
     184
     185bool isNewWindowEqualToOldWindow()
     186{
     187    1. Compare every member of the NPWindow
     188    2. Compare the npPort.cgPort members
     189}
     190
     191bool sendEvent(void* event, bool eventIsDrawRect)
     192{
     193    1. Ensure that the plugin is started
     194    2. Check that we're not in NPP_SetWindow (for SVG viewer install)
     195    3. Check that we have a page and a frame
     196    4. Verify that if the event is updateEvt we're inside a paint event
     197    5. If the event is an updateEvt (draw), prepare for drawing:
     198        1. Call saveAndSetNewPortStateForUpdate()
     199        2. Call setWindowIfNecessary()
     200    6. If we have a portState (we're drawing and the previous 5.1 call returned one)
     201        1. Call restorePortState()
     202
     203    This is basically a wrapper for NPP_HandleEvent
     204}
     205
     206void sendDrawRectEvent(NSRect rect)
     207{
     208    1. Get the current graphics context
     209    2. Call drawRect on the event handler with the rect and context
     210}
     211
     212void drawRect(NSRect rect)
     213{
     214    1. Ensure we're not using NPDrawingModelCoreAnimation
     215    2. Ensure the plugin is started
     216    3. If we are drawing to screen, call sendDrawRectEvent()
     217    4. Otherwise, do bitmap drawing
     218}
     219
     220void invalidateRect(NPRect rect)
     221{
     222    1. Call invalidatePluginContentRect() with rect
     223}
     224
     225void invalidateRegion(NPRegion region)
     226{
     227    1. Call invalidatePluginContentRect() with region (converted to rect)
     228}
     229
     230void forceRedraw()
     231{
     232    1. Call invalidatePluginContentRect() with plugin bounds
     233    2. Call displayIfNeeded() on the plugin's NSView
     234}
     235
     236void invalidatePluginContentRect(NSRect rect)
     237{
     238    1. Get the RenderBoxModelObject (renderer) for the plugin element
     239    2. Compute the contentRect for the rect
     240    3. Move contentRect based on render-object top-left + padding
     241    4. Call repaintRectangle(contentRect) on the renderer
     242}
     243
     244
     245== The NPWindow struct ==
     246
     247
     248Has:
     249
     250  - Geometry (x, y, width, height)
     251  - Clip (a NPRect)
     252  - A platform spesific window handle (window)
     253    - For CoreGraphics this is a NP_CGContext, which has:
     254      - A CoreGraphics context reference (CGContextRef)
     255      - A window handle depending on the event model
     256        - For Carbon it's a WindowRef
     257        - For Cocoa it's a NPNSWindow
     258
     259So:
     260
     261NPWindow (window) {
     262 - x
     263 - y
     264 - width
     265 - height
     266 - clip
     267 - window {
     268    QuickDraw: NP_Port {
     269     # Not relevant since we don't want to support this
     270     # (deprecated in 10.5 and not available in 64-bit)
     271    }
     272    CoreGraphics: NP_CGContext {
     273     - context (CGContextRef)
     274     - window {
     275        Carbon: WindowRef
     276        Cocoa: NPNSWindow
     277    }
     278    OpenGL: NP_GLContext {
     279      # mirrors the CoreGraphics NP_CGContext
     280      # except for the CGLContextObj context
     281    }
     282    CoreAnimation: ???
     283  }
     284}
     285
     286The plugin is notified of changes to this struct by calling NPP_SetWindow
     287
     288
     289== Drawing models ==
     290
     291
     292Stored in the NPNVpluginDrawingModel variable.
     293
     294Plugin can query host by NPN_GetValue(NPNVpluginDrawingModel)
     295
     296=== QuickDraw ===
     297
     298 - Feature can be disabled by defining NP_NO_QUICKDRAW
     299 - Plugin can query for spesific support using NPN_GetValue(NPNVsupportsQuickDrawBool)
     300 - NPDrawingModel value is NPDrawingModelQuickDraw
     301 - Depricated in 10.5 and not available in 64-bit
     302
     303=== CoreGraphics ===
     304
     305 - Plugin can query for spesific support using NPN_GetValue(NPNVsupportsCoreGraphicsBool)
     306 - NPDrawingModel value is NPDrawingModelCoreGraphics
     307 - Preferred drawing model
     308
     309=== OpenGL ===
     310
     311 - Plugin can query for spesific support using NPN_GetValue(NPNVsupportsOpenGLBool)
     312 - NPDrawingModel value is NPDrawingModelOpenGL
     313
     314=== CoreAnimation ===
     315
     316 - Plugin can query for spesific support using NPN_GetValue(NPNVsupportsCoreAnimationBool)
     317 - NPDrawingModel value is NPDrawingModelCoreAnimation
     318 - ???
     319
     320
     321== Event models ==
     322
     323
     324Current event model stored in the NPNVpluginEventModel variable.
     325
     326=== Carbon ===
     327
     328 - Plugin can query for spesific support using NPN_GetValue(NPNVsupportsCarbonBool)
     329 - NPNVpluginEventModel value is NPEventModelCarbon
     330 - The NPEvent type sent to NPP_HandleEvent is an EventRecord
     331
     332=== Cocoa ===
     333
     334 - Plugin can query for spesific support using NPN_GetValue(NPNVsupportsCocoaBool)
     335 - NPNVpluginEventModel is NPEventModelCocoa
     336 - The NPEvent type sent to NPP_HandleEvent is a NPCocoaEvent
     337 - Does not send null-events
     338 - The window field of NPWindow is null. Instead the CGContextRef to use when
     339   drawing is a member of the draw event struct.
     340
     341== Event handling ==
     342
     343
     344As part of the plugin view's createPlugin() the event handler is created after
     345determining the plugin's drawing and event model. The factory function in
     346WebNetscapePluginEventHandler uses the event model to determine the correct
     347event handler subclass.
     348
     349The WebNetscapePluginEventHandler takes care of normal key and mouse events,
     350as well as drawRect(), flagsChanged(), focus, and timers.
     351
     352All event-functions in the event handler uses sendEvent(EventRecord* event)
     353to do its work, which in turn calls sendEvent() on the PluginView.
     354
     355So the call chain is something like:
     356
     357View::mouseDown() to Handler::mouseDown() to Handler::sendEvent() to View::sendEvent()
     358which finally ends up in NPP_HandleEvent.
     359
     360The event passed to View::sendEvent() is a void*, and is passed along to NPP_HandleEvent
     361directly, so it's up to the event handler to pass the correct type of event -- that is,
     362either an EventRecord or a NPCocoaEvent
     363
     364