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.

1242 lines
42KB

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