https://developer.mozilla.org/en-US/docs/Plugins/Guide/Drawing_and_Event_Handling
本章介绍如何确定插件实例是窗口化还是无窗口化,如何绘制和重绘插件,以及如何处理插件事件。
在确定插件实例在网页中的显示方式时,您(以及网页作者)有很多选项。编写网页的内容提供商确定其显示模式:插件是嵌入式还是显示在其自己的单独页面中。您可以通过定义插件本身的方式来确定插件是窗口化还是无窗口化。
- 窗口化插件被绘制到网页上的其本机窗口(或本机窗口的一部分)中。窗口插件是不透明的,将页面的一部分隐藏在其显示窗口下方。这种类型的插件决定了它何时自行绘制。
- 无窗口插件不需要本机窗口。它绘制在一个名为drawable的目标中,该目标对应于浏览器窗口或屏幕外位图。可以通过多种方式定义drawable,具体取决于平台。无窗口插件可以是不透明或透明的。无窗口插件仅在响应来自浏览器的绘制消息时自行绘制。
有关HTML确定插件显示模式的方式的信息,请参阅使用HTML显示插件。
注意:在Gecko 1.9 Alpha 7之前的X Window System平台上不支持无窗口插件(错误137189)。
NPWindow结构部分
加载插件时,会将其绘制到目标区域。此目标是窗口插件的本机窗口,或无窗口插件的drawable。该NPWindow结构表示任一天然窗口或可拉伸。此结构包含有关坐标位置,大小,插件状态(窗口或无窗口)以及某些特定于平台的信息的信息。
注意:当插件被绘制到窗口时,插件负责保留状态信息并确保恢复原始状态。
对于无窗口插件,浏览器使用表示可绘制的NPWindow结构调用NPP_SetWindow方法。对于窗口化插件,浏览器使用表示窗口的结构调用方法。NPP_SetWindow
NPWindow
//The NPWindow Structure typedef enum { NPWindowTypeWindow = 1, NPWindowTypeDrawable } NPWindowType; typedef struct _NPWindow { void* window; /* Platform-specific handle */ uint32 x; /* Position of top-left corner */ uint32 y; /* relative to a Netscape page */ uint32 width; /* Maximum window size */ uint32 height; NPRect clipRect; /* Clipping rectangle in port coordinates */ #ifdef XP_UNIX void * ws_info; /* Platform-dependent additional data */ #endif /* XP_UNIX */ NPWindowType type; /* Whether this is a window or a drawable */ } NPWindow;
window参数是Windows和Unix上浏览器窗口层次结构中本机窗口元素的特定于平台的句柄。在Mac OS上,window是指向NP_Port的指针。
该x
和y
字段指定插件相对于页面的左上角。
该width
和height
字段指定插件区域的尺寸。插件不应修改这些值。
该clipRect
字段在坐标系中定义插件的剪切矩形,其中原点是可绘制或窗口的左上角。只要drawable发生变化,浏览器就会调用NPP_SetWindow。对于无窗口插件,插件不可见clipRect
的0,0,0,0
信号。
type字段表示NPWindow
目标区域的类型:
- NPWindowTypeWindow:窗口插件。窗口字段包含窗口的特定于平台的句柄。
- NPWindowTypeDrawable:无窗口插件。窗口字段包含一个特定于平台的可绘制句柄,如下所示:
- Windows:HDC
- Mac OS:指向
NP_Port
结构的指针。 - Unix / X11:没用过。(绘图信息中提供了drawable。见下文。)
在这两种情况下,drawable都可以是一个离屏像素图。
绘图插件部分
本节介绍在绘制窗口插件和无窗口插件时使用的方法和过程。以下各节介绍了仅适用于其中一种插件类型的进程。
插件使用这些方法来绘制插件和处理事件:
浏览器调用的插件方法:
- NPP_HandleEvent:向实例提供特定于平台的事件。
- NPP_Print:为实例请求特定于平台的打印操作。
- NPP_SetWindow:设置插件绘制的窗口。
浏览器端方法,由插件调用:
- NPN_ForceRedraw:强制向无窗口插件发送绘制消息。
- NPN_InvalidateRect:在重新绘制或刷新之前,在无窗口插件中使区域无效。
- NPN_InvalidateRegion:在重新绘制或刷新之前,在无窗口插件中使区域无效。
打印插件
浏览器调用该NPP_Print
方法来请求插件实例自行打印。
void NPP_Print(NPP instance, NPPrint *printInfo);
instance参数表示当前插件。
该PrintInfo
参数确定打印模式。它被设置为NP_FULL
指示整页插件打印,或者NP_EMBED
是否是嵌入式插件,作为嵌入其中的窗口的一部分打印。
- 嵌入式插件与浏览器共享打印。插件打印它占用的页面部分,浏览器处理剩余的打印过程,包括显示打印对话框,获取打印机设备上下文,当然还有打印页面的其余部分。
嵌入式插件可以将pluginPrinted
其PrintInfo
参数中的字段设置为false(默认值)。这是_NPFullPrint
结构的子结构的一个领域NPPrint
。浏览器显示必要的打印对话框并NPP_Print
再次调用。这个时候,PrintInfo->mode
应该设置为NP_EMBED
。
- 整页插件可根据需要处理打印对话框和打印过程。在这种情况下,在浏览器显示任何打印对话框之前,
NPP_Print
调用PrintInfo->mode
等于NP_FULL
。在Mac OS上,整页打印要求PrintInfo字段包含标准Mac OS THPrint(请参阅参考资料Printing.h
)。
当然,NPP_Print
也PrintInfo->mode
可以NP_EMBED
在嵌入实例时调用它。在这种情况下,platformPrint->embedPrint.window
包含应该打印插件的窗口。
在MS Windows上,请注意窗口矩形的坐标是TWIPS格式。因此,DPtoLP
在输出文本时,需要使用Windows API调用转换x和y坐标。
设置窗口
浏览器调用该NPP_SetWindow
函数来设置插件绘制或返回错误代码的窗口。此窗口对实例的生命周期有效,或直到NPP_SetWindow
使用不同的值再次调用。
NPP_SetWindow
对给定实例的后续调用通常意味着窗口已调整大小。如果任一窗口或window->window
为null,则插件不得在窗口上执行任何其他图形操作,并且应释放任何相关资源。
NPError NPP_SetWindow(NPP instance, NPWindow *window);
instance参数表示当前插件。
window参数是指向插件的绘图目标的指针。对于无窗口插件,指定的特定于平台的窗口信息window->window
是drawable的特定于平台的句柄。
MS Windows和Unix
对于MS Windows和Unix上的窗口插件,该window->window
字段是Netscape窗口层次结构的子窗口的句柄。
苹果系统
该window->window
字段指向一个NP_Port
结构。
获取资讯
要从浏览器接收信息,插件会调用该NPN_GetValue
方法。
NPError NPN_GetValue(NPP instance, NPNVariable variable, void *value);
该instance
参数表示当前插件。
Unix和MS Windows
查询的信息在变量参数中返回。此参数仅对Unix和MS Windows平台有效。对于Unix,值是当前display(NPNVxDisplay
)或应用程序的context(NPNVxtAppContext
)。对于MS Windows,该值是发生插件绘图的本机窗口(NPNVnetscapeWindow
)。
value参数包含插件的名称。
您还可以使用它NPN_GetValue
来帮助创建无窗口插件的菜单或对话框。
窗口插件部分
浏览器为每个窗口插件提供了自己的本机窗口,通常是浏览器窗口本身的子窗口,用于绘制。该插件可完全控制该窗口内的绘图和事件处理。
在Mac OS上,浏览器不为窗口插件提供本机窗口,因为Mac OS平台不支持子窗口。相反,窗口插件在浏览器指定的偏移处绘制到与浏览器窗口关联的图形端口。
在MS Windows和Unix上,浏览器为每个插件实例创建一个子窗口,并向其传递一个窗口NPP_SetWindow
。在Mac OS上,应用程序使用NPP_SetWindow
将其图形端口的矩形部分专用于每个实例。在任何平台上,浏览器都应该小心不要插入插件区域,反之亦然。传入的数据结构NPP_SetWindow
是一个NPWindow
对象,它包含实例区域的坐标和各种特定于平台的数据。
通常,浏览器NPP_SetWindow
在创建实例后调用,以便插件可以立即开始绘制。但是,浏览器可以创建NPP_SetWindow
从不调用的不可见实例,并且永远不会创建窗口。当使用object
已使用CSS规则隐藏的HTML 元素(请参阅简介中的插件显示模式)或使用已设置embed
其hidden
属性的元素调用插件时,会发生这种情况。
NPP_SetWindow
只要实例的大小或位置发生变化,浏览器就应该再次调用,每次都传递相同的NPWindow
对象,但值不同。
浏览器还可以NPP_SetWindow
使用窗口的不同值多次调用,包括null。例如,如果用户从页面中删除实例,则浏览器应调用NPP_SetWindow
窗口值为null。此值可防止实例进一步绘制,直到将其粘贴回页面并NPP_SetWindow
再次使用新值进行调用。
苹果系统
在Mac OS上,浏览器传递NP_Port
结构窗口字段中的NPWindow
结构。此结构包含指向CGraphPtr
插件实例应绘制到的图形端口()的指针以及此端口左上角的x和y坐标。插件可以使用这些坐标调用SetOrigin(portx, porty)
,将其矩形的左上角放在(0,0)处。Mac OS GrafPort
结构的clipRgn
字段应设置为端口坐标中实例的剪切矩形。
由于插件和浏览器共享相同的图形端口,因此它们共同负责正确管理它。浏览器在以两种方式传递插件更新事件之前设置插件的端口:
- 浏览器调用
SetOrigin(npport->portx, npport->porty)
。此方法使实例的左上角坐标等于(0,0)。 - 浏览器将端口的剪辑区域设置为当前可见的插件区域(不滚动页面,浮动调色板遮挡或隐藏)。
但是,要使插件在任何其他时间绘制,例如,要在鼠标按下事件时突出显示或在空闲时绘制动画,它必须保存端口的当前设置,并根据需要设置其绘图环境,绘制,然后将端口还原到以前的设置。在这种情况下,插件使浏览器无需在每次调用插件之前和之后保存和恢复其端口设置。
浏览器和插件都可以为共享端口安装Drag Manager处理程序。因为无论光标位于何处,Drag Manager都会调用两个处理程序,当光标位于实例矩形上时,浏览器不会显示拖动突出显示。此外,当实例矩形内发生丢弃时,浏览器不执行任何操作。然后,插件可以在实例矩形中显示拖动突出显示和处理丢弃。
浏览器还负责向插件发送针对实例的所有事件,例如当光标位于实例矩形内时单击鼠标,或者在应用程序打开和关闭时暂停和恢复事件。通过调用将事件发送到插件NPP_HandleEvent
; 有关事件类型的完整列表,请参阅参考条目NPEvent
。
视窗
在Windows上,浏览器注册一个窗口类,并为插件实例创建该类的实例。然后,插件可以对窗口进行子类化以接收所需的任何事件。如果插件需要接收周期性时间消息(例如,对于动画),则应使用计时器或单独的线程。
Unix的
在Unix上,浏览器为实例创建Motif绘图区域窗口小部件,并在窗口字段中传递窗口小部件的窗口ID NPWindow
。此外,浏览器创建一个NPSetWindowCallbackStruct
对象,并将它在ws_info
的领域NPWindow
。与在Windows上一样,插件可以接收实例的所有事件,在本例中是通过窗口小部件。如果插件需要接收定期时间消息,则应安装定时器或分叉线程。
窗口插件的事件处理
窗口化插件实例的所有成像和用户界面事件都根据其本机平台的窗口系统进行处理。插件API提供了一个本机窗口句柄,实例通过API调用在其中绘制NPP_SetWindow
。NPP_SetWindow
将实例传递给NPWindow
包含本机窗口句柄的对象。
在Windows和Unix上,每个实例在浏览器窗口层次结构中接收自己的子窗口,并且映像和事件处理与此窗口相关。Mac OS不支持子窗口。本机窗口在实例和浏览器之间共享。实例必须将其绘图限制为共享窗口的指定区域,并且必须始终保存当前设置,设置绘图环境并将共享绘图环境恢复为先前的设置。在Mac OS上,事件通过显式提供给实例NPP_HandleEvent
。
无窗口插件部分
无窗口插件不需要绘制本机窗口。相反,它会绘制成一个可绘制的(HDC
在Windows或CGrafPtr
Mac OS上),可以在屏幕上或屏幕外。
无窗口插件为插件编写器提供了一些重要的设计可能性:
- 您可以在一个部分中放置一个无窗口插件; 其他部分可以存在于其上方和下方。
- 您可以创建透明插件。在这种情况下,浏览器绘制插件后面的页面部分。无窗口插件仅绘制不透明的部分。这样,插件可以在现有背景上绘制不规则形状的区域,例如图形或文本。
- 浏览器支持插件的屏幕外绘制。这使得可以操纵插件内容。例如,3D应用程序可以使用插件的内容作为纹理贴图。
由于无窗口插件可以分层或绘制到任意drawables,因此浏览器(与本机窗口系统相对)负责控制其绘图和事件处理。
有关控制插件实例的绘制的更多信息,请参阅以下各项:
指定插件是无窗口的
要指定插件是无窗口的,请使用该NPN_SetValue
方法。
instance参数表示当前插件。变量参数包含要设置的插件信息。value参数返回插件的名称。
要指定插件是无窗口的,请使用NPN_SetValue
with NPPVpluginWindowBool
作为variable的值,使用false作为value的值。该插件通过它进行此调用NPP_New method
。如果插件没有进行此调用,则会将其视为窗口插件。
Mac OS X上的插件始终没有窗口。
NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char *argn[], char *argv[], NPSavedData *saved) { ... NPError result = NPN_SetValue(instance, NPPVpluginWindowBool, (void*)false); }
使绘图区无效
在重绘或刷新部分绘图区域之前,无窗口插件必须首先使用以下任一浏览器方法使区域无效:NPN_InvalidateRect
或NPN_InvalidateRegion
。两种方法都执行相同的操作:
- 它们在重新绘制或刷新之前使指定的绘图区域无效。
- 它们将更新事件或绘制消息传递给插件。
浏览器以定时的间隔重绘文档的无效区域和无窗口插件。要强制绘制消息,插件可以NPN_ForceRedraw
在调用其中一个invalidate方法后调用。如果插件调用其中一种方法,它会异步接收绘制消息。
void NPN_InvalidateRect(NPP instance, NPRect *invalidRect); void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion);
instance参数表示当前插件。在invalidRect
和invalidRegion
参数表示无效区域,在坐标系的原点是在插件顶部左指定。
这两种方法都会导致该NPP_HandleEvent
方法将更新事件或绘制消息传递给插件。
#ifdef XP_MAC typedef RgnHandle NPRegion; #elif defined(XP_WIN) typedef HRGN NPRegion; #elif defined(XP_UNIX) typedef Region NPRegion; #else typedef void* NPRegion; #endif /* XP_MAC */ void NPN_InvalidateRect(NPP instance, NPRect *invalidRect); void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion);
强制绘制消息
窗口和无窗口插件具有不同的绘图模型。窗口插件确定何时绘制,而无窗口插件绘制以响应来自浏览器的绘制消息。NPN_ForceRedraw()
一旦使用NPN_InvalidateRect()
或使区域无效,插件可以调用同步强制绘制消息NPN_InvalidateRegion()
。
void NPN_ForceRedraw(NPP instance);//再某些浏览器上不支持改消息 by_songgp add
此方法导致插件的同步更新事件或绘制消息。
NPN_ForceRedraw()
。
收到油漆信息
除非接收到绘制消息,否则插件不得绘制到其drawable中。它不需要调用特定于平台的函数来开始在窗口中绘制。也就是说,插件不会BeginPaint
在Windows或BeginUpdate
Mac OS上调用。
-
视窗
插件会收到一条WM_PAINT
消息。该lParam
参数WM_PAINT
保存指向RECT
指定更新区域的边界框的结构的指针。但是,有些插件会选择忽略这个paint rect,而是总是更新整个插件窗口。此外,由于插件和浏览器共享相同的HDC,插件必须将当前设置保存在HDC上,设置自己的环境,自行绘制,并将HDC恢复到以前的设置。每当控件返回浏览器时,必须在从NPP_HandleEvent
调用与绘图相关的浏览器端方法之前或之后返回之前恢复HDC设置。
-
苹果系统
插件接收更新事件。drawable CGrafPtr
端口的剪辑区域设置为更新区域。与Mac OS上的窗口插件一样,插件必须首先保存端口的当前设置,根据需要设置绘图环境,绘制并将端口恢复到先前的设置。这应该在插件从NP_HandleEvent
插件调用与绘图相关的浏览器方法之前或之后返回之前发生。
-
的Unix / X11
插件接收GraphicsExpose
事件。该XGraphicsExposeEvent
结构包含Xlib Drawable
(它是一个offscreen pixmap),它Display
和相对于drawable左上角指定的脏矩形(可选剪辑矩形)。
插件应绘制到NPWindow结构和成员中Drawable
指定的偏移量,x
并在y
成员中指定剪辑矩形,并在成员中指定和指定。clipRect
Visual
Colormap
ws_info
制作插件不透明
如果没有透明区域,无窗口插件是不透明的。当浏览器为插件生成绘制消息时,它假定插件负责绘制要更新的整个区域。由于浏览器不需要在插件后面绘制背景,因此不透明的无窗口插件比透明插件更有效。
默认情况下,无窗口插件是透明的。要使透明插件不透明,请调用NPN_SetValue
设置NPPVpluginTransparentBool
为false。插件可以在指定它是无窗口插件后随时调用此方法。
使插件透明化
如果窗口具有透明区域,则它是透明的。以下是两个具有透明区域的插件示例:
- 一个小于封闭
object
或embed
元素指定区域的插件 - 一个非矩形边界的插件
浏览器负责呈现透明无窗口插件的背景。在为插件生成绘制消息之前,浏览器会确保已将背景绘制到要更新的区域中。然后,插件可以绘制与其不透明区域对应的更新区域的一部分。这可确保插件的透明区域始终有效。
默认情况下,无窗口插件是透明的。如果要使不透明的无窗口插件透明,请调用该NPN_SetValue
方法并将NPPVpluginTransparentBool
值设置为true。插件可以在指定它是无窗口插件后随时调用此方法。
创建弹出菜单和对话框
仅限MS Windows和Unix / X11
无窗口插件不会在其自己的本机窗口中绘制。相反,它直接绘制给它的drawable。如果您需要在插件中显示弹出菜单和模式对话框,则此行为会出现问题; 插件需要父窗口才能创建这样的窗口。
要解决此问题,请使用NPN_GetValue
以找出插件的绘制位置。使用NPNVnetscapeWindow
作为变量参数的值。
NPError NPN_GetValue(NPP instance, NPNVariable variable, void *value);
instance参数表示当前插件。变量参数包含调用请求的信息,在这种情况下NPNVnetscapeWindow
(发生插件绘图的本机窗口)。
-
视窗
请求的信息(HWND类型的值)从值参数中的NPN_GetValue返回。
在许多情况下,插件可能仍然必须创建自己的窗口(浏览器窗口的透明子窗口),以充当弹出菜单和模式对话框的所有者窗口。您可以为此透明子窗口提供自己的WindowProc进程。插件可以使用它来处理WM_COMMAND
由于跟踪弹出菜单或模式对话框而发送给它的消息。
-
的Unix / X11
NPN_GetValue的value参数必须指向Xlib Window
。成功返回后,它将包含浏览器顶层窗口。使用此选项Window
可在对话框上设置WM_TRANSIENT_FOR属性。
无窗口插件的事件处理
在所有平台上,特定于平台的事件都通过该NPP_HandleEvent
方法传递给无窗口插件。插件必须NPP_HandleEvent
在处理事件时返回true,否则返回false。Mac OS将这种机制用于窗口插件和无窗口插件; 在此平台上,NPP_HandleEvent
插件是唯一可以从其主机应用程序接收事件的方式。
int16 NPP_HandleEvent(NPP instance, NPEvent *event);
instance参数表示当前插件。有关事件类型的列表,应用程序负责传递给插件,请参阅NPEvent
结构。
此代码显示通过此方法为每个平台传递的特定数据:
#ifdef XP_MAC typedef EventRecord NPEvent; #elif defined(XP_WIN) typedef struct _NPEvent { int16 event; int16 wParam; int32 lParam; } NPEvent; #elif defined(XP_UNIX) typedef XEvent NPEvent; #else typedef void NPEvent; #endif /* XP_MAC */ int16 NPP_HandleEvent(NPP instance, NPEvent* event);
在Mac OS上,NPP_HandleEvent
调用时,正确设置当前端口,使其原点与插件的左上角匹配。插件不需要设置当前端口以进行鼠标坐标转换。