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.

1186 lines
38KB

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