The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1163 lines
36KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. //==============================================================================
  24. /*
  25. This file contains all the mess that creates an NPAPI interface, and connects
  26. that interface to your BrowserPluginComponent object.
  27. */
  28. //==============================================================================
  29. #if defined (__APPLE__) && ! JUCE_NPAPI_WRAPPED_IN_MM
  30. #error "On the Mac, you can't compile this .cpp file directly - use juce_NPAPI_GlueCode.mm instead"
  31. #endif
  32. #define XPCOM_GLUE
  33. //==============================================================================
  34. #if _MSC_VER
  35. #define XP_WIN
  36. #define _X86_
  37. #include <windows.h>
  38. #include <windowsx.h>
  39. #include "npapi/npupp.h"
  40. // Cunning trick used to add functions to export list and avoid messing about with .def files.
  41. // (can't add a declspec because the functions have already been pre-declared in the npapi headers).
  42. #define EXPORTED_FUNCTION comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)
  43. //==============================================================================
  44. #elif defined (__APPLE__)
  45. #define XP_MACOSX
  46. #define OSCALL
  47. #include <WebKit/npapi.h>
  48. #include <WebKit/npfunctions.h>
  49. #include <WebKit/npruntime.h>
  50. //==============================================================================
  51. #else
  52. #define XP_UNIX
  53. #include "npapi.h"
  54. #include "npupp.h"
  55. #include "npruntime.h"
  56. #endif
  57. //==============================================================================
  58. #include "../../../juce_amalgamated.h"
  59. #include "juce_BrowserPluginComponent.h"
  60. #include "juce_IncludeBrowserPluginInfo.h"
  61. #if JUCE_MAC && JUCE_DEBUG && 0
  62. static void log (const String& s)
  63. {
  64. FILE* f = fopen ("/Users/jules/Desktop/log.txt", "a+");
  65. fprintf (f, (const char*) s);
  66. fprintf (f, "\n");
  67. fflush (f);
  68. fclose (f);
  69. }
  70. #else
  71. #define log(a)
  72. #endif
  73. //==============================================================================
  74. #if JUCE_MAC
  75. static const String nsStringToJuce (NSString* s) { return String::fromUTF8 ((juce::uint8*) [s UTF8String]); }
  76. static NSString* juceStringToNS (const String& s) { return [NSString stringWithUTF8String: (const char*) s.toUTF8()]; }
  77. #pragma export on
  78. extern "C"
  79. {
  80. NPError NP_Initialize (NPNetscapeFuncs*);
  81. NPError NP_GetEntryPoints (NPPluginFuncs*);
  82. NPError NP_Shutdown();
  83. }
  84. #pragma export off
  85. #endif
  86. //==============================================================================
  87. static NPNetscapeFuncs browser;
  88. String browserVersionDesc;
  89. //==============================================================================
  90. NPError NP_GetValue (void* future, NPPVariable variable, void* value)
  91. {
  92. return NPP_GetValue ((NPP_t*) future, variable, value);
  93. }
  94. #if JUCE_WIN32 || JUCE_MAC
  95. NPError OSCALL NP_GetEntryPoints (NPPluginFuncs* funcs)
  96. {
  97. #if JUCE_WIN32
  98. #pragma EXPORTED_FUNCTION
  99. #endif
  100. log ("NP_GetEntryPoints");
  101. if (funcs == 0 || (funcs->size > 0 && funcs->size < sizeof (NPPluginFuncs)))
  102. return NPERR_INVALID_FUNCTABLE_ERROR;
  103. funcs->size = sizeof (NPPluginFuncs);
  104. funcs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
  105. funcs->newp = NPP_New;
  106. funcs->destroy = NPP_Destroy;
  107. funcs->setwindow = NPP_SetWindow;
  108. funcs->newstream = NPP_NewStream;
  109. funcs->destroystream = NPP_DestroyStream;
  110. funcs->asfile = NPP_StreamAsFile;
  111. funcs->writeready = NPP_WriteReady;
  112. #if JUCE_MAC
  113. funcs->write = (NPP_WriteProcPtr) NPP_Write;
  114. #else
  115. funcs->write = NPP_Write;
  116. #endif
  117. funcs->print = NPP_Print;
  118. funcs->event = NPP_HandleEvent;
  119. funcs->urlnotify = NPP_URLNotify;
  120. funcs->getvalue = NPP_GetValue;
  121. funcs->setvalue = NPP_SetValue;
  122. funcs->javaClass = 0;
  123. return NPERR_NO_ERROR;
  124. }
  125. #endif
  126. NPError OSCALL NP_Initialize (NPNetscapeFuncs* funcs
  127. #ifdef XP_UNIX
  128. , NPPluginFuncs* pluginFuncs
  129. #endif
  130. )
  131. {
  132. #if JUCE_WIN32
  133. #pragma EXPORTED_FUNCTION
  134. #endif
  135. log ("NP_Initialize");
  136. if (funcs == 0)
  137. return NPERR_INVALID_FUNCTABLE_ERROR;
  138. if (((funcs->version >> 8) & 0xff) > NP_VERSION_MAJOR)
  139. return NPERR_INCOMPATIBLE_VERSION_ERROR;
  140. zerostruct (browser);
  141. memcpy (&browser, funcs, jmin (funcs->size, sizeof (browser)));
  142. #ifdef XP_UNIX
  143. pluginFuncs->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
  144. pluginFuncs->size = sizeof (NPPluginFuncs);
  145. pluginFuncs->newp = NewNPP_NewProc (NPP_New);
  146. pluginFuncs->destroy = NewNPP_DestroyProc (NPP_Destroy);
  147. pluginFuncs->setwindow = NewNPP_SetWindowProc (NPP_SetWindow);
  148. pluginFuncs->newstream = NewNPP_NewStreamProc (NPP_NewStream);
  149. pluginFuncs->destroystream = NewNPP_DestroyStreamProc (NPP_DestroyStream);
  150. pluginFuncs->asfile = NewNPP_StreamAsFileProc (NPP_StreamAsFile);
  151. pluginFuncs->writeready = NewNPP_WriteReadyProc (NPP_WriteReady);
  152. pluginFuncs->write = NewNPP_WriteProc (NPP_Write);
  153. pluginFuncs->print = NewNPP_PrintProc (NPP_Print);
  154. pluginFuncs->urlnotify = NewNPP_URLNotifyProc (NPP_URLNotify);
  155. pluginFuncs->event = 0;
  156. pluginFuncs->getvalue = NewNPP_GetValueProc (NPP_GetValue);
  157. #ifdef OJI
  158. pluginFuncs->javaClass = NPP_GetJavaClass();
  159. #endif
  160. #endif
  161. return NPERR_NO_ERROR;
  162. }
  163. NPError OSCALL NP_Shutdown()
  164. {
  165. #if JUCE_WIN32
  166. #pragma EXPORTED_FUNCTION
  167. #endif
  168. log ("NP_Shutdown");
  169. return NPERR_NO_ERROR;
  170. }
  171. char* NP_GetMIMEDescription()
  172. {
  173. log ("NP_GetMIMEDescription");
  174. static String mimeDesc;
  175. mimeDesc = String (T(JuceBrowserPlugin_MimeType))
  176. + T(":") + String (T(JuceBrowserPlugin_FileSuffix))
  177. + T(":") + String (T(JuceBrowserPlugin_Name));
  178. return (char*) (const char*) mimeDesc.toUTF8();
  179. }
  180. //==============================================================================
  181. /*
  182. NPError NPN_GetURLNotify (NPP instance, const char *url, const char *target, void* notifyData)
  183. {
  184. return (browser.version & 0xFF) >= NPVERS_HAS_NOTIFICATION
  185. ? browser.geturlnotify (instance, url, target, notifyData);
  186. : NPERR_INCOMPATIBLE_VERSION_ERROR;
  187. }
  188. NPError NPN_PostURLNotify (NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file, void* notifyData)
  189. {
  190. return (browser.version & 0xFF) >= NPVERS_HAS_NOTIFICATION
  191. ? browser.posturlnotify (instance, url, window, len, buf, file, notifyData)
  192. : NPERR_INCOMPATIBLE_VERSION_ERROR;
  193. }
  194. NPError NPN_NewStream (NPP instance, NPMIMEType type, const char* target, NPStream** stream)
  195. {
  196. return (browser.version & 0xFF) >= NPVERS_HAS_STREAMOUTPUT
  197. ? browser.newstream (instance, type, target, stream)
  198. : NPERR_INCOMPATIBLE_VERSION_ERROR;
  199. }
  200. int32 NPN_Write (NPP instance, NPStream *stream, int32 len, void *buffer)
  201. {
  202. return (browser.version & 0xFF) >= NPVERS_HAS_STREAMOUTPUT
  203. ? browser.write (instance, stream, len, buffer)
  204. : -1;
  205. }
  206. NPError NPN_DestroyStream (NPP instance, NPStream* stream, NPError reason)
  207. {
  208. return (browser.version & 0xFF) >= NPVERS_HAS_STREAMOUTPUT
  209. ? browser.destroystream (instance, stream, reason)
  210. : NPERR_INCOMPATIBLE_VERSION_ERROR;
  211. }
  212. */
  213. //==============================================================================
  214. class BrowserPluginHolderComponent : public Component
  215. {
  216. public:
  217. //==============================================================================
  218. BrowserPluginHolderComponent (NPP npp_)
  219. : npp (npp_),
  220. child (0)
  221. {
  222. log ("BrowserPluginHolderComponent created");
  223. #if JUCE_WIN32
  224. parentHWND = 0;
  225. oldWinProc = 0;
  226. #else
  227. currentParentView = 0;
  228. #endif
  229. setOpaque (true);
  230. setWantsKeyboardFocus (false);
  231. addAndMakeVisible (child = createBrowserPlugin());
  232. jassert (child != 0); // You have to create one of these!
  233. }
  234. ~BrowserPluginHolderComponent()
  235. {
  236. log ("BrowserPluginHolderComponent deleted");
  237. setWindow (0);
  238. deleteAndZero (child);
  239. }
  240. //==============================================================================
  241. void paint (Graphics& g)
  242. {
  243. if (child == 0 || ! child->isOpaque())
  244. g.fillAll (Colours::white);
  245. }
  246. void resized()
  247. {
  248. if (child != 0)
  249. child->setBounds (0, 0, getWidth(), getHeight());
  250. }
  251. const var getObject()
  252. {
  253. return child->getJavascriptObject();
  254. }
  255. //==============================================================================
  256. NPP npp;
  257. BrowserPluginComponent* child;
  258. private:
  259. //==============================================================================
  260. #if JUCE_WIN32
  261. HWND parentHWND;
  262. WNDPROC oldWinProc;
  263. void resizeToParentWindow()
  264. {
  265. if (IsWindow (parentHWND))
  266. {
  267. RECT r;
  268. GetWindowRect (parentHWND, &r);
  269. setBounds (0, 0, r.right - r.left, r.bottom - r.top);
  270. }
  271. }
  272. static LRESULT CALLBACK interceptingWinProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  273. {
  274. switch (msg)
  275. {
  276. case WM_PAINT:
  277. {
  278. PAINTSTRUCT ps;
  279. HDC hdc = BeginPaint (hWnd, &ps);
  280. EndPaint (hWnd, &ps);
  281. }
  282. return 0;
  283. case WM_ERASEBKGND:
  284. return 1;
  285. case WM_WINDOWPOSCHANGING:
  286. if ((((WINDOWPOS*) lParam)->flags & SWP_NOSIZE) == 0)
  287. {
  288. BrowserPluginHolderComponent* const comp = (BrowserPluginHolderComponent*) GetWindowLong (hWnd, GWL_USERDATA);
  289. comp->resizeToParentWindow();
  290. }
  291. break;
  292. default:
  293. break;
  294. }
  295. return DefWindowProc (hWnd, msg, wParam, lParam);
  296. }
  297. public:
  298. void setWindow (NPWindow* window)
  299. {
  300. HWND newHWND = (window != 0 ? ((HWND) window->window) : 0);
  301. if (parentHWND != newHWND)
  302. {
  303. removeFromDesktop();
  304. setVisible (false);
  305. if (IsWindow (parentHWND))
  306. {
  307. SubclassWindow (parentHWND, oldWinProc); // restore the old winproc..
  308. oldWinProc = 0;
  309. }
  310. parentHWND = newHWND;
  311. if (parentHWND != 0)
  312. {
  313. addToDesktop (0);
  314. HWND ourHWND = (HWND) getWindowHandle();
  315. SetParent (ourHWND, parentHWND);
  316. DWORD val = GetWindowLong (ourHWND, GWL_STYLE);
  317. val = (val & ~WS_POPUP) | WS_CHILD;
  318. SetWindowLong (ourHWND, GWL_STYLE, val);
  319. setVisible (true);
  320. oldWinProc = SubclassWindow (parentHWND, (WNDPROC) interceptingWinProc);
  321. SetWindowLong (parentHWND, GWL_USERDATA, (LONG) this);
  322. resizeToParentWindow();
  323. }
  324. }
  325. }
  326. //==============================================================================
  327. #else
  328. NSView* currentParentView;
  329. NSView* findViewAt (NSView* parent, float x, float y) const
  330. {
  331. NSRect frame = [parent frame];
  332. NSRect bounds = [parent bounds];
  333. x -= frame.origin.x;
  334. y -= frame.origin.y;
  335. Rectangle rr (frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
  336. Rectangle rr2 (bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
  337. //log (String ((int) x) + ", " + String ((int) y) + " - " + nsStringToJuce([parent description]) + " " + rr.toString() + " " + rr2.toString());
  338. if (x >= 0 && x < frame.size.width && y >= 0 && y < frame.size.height)
  339. {
  340. x += bounds.origin.x; // adjust for scrolling panels
  341. y += bounds.origin.y;
  342. for (int i = [[parent subviews] count]; --i >= 0;)
  343. {
  344. NSView* v = (NSView*) [[parent subviews] objectAtIndex: i];
  345. if (v != (NSView*) getWindowHandle())
  346. {
  347. NSView* found = findViewAt (v, x, y);
  348. if (found != 0)
  349. return found;
  350. }
  351. }
  352. return parent;
  353. }
  354. return 0;
  355. }
  356. public:
  357. static bool isBrowserContentView (NSView* v)
  358. {
  359. return [[v className] isEqualToString: @"WebNetscapePluginDocumentView"]
  360. || [[v className] isEqualToString: @"WebPluginDocumentView"]
  361. || [[v className] isEqualToString: @"ChildView"];
  362. }
  363. void setWindow (NPWindow* window)
  364. {
  365. const ScopedAutoReleasePool pool;
  366. NSView* parentView = 0;
  367. WindowRef windowRef = window != 0 ? ((NP_CGContext*) window->window)->window : 0;
  368. if (windowRef != 0)
  369. {
  370. NSWindow* win = [[[NSWindow alloc] initWithWindowRef: windowRef] autorelease];
  371. const Rectangle clip (window->clipRect.left, window->clipRect.top,
  372. window->clipRect.right - window->clipRect.left,
  373. window->clipRect.bottom - window->clipRect.top);
  374. const Rectangle target ((int) window->x, (int) window->y, (int) window->width, (int) window->height);
  375. const Rectangle intersection (clip.getIntersection (target));
  376. // in firefox the clip rect is usually out of step with the target rect, but in safari it matches
  377. log ("plugin window clip: " + clip.toString());
  378. log ("plugin window target: " + target.toString());
  379. log ("plugin window intersection: " + intersection.toString());
  380. if (! intersection.isEmpty())
  381. {
  382. NSView* content = [win contentView];
  383. float wx = (float) intersection.getCentreX();
  384. float wy = (float) intersection.getCentreY();
  385. NSRect v = [content convertRect: [content frame] toView: nil];
  386. NSRect w = [win frame];
  387. log ("wx: " + Rectangle (v.origin.x, v.origin.y, v.size.width, v.size.height).toString()
  388. + " " + Rectangle (w.origin.x, w.origin.y, w.size.width, w.size.height).toString());
  389. // adjust the requested window pos to deal with the content view's origin within the window
  390. wy -= w.size.height - (v.origin.y + v.size.height);
  391. parentView = findViewAt (content, wx, wy);
  392. if (! isBrowserContentView (parentView))
  393. parentView = currentParentView;
  394. }
  395. log ("parent: " + nsStringToJuce ([parentView description]));
  396. }
  397. if (parentView != currentParentView)
  398. {
  399. log ("new view: " + nsStringToJuce ([parentView description]));
  400. removeFromDesktop();
  401. setVisible (false);
  402. currentParentView = parentView;
  403. if (parentView != 0)
  404. {
  405. setSize (window->width, window->height);
  406. addToDesktop (0, parentView);
  407. setVisible (true);
  408. }
  409. }
  410. if (window != 0)
  411. setSize (window->width, window->height);
  412. }
  413. #endif
  414. };
  415. //==============================================================================
  416. static NPIdentifier getIdentifierFromString (const var::identifier& s) throw()
  417. {
  418. return browser.getstringidentifier (s.name.toUTF8());
  419. }
  420. static const var createValueFromNPVariant (NPP npp, const NPVariant& v);
  421. static void createNPVariantFromValue (NPP npp, NPVariant& out, const var& v);
  422. #if JUCE_DEBUG
  423. static int numDOWNP = 0, numJuceSO = 0;
  424. #endif
  425. //==============================================================================
  426. class DynamicObjectWrappingNPObject : public DynamicObject
  427. {
  428. NPP npp;
  429. NPObject* const source;
  430. public:
  431. DynamicObjectWrappingNPObject (NPP npp_, NPObject* const source_)
  432. : npp (npp_),
  433. source (browser.retainobject (source_))
  434. {
  435. DBG ("num NP wrapper objs: " + String (++numDOWNP));
  436. }
  437. ~DynamicObjectWrappingNPObject()
  438. {
  439. browser.releaseobject (source);
  440. DBG ("num NP wrapper objs: " + String (--numDOWNP));
  441. }
  442. const var getProperty (const var::identifier& propertyName) const
  443. {
  444. NPVariant result;
  445. VOID_TO_NPVARIANT (result);
  446. browser.getproperty (npp, source, getIdentifierFromString (propertyName), &result);
  447. const var v (createValueFromNPVariant (npp, result));
  448. browser.releasevariantvalue (&result);
  449. return v;
  450. }
  451. bool hasProperty (const var::identifier& propertyName) const
  452. {
  453. NPVariant result;
  454. VOID_TO_NPVARIANT (result);
  455. const bool hasProp = browser.getproperty (npp, source, getIdentifierFromString (propertyName), &result);
  456. browser.releasevariantvalue (&result);
  457. return hasProp;
  458. }
  459. void setProperty (const var::identifier& propertyName, const var& newValue)
  460. {
  461. NPVariant value;
  462. createNPVariantFromValue (npp, value, newValue);
  463. browser.setproperty (npp, source, getIdentifierFromString (propertyName), &value);
  464. browser.releasevariantvalue (&value);
  465. }
  466. void removeProperty (const var::identifier& propertyName)
  467. {
  468. browser.removeproperty (npp, source, getIdentifierFromString (propertyName));
  469. }
  470. bool hasMethod (const var::identifier& methodName) const
  471. {
  472. return browser.hasmethod (npp, source, getIdentifierFromString (methodName));
  473. }
  474. const var invokeMethod (const var::identifier& methodName,
  475. const var* parameters,
  476. int numParameters)
  477. {
  478. var returnVal;
  479. NPVariant result;
  480. VOID_TO_NPVARIANT (result);
  481. if (numParameters > 0)
  482. {
  483. NPVariant* const params = (NPVariant*) juce_malloc (sizeof (NPVariant) * numParameters);
  484. int i;
  485. for (i = 0; i < numParameters; ++i)
  486. createNPVariantFromValue (npp, params[i], parameters[i]);
  487. if (browser.invoke (npp, source, getIdentifierFromString (methodName),
  488. params, numParameters, &result))
  489. {
  490. returnVal = createValueFromNPVariant (npp, result);
  491. browser.releasevariantvalue (&result);
  492. }
  493. for (i = 0; i < numParameters; ++i)
  494. browser.releasevariantvalue (&params[i]);
  495. juce_free (params);
  496. }
  497. else
  498. {
  499. if (browser.invoke (npp, source, getIdentifierFromString (methodName), 0, 0, &result))
  500. {
  501. returnVal = createValueFromNPVariant (npp, result);
  502. browser.releasevariantvalue (&result);
  503. }
  504. }
  505. return returnVal;
  506. }
  507. };
  508. //==============================================================================
  509. class NPObjectWrappingDynamicObject : public NPObject
  510. {
  511. public:
  512. static NPObject* create (NPP npp, const var& objectToWrap);
  513. virtual ~NPObjectWrappingDynamicObject()
  514. {
  515. DBG ("num Juce wrapper objs: " + String (--numJuceSO));
  516. }
  517. private:
  518. NPObjectWrappingDynamicObject (NPP npp_)
  519. : npp (npp_)
  520. {
  521. DBG ("num Juce wrapper objs: " + String (++numJuceSO));
  522. }
  523. //==============================================================================
  524. bool construct (const NPVariant *args, uint32_t argCount, NPVariant *result);
  525. void invalidate() {}
  526. bool hasMethod (NPIdentifier name)
  527. {
  528. DynamicObject* const o = object.getObject();
  529. return o != 0 && o->hasMethod (identifierToString (name));
  530. }
  531. bool invoke (NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* out)
  532. {
  533. DynamicObject* const o = object.getObject();
  534. const var::identifier methodName (identifierToString (name));
  535. if (o == 0 || ! o->hasMethod (methodName))
  536. return false;
  537. var* params = (var*) juce_calloc (sizeof (var) * argCount);
  538. for (uint32_t i = 0; i < argCount; ++i)
  539. params[i] = createValueFromNPVariant (npp, args[i]);
  540. const var result (o->invokeMethod (methodName, params, argCount));
  541. for (int i = argCount; --i >= 0;)
  542. params[i] = var();
  543. juce_free (params);
  544. if (out != 0)
  545. createNPVariantFromValue (npp, *out, result);
  546. return true;
  547. }
  548. bool invokeDefault (const NPVariant* args, uint32_t argCount, NPVariant* result)
  549. {
  550. return false;
  551. }
  552. bool hasProperty (NPIdentifier name)
  553. {
  554. DynamicObject* const o = object.getObject();
  555. return o != 0 && o->hasProperty (identifierToString (name));
  556. }
  557. bool getProperty (NPIdentifier name, NPVariant* out)
  558. {
  559. DynamicObject* const o = object.getObject();
  560. const var::identifier propName (identifierToString (name));
  561. if (o == 0 || ! o->hasProperty (propName))
  562. return false;
  563. const var result (o->getProperty (propName));
  564. if (out != 0)
  565. createNPVariantFromValue (npp, *out, result);
  566. return true;
  567. }
  568. bool setProperty (NPIdentifier name, const NPVariant* value)
  569. {
  570. DynamicObject* const o = object.getObject();
  571. if (value == 0 || o == 0)
  572. return false;
  573. o->setProperty (identifierToString (name), createValueFromNPVariant (npp, *value));
  574. return true;
  575. }
  576. bool removeProperty (NPIdentifier name)
  577. {
  578. DynamicObject* const o = object.getObject();
  579. const var::identifier propName (identifierToString (name));
  580. if (o == 0 || ! o->hasProperty (propName))
  581. return false;
  582. o->removeProperty (propName);
  583. return true;
  584. }
  585. bool enumerate (NPIdentifier** identifier, uint32_t* count)
  586. {
  587. return false;
  588. }
  589. //==============================================================================
  590. NPP npp;
  591. var object;
  592. static const var::identifier identifierToString (NPIdentifier id)
  593. {
  594. NPUTF8* const name = browser.utf8fromidentifier (id);
  595. const var::identifier result ((const char*) name);
  596. browser.memfree (name);
  597. return result;
  598. }
  599. public:
  600. //==============================================================================
  601. static NPObject* createInstance (NPP npp, NPClass* aClass) { return new NPObjectWrappingDynamicObject (npp); }
  602. static void class_deallocate (NPObject* npobj) { delete (NPObjectWrappingDynamicObject*) npobj; }
  603. static void class_invalidate (NPObject* npobj) { ((NPObjectWrappingDynamicObject*) npobj)->invalidate(); }
  604. static bool class_hasMethod (NPObject* npobj, NPIdentifier name) { return ((NPObjectWrappingDynamicObject*) npobj)->hasMethod (name); }
  605. static bool class_invoke (NPObject* npobj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) { return ((NPObjectWrappingDynamicObject*) npobj)->invoke (name, args, argCount, result); }
  606. static bool class_invokeDefault (NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) { return ((NPObjectWrappingDynamicObject*) npobj)->invokeDefault (args, argCount, result); }
  607. static bool class_hasProperty (NPObject* npobj, NPIdentifier name) { return ((NPObjectWrappingDynamicObject*) npobj)->hasProperty (name); }
  608. static bool class_getProperty (NPObject* npobj, NPIdentifier name, NPVariant* result) { return ((NPObjectWrappingDynamicObject*) npobj)->getProperty (name, result); }
  609. static bool class_setProperty (NPObject* npobj, NPIdentifier name, const NPVariant* value) { return ((NPObjectWrappingDynamicObject*) npobj)->setProperty (name, value); }
  610. static bool class_removeProperty (NPObject* npobj, NPIdentifier name) { return ((NPObjectWrappingDynamicObject*) npobj)->removeProperty (name); }
  611. static bool class_enumerate (NPObject* npobj, NPIdentifier** identifier, uint32_t* count) { return ((NPObjectWrappingDynamicObject*) npobj)->enumerate (identifier, count); }
  612. static bool class_construct (NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) { return ((NPObjectWrappingDynamicObject*) npobj)->construct (args, argCount, result); }
  613. };
  614. static NPClass sNPObjectWrappingDynamicObject_NPClass =
  615. {
  616. NP_CLASS_STRUCT_VERSION_ENUM, NPObjectWrappingDynamicObject::createInstance,
  617. NPObjectWrappingDynamicObject::class_deallocate, NPObjectWrappingDynamicObject::class_invalidate,
  618. NPObjectWrappingDynamicObject::class_hasMethod, NPObjectWrappingDynamicObject::class_invoke,
  619. NPObjectWrappingDynamicObject::class_invokeDefault, NPObjectWrappingDynamicObject::class_hasProperty,
  620. NPObjectWrappingDynamicObject::class_getProperty, NPObjectWrappingDynamicObject::class_setProperty,
  621. NPObjectWrappingDynamicObject::class_removeProperty, NPObjectWrappingDynamicObject::class_enumerate
  622. };
  623. bool NPObjectWrappingDynamicObject::construct (const NPVariant* args, uint32_t argCount, NPVariant* result)
  624. {
  625. NPObject* const newObj = browser.createobject (npp, &sNPObjectWrappingDynamicObject_NPClass);
  626. if (newObj == 0)
  627. return false;
  628. OBJECT_TO_NPVARIANT (newObj, *result);
  629. return true;
  630. }
  631. NPObject* NPObjectWrappingDynamicObject::create (NPP npp, const var& objectToWrap)
  632. {
  633. jassert (objectToWrap.getObject() != 0);
  634. NPObject* const nppObject = browser.createobject (npp, &sNPObjectWrappingDynamicObject_NPClass);
  635. if (nppObject != 0)
  636. ((NPObjectWrappingDynamicObject*) nppObject)->object = objectToWrap;
  637. return nppObject;
  638. }
  639. //==============================================================================
  640. static const var createValueFromNPVariant (NPP npp, const NPVariant& v)
  641. {
  642. if (NPVARIANT_IS_BOOLEAN (v))
  643. return var (NPVARIANT_TO_BOOLEAN (v));
  644. else if (NPVARIANT_IS_INT32 (v))
  645. return var (NPVARIANT_TO_INT32 (v));
  646. else if (NPVARIANT_IS_DOUBLE (v))
  647. return var (NPVARIANT_TO_DOUBLE (v));
  648. else if (NPVARIANT_IS_STRING (v))
  649. #if JUCE_MAC
  650. return var (String::fromUTF8 ((const juce::uint8*) (NPVARIANT_TO_STRING (v).UTF8Characters),
  651. (int) NPVARIANT_TO_STRING (v).UTF8Length));
  652. #else
  653. return var (String::fromUTF8 ((const juce::uint8*) (NPVARIANT_TO_STRING (v).utf8characters),
  654. (int) NPVARIANT_TO_STRING (v).utf8length));
  655. #endif
  656. else if (NPVARIANT_IS_OBJECT (v))
  657. return var (new DynamicObjectWrappingNPObject (npp, NPVARIANT_TO_OBJECT (v)));
  658. return var();
  659. }
  660. static void createNPVariantFromValue (NPP npp, NPVariant& out, const var& v)
  661. {
  662. if (v.isInt())
  663. INT32_TO_NPVARIANT ((int) v, out);
  664. else if (v.isBool())
  665. BOOLEAN_TO_NPVARIANT ((bool) v, out);
  666. else if (v.isDouble())
  667. DOUBLE_TO_NPVARIANT ((double) v, out);
  668. else if (v.isString())
  669. {
  670. const String s (v.toString());
  671. const char* const utf8 = s.toUTF8();
  672. const int utf8Len = strlen (utf8) + 1;
  673. char* const stringCopy = (char*) browser.memalloc (utf8Len);
  674. memcpy (stringCopy, utf8, utf8Len);
  675. STRINGZ_TO_NPVARIANT (stringCopy, out);
  676. }
  677. else if (v.isObject())
  678. OBJECT_TO_NPVARIANT (NPObjectWrappingDynamicObject::create (npp, v), out);
  679. else
  680. VOID_TO_NPVARIANT (out);
  681. }
  682. //==============================================================================
  683. class JucePluginInstance
  684. {
  685. public:
  686. //==============================================================================
  687. JucePluginInstance (NPP npp_)
  688. : npp (npp_),
  689. holderComp (0),
  690. scriptObject (0)
  691. {
  692. }
  693. ~JucePluginInstance()
  694. {
  695. setWindow (0);
  696. }
  697. bool setWindow (NPWindow* window)
  698. {
  699. if (window != 0)
  700. {
  701. if (holderComp == 0)
  702. holderComp = new BrowserPluginHolderComponent (npp);
  703. holderComp->setWindow (window);
  704. }
  705. else
  706. {
  707. deleteAndZero (holderComp);
  708. scriptObject = 0;
  709. }
  710. return true;
  711. }
  712. NPObject* getScriptableObject()
  713. {
  714. if (scriptObject == 0)
  715. scriptObject = NPObjectWrappingDynamicObject::create (npp, holderComp->getObject());
  716. if (scriptObject != 0 && shouldRetainBrowserObject())
  717. browser.retainobject (scriptObject);
  718. return scriptObject;
  719. }
  720. //==============================================================================
  721. NPP npp;
  722. BrowserPluginHolderComponent* holderComp;
  723. NPObject* scriptObject;
  724. private:
  725. bool shouldRetainBrowserObject() const
  726. {
  727. const String version (browser.uagent (npp));
  728. if (! version.containsIgnoreCase (T(" AppleWebKit/")))
  729. return true;
  730. int versionNum = version.fromFirstOccurrenceOf (T(" AppleWebKit/"), false, true).getIntValue();
  731. return versionNum == 0 || versionNum >= 420;
  732. }
  733. };
  734. //==============================================================================
  735. static NPP currentlyInitialisingNPP = 0;
  736. static int numPluginInstances = 0;
  737. NPError NPP_New (NPMIMEType pluginType, NPP npp, ::uint16 mode, ::int16 argc, char* argn[], char* argv[], NPSavedData* saved)
  738. {
  739. log ("NPP_New");
  740. if (npp == 0)
  741. return NPERR_INVALID_INSTANCE_ERROR;
  742. #if JUCE_MAC
  743. browser.setvalue (npp, (NPPVariable) NPNVpluginDrawingModel, (void*) NPDrawingModelCoreGraphics);
  744. #endif
  745. if (numPluginInstances++ == 0)
  746. {
  747. log ("initialiseJuce_GUI()");
  748. initialiseJuce_GUI();
  749. }
  750. currentlyInitialisingNPP = npp;
  751. JucePluginInstance* p = new JucePluginInstance (npp);
  752. currentlyInitialisingNPP = 0;
  753. npp->pdata = (void*) p;
  754. return NPERR_NO_ERROR;
  755. }
  756. NPError NPP_Destroy (NPP npp, NPSavedData** save)
  757. {
  758. log ("NPP_Destroy");
  759. if (npp == 0)
  760. return NPERR_INVALID_INSTANCE_ERROR;
  761. JucePluginInstance* const p = (JucePluginInstance*) npp->pdata;
  762. if (p != 0)
  763. {
  764. delete p;
  765. if (--numPluginInstances == 0)
  766. {
  767. log ("shutdownJuce_GUI()");
  768. shutdownJuce_GUI();
  769. browserVersionDesc = String::empty;
  770. }
  771. }
  772. return NPERR_NO_ERROR;
  773. }
  774. NPError NPP_SetWindow (NPP npp, NPWindow* pNPWindow)
  775. {
  776. if (npp == 0)
  777. return NPERR_INVALID_INSTANCE_ERROR;
  778. if (pNPWindow == 0)
  779. return NPERR_GENERIC_ERROR;
  780. JucePluginInstance* const p = (JucePluginInstance*) npp->pdata;
  781. if (p == 0)
  782. return NPERR_GENERIC_ERROR;
  783. currentlyInitialisingNPP = npp;
  784. NPError result = p->setWindow (pNPWindow) ? NPERR_NO_ERROR
  785. : NPERR_MODULE_LOAD_FAILED_ERROR;
  786. currentlyInitialisingNPP = 0;
  787. return result;
  788. }
  789. //==============================================================================
  790. NPError NPP_GetValue (NPP npp, NPPVariable variable, void* value)
  791. {
  792. if (npp == 0)
  793. return NPERR_INVALID_INSTANCE_ERROR;
  794. JucePluginInstance* const p = (JucePluginInstance*) npp->pdata;
  795. if (p == 0)
  796. return NPERR_GENERIC_ERROR;
  797. switch (variable)
  798. {
  799. case NPPVpluginNameString:
  800. *((char**) value) = JuceBrowserPlugin_Name;
  801. break;
  802. case NPPVpluginDescriptionString:
  803. *((char**) value) = JuceBrowserPlugin_Desc;
  804. break;
  805. case NPPVpluginScriptableNPObject:
  806. *((NPObject**) value) = p->getScriptableObject();
  807. break;
  808. default:
  809. return NPERR_GENERIC_ERROR;
  810. }
  811. return NPERR_NO_ERROR;
  812. }
  813. NPError NPP_NewStream (NPP npp,
  814. NPMIMEType type,
  815. NPStream* stream,
  816. NPBool seekable,
  817. ::uint16* stype)
  818. {
  819. if (npp == 0)
  820. return NPERR_INVALID_INSTANCE_ERROR;
  821. return NPERR_NO_ERROR;
  822. }
  823. ::int32 NPP_WriteReady (NPP npp, NPStream *stream)
  824. {
  825. if (npp == 0)
  826. return NPERR_INVALID_INSTANCE_ERROR;
  827. return 0x0fffffff;
  828. }
  829. ::int32 NPP_Write (NPP npp, NPStream *stream, ::int32 offset, ::int32 len, void *buffer)
  830. {
  831. if (npp == 0)
  832. return NPERR_INVALID_INSTANCE_ERROR;
  833. return len;
  834. }
  835. NPError NPP_DestroyStream (NPP npp, NPStream *stream, NPError reason)
  836. {
  837. if (npp == 0)
  838. return NPERR_INVALID_INSTANCE_ERROR;
  839. return NPERR_NO_ERROR;
  840. }
  841. void NPP_StreamAsFile (NPP npp, NPStream* stream, const char* fname)
  842. {
  843. if (npp == 0)
  844. return;
  845. }
  846. void NPP_Print (NPP npp, NPPrint* printInfo)
  847. {
  848. if (npp == 0)
  849. return;
  850. }
  851. void NPP_URLNotify (NPP npp, const char* url, NPReason reason, void* notifyData)
  852. {
  853. if (npp == 0)
  854. return;
  855. }
  856. NPError NPP_SetValue (NPP npp, NPNVariable variable, void* value)
  857. {
  858. if (npp == 0)
  859. return NPERR_INVALID_INSTANCE_ERROR;
  860. return NPERR_NO_ERROR;
  861. }
  862. ::int16 NPP_HandleEvent (NPP npp, void* ev)
  863. {
  864. if (npp != 0)
  865. {
  866. //JucePluginInstance* const p = (JucePluginInstance*) npp->pdata;
  867. }
  868. return 0;
  869. }
  870. //==============================================================================
  871. static NPP getInstance (const BrowserPluginComponent* bpc)
  872. {
  873. BrowserPluginHolderComponent* holder = dynamic_cast <BrowserPluginHolderComponent*> (bpc->getParentComponent());
  874. if (holder != 0)
  875. return holder->npp;
  876. return currentlyInitialisingNPP;
  877. }
  878. //==============================================================================
  879. BrowserPluginComponent::BrowserPluginComponent()
  880. {
  881. }
  882. BrowserPluginComponent::~BrowserPluginComponent()
  883. {
  884. }
  885. const String BrowserPluginComponent::getBrowserVersion() const
  886. {
  887. if (browserVersionDesc.isEmpty())
  888. {
  889. if (getInstance (this) != 0)
  890. browserVersionDesc << browser.uagent (getInstance (this));
  891. else
  892. browserVersionDesc << "Netscape Plugin V" << (int) ((browser.version >> 8) & 0xff)
  893. << "." << (int) (browser.version & 0xff);
  894. }
  895. return browserVersionDesc;
  896. }
  897. //==============================================================================
  898. #if JUCE_WIN32
  899. extern const String getActiveXBrowserURL (const BrowserPluginComponent* comp);
  900. #endif
  901. const String BrowserPluginComponent::getBrowserURL() const
  902. {
  903. String result;
  904. #if JUCE_WIN32
  905. result = getActiveXBrowserURL (this);
  906. if (result.isNotEmpty())
  907. return result;
  908. #endif
  909. NPP npp = getInstance (this);
  910. if (npp != 0)
  911. {
  912. NPObject* windowObj = 0;
  913. browser.getvalue (npp, NPNVWindowNPObject, &windowObj);
  914. if (windowObj != 0)
  915. {
  916. NPVariant location;
  917. bool ok = browser.getproperty (npp, windowObj,
  918. browser.getstringidentifier ("location"), &location);
  919. browser.releaseobject (windowObj);
  920. jassert (ok);
  921. if (ok)
  922. {
  923. NPVariant href;
  924. ok = browser.getproperty (npp, location.value.objectValue,
  925. browser.getstringidentifier ("href"), &href);
  926. browser.releasevariantvalue (&location);
  927. jassert (ok);
  928. if (ok)
  929. {
  930. result = URL::removeEscapeChars (createValueFromNPVariant (npp, href).toString());
  931. browser.releasevariantvalue (&href);
  932. }
  933. }
  934. }
  935. }
  936. return result;
  937. }