This article was written in 2009/03/20.

The problem of Plugin API

因為 JavaScript Engine 都是 Single Thread,所以就連 Plugin API (NPAPI) 對於 callback 回 JavaScript都有一個限制:

只有當 JavaScript 呼叫 Plugin 的 Method 時,Plugin 的 Method 中才能
callback 回 JavaScript。也就是說 Plugin callback 透過 NPN_Invoke()
都必須是 Synchronous 的,否則就會搞亂原本 JavaScript Engine 的處理。

之所以有這樣的限制,其實是為了簡化 JavaScript Engine 的實作,因為不需要考慮到 Thread Safe 的問題。但是也就是因為這樣的限制,造成底層的 Plugin 無法「主動」呼叫 JavaScript ,而必須透過 NPN_GetURL() 的機制來呼叫某個特定的 JavaScript function。

There is no NPN_XXXX API for Plugin to send any asynchronous event to Browser except NPN_GetURL().

如此一來,這個 JavaScript function 無法被直接動態指定,而需要間接地字串處理來得到 NPN_GetURL() 所需要的 JavaScript code,對於想以 Browser 來當作 Platform 架構的我們來說,會造成 Application 對於Plugin 的 Dependency 。

如下示意圖:

Architecture of Plugin callback Model with NPN_GetURL()

Bad Design Dependency Hierarchy

 

由於 Plugin 僅能透過 NPN_GetURL() 來傳送訊息給 Browser (JavaScript Application),結果導致 JavaScript Application 必須直接depend on Plugin 所提供之特定 API。

JavaScript Applications would depend on Plugin-specific API.

Better Model of Plugin callback to JavaScript

總結之前的討論,一個良好的 Model 該具備以下幾點:

  • 符合 JavaScript Event Model,讓 User Application 不需針對特定 plugin 實作。
  • Plugin callback 時,需轉化為 JavaScript Event。
  • Abstraction 不用不停 polling plugin 才能有機會執行 callback function。

下圖則是一個良好處理 Plugin callback 的 Model 示意圖:

Architecture of Plugin callback via DOM Event

Plugin callback 轉化為 DOM Event

HTML5 window.postMessage() as the temp solution

在 HTML 5 draft 中的 window.postMessage() 則是我們可以拿來做為處理 plugin callback 的方法,因為 window.postMessage() 產生的 DOM Event 是 JavaScript 可以支援且控制的,而 Abstraction 可以利用 window.addEventListener() 來處理這個 DOM Event,也就是說這個 Model 可以透過標準內的機制避免掉 User Application 直接 Depend on 特定的 Plugin 實作。

以上是利用 DOM 處理 Asynchronous Event 的機制,將 Plugin callback 轉化為 DOM Event,如此一來 User Application 可以將 Plugin callback 當做是另一種 DOM Event 來處理,既符合標準且不會打亂原本 JavaScript Engine 內 Single Thread 的設計。

There is no direct dependency on Plugin-specific API

What is the best solution?

Modify NPAPI to support asynchronous callback to Browser, and transferred to DOM event.