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.

1246 lines
42KB

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