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.

1238 lines
42KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software 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 var::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. public:
  494. DynamicObjectWrappingNPObject (NPP npp_, NPObject* const source_)
  495. : npp (npp_),
  496. source (browser.retainobject (source_))
  497. {
  498. DBG ("num NP wrapper objs: " + String (++numDOWNP));
  499. }
  500. ~DynamicObjectWrappingNPObject()
  501. {
  502. browser.releaseobject (source);
  503. DBG ("num NP wrapper objs: " + String (--numDOWNP));
  504. }
  505. var getProperty (const var::identifier& propertyName) const override
  506. {
  507. NPVariant result;
  508. VOID_TO_NPVARIANT (result);
  509. browser.getproperty (npp, source, getIdentifierFromString (propertyName), &result);
  510. const var v (createValueFromNPVariant (npp, result));
  511. browser.releasevariantvalue (&result);
  512. return v;
  513. }
  514. bool hasProperty (const var::identifier& propertyName) const override
  515. {
  516. NPVariant result;
  517. VOID_TO_NPVARIANT (result);
  518. const bool hasProp = browser.getproperty (npp, source, getIdentifierFromString (propertyName), &result);
  519. browser.releasevariantvalue (&result);
  520. return hasProp;
  521. }
  522. void setProperty (const var::identifier& propertyName, const var& newValue) override
  523. {
  524. NPVariant value;
  525. createNPVariantFromValue (npp, value, newValue);
  526. browser.setproperty (npp, source, getIdentifierFromString (propertyName), &value);
  527. browser.releasevariantvalue (&value);
  528. }
  529. void removeProperty (const var::identifier& propertyName) override
  530. {
  531. browser.removeproperty (npp, source, getIdentifierFromString (propertyName));
  532. }
  533. bool hasMethod (const var::identifier& methodName) const override
  534. {
  535. return browser.hasmethod (npp, source, getIdentifierFromString (methodName));
  536. }
  537. var invokeMethod (Identifier methodName, const var::NativeFunctionArgs& args) override
  538. {
  539. var returnVal;
  540. NPVariant result;
  541. VOID_TO_NPVARIANT (result);
  542. if (args.numArguments > 0)
  543. {
  544. HeapBlock<NPVariant> params (args.numArguments);
  545. for (int i = 0; i < args.numArguments; ++i)
  546. createNPVariantFromValue (npp, params[i], args.arguments[i]);
  547. if (browser.invoke (npp, source, getIdentifierFromString (methodName),
  548. params, args.numArguments, &result))
  549. {
  550. returnVal = createValueFromNPVariant (npp, result);
  551. browser.releasevariantvalue (&result);
  552. }
  553. for (int i = 0; i < args.numArguments; ++i)
  554. browser.releasevariantvalue (&params[i]);
  555. }
  556. else
  557. {
  558. if (browser.invoke (npp, source, getIdentifierFromString (methodName), 0, 0, &result))
  559. {
  560. returnVal = createValueFromNPVariant (npp, result);
  561. browser.releasevariantvalue (&result);
  562. }
  563. }
  564. return returnVal;
  565. }
  566. };
  567. //==============================================================================
  568. class NPObjectWrappingDynamicObject : public NPObject
  569. {
  570. public:
  571. static NPObject* create (NPP npp, const var& objectToWrap);
  572. virtual ~NPObjectWrappingDynamicObject()
  573. {
  574. DBG ("num Juce wrapper objs: " + String (--numJuceWDO));
  575. }
  576. private:
  577. NPObjectWrappingDynamicObject (NPP n) : npp (n)
  578. {
  579. DBG ("num Juce wrapper objs: " + String (++numJuceWDO));
  580. }
  581. //==============================================================================
  582. bool construct (const NPVariant *args, uint32_t argCount, NPVariant *result);
  583. void invalidate() {}
  584. bool hasMethod (NPIdentifier name)
  585. {
  586. DynamicObject* const o = object.getDynamicObject();
  587. return o != nullptr && o->hasMethod (identifierToString (name));
  588. }
  589. bool invoke (NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* out)
  590. {
  591. DynamicObject* const o = object.getDynamicObject();
  592. const var::identifier methodName (identifierToString (name));
  593. if (o == nullptr || ! o->hasMethod (methodName))
  594. return false;
  595. struct ParamHolder
  596. {
  597. ParamHolder (uint32_t num) { params = new var [num]; }
  598. ~ParamHolder() { delete[] params; }
  599. var* params;
  600. };
  601. ParamHolder params (argCount);
  602. for (uint32_t i = 0; i < argCount; ++i)
  603. params.params[i] = createValueFromNPVariant (npp, args[i]);
  604. const var result (o->invokeMethod (methodName, var::NativeFunctionArgs (object, params.params, (int) argCount)));
  605. if (out != nullptr)
  606. createNPVariantFromValue (npp, *out, result);
  607. return true;
  608. }
  609. bool invokeDefault (const NPVariant* args, uint32_t argCount, NPVariant* result)
  610. {
  611. return false;
  612. }
  613. bool hasProperty (NPIdentifier name)
  614. {
  615. DynamicObject* const o = object.getDynamicObject();
  616. return o != nullptr && o->hasProperty (identifierToString (name));
  617. }
  618. bool getProperty (NPIdentifier name, NPVariant* out)
  619. {
  620. DynamicObject* const o = object.getDynamicObject();
  621. const var::identifier propName (identifierToString (name));
  622. if (o == nullptr || ! o->hasProperty (propName))
  623. return false;
  624. const var result (o->getProperty (propName));
  625. if (out != nullptr)
  626. createNPVariantFromValue (npp, *out, result);
  627. return true;
  628. }
  629. bool setProperty (NPIdentifier name, const NPVariant* value)
  630. {
  631. DynamicObject* const o = object.getDynamicObject();
  632. if (value == nullptr || o == nullptr)
  633. return false;
  634. o->setProperty (identifierToString (name), createValueFromNPVariant (npp, *value));
  635. return true;
  636. }
  637. bool removeProperty (NPIdentifier name)
  638. {
  639. DynamicObject* const o = object.getDynamicObject();
  640. const var::identifier propName (identifierToString (name));
  641. if (o == nullptr || ! o->hasProperty (propName))
  642. return false;
  643. o->removeProperty (propName);
  644. return true;
  645. }
  646. bool enumerate (NPIdentifier** identifier, uint32_t* count)
  647. {
  648. return false;
  649. }
  650. //==============================================================================
  651. NPP npp;
  652. var object;
  653. static var::identifier identifierToString (NPIdentifier id)
  654. {
  655. NPUTF8* const name = browser.utf8fromidentifier (id);
  656. const var::identifier result ((const char*) name);
  657. browser.memfree (name);
  658. return result;
  659. }
  660. public:
  661. //==============================================================================
  662. static NPObject* createInstance (NPP npp, NPClass* aClass) { return new NPObjectWrappingDynamicObject (npp); }
  663. static void class_deallocate (NPObject* npobj) { delete (NPObjectWrappingDynamicObject*) npobj; }
  664. static void class_invalidate (NPObject* npobj) { ((NPObjectWrappingDynamicObject*) npobj)->invalidate(); }
  665. static bool class_hasMethod (NPObject* npobj, NPIdentifier name) { return ((NPObjectWrappingDynamicObject*) npobj)->hasMethod (name); }
  666. static bool class_invoke (NPObject* npobj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) { return ((NPObjectWrappingDynamicObject*) npobj)->invoke (name, args, argCount, result); }
  667. static bool class_invokeDefault (NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) { return ((NPObjectWrappingDynamicObject*) npobj)->invokeDefault (args, argCount, result); }
  668. static bool class_hasProperty (NPObject* npobj, NPIdentifier name) { return ((NPObjectWrappingDynamicObject*) npobj)->hasProperty (name); }
  669. static bool class_getProperty (NPObject* npobj, NPIdentifier name, NPVariant* result) { return ((NPObjectWrappingDynamicObject*) npobj)->getProperty (name, result); }
  670. static bool class_setProperty (NPObject* npobj, NPIdentifier name, const NPVariant* value) { return ((NPObjectWrappingDynamicObject*) npobj)->setProperty (name, value); }
  671. static bool class_removeProperty (NPObject* npobj, NPIdentifier name) { return ((NPObjectWrappingDynamicObject*) npobj)->removeProperty (name); }
  672. static bool class_enumerate (NPObject* npobj, NPIdentifier** identifier, uint32_t* count) { return ((NPObjectWrappingDynamicObject*) npobj)->enumerate (identifier, count); }
  673. static bool class_construct (NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) { return ((NPObjectWrappingDynamicObject*) npobj)->construct (args, argCount, result); }
  674. };
  675. static NPClass sNPObjectWrappingDynamicObject_NPClass =
  676. {
  677. #ifndef NP_CLASS_STRUCT_VERSION_ENUM
  678. NP_CLASS_STRUCT_VERSION, NPObjectWrappingDynamicObject::createInstance,
  679. NPObjectWrappingDynamicObject::class_deallocate, NPObjectWrappingDynamicObject::class_invalidate,
  680. NPObjectWrappingDynamicObject::class_hasMethod, NPObjectWrappingDynamicObject::class_invoke,
  681. NPObjectWrappingDynamicObject::class_invokeDefault, NPObjectWrappingDynamicObject::class_hasProperty,
  682. NPObjectWrappingDynamicObject::class_getProperty, NPObjectWrappingDynamicObject::class_setProperty,
  683. NPObjectWrappingDynamicObject::class_removeProperty
  684. #else
  685. NP_CLASS_STRUCT_VERSION_ENUM, NPObjectWrappingDynamicObject::createInstance,
  686. NPObjectWrappingDynamicObject::class_deallocate, NPObjectWrappingDynamicObject::class_invalidate,
  687. NPObjectWrappingDynamicObject::class_hasMethod, NPObjectWrappingDynamicObject::class_invoke,
  688. NPObjectWrappingDynamicObject::class_invokeDefault, NPObjectWrappingDynamicObject::class_hasProperty,
  689. NPObjectWrappingDynamicObject::class_getProperty, NPObjectWrappingDynamicObject::class_setProperty,
  690. NPObjectWrappingDynamicObject::class_removeProperty, NPObjectWrappingDynamicObject::class_enumerate
  691. #endif
  692. };
  693. bool NPObjectWrappingDynamicObject::construct (const NPVariant* args, uint32_t argCount, NPVariant* result)
  694. {
  695. NPObject* const newObj = browser.createobject (npp, &sNPObjectWrappingDynamicObject_NPClass);
  696. if (newObj == nullptr)
  697. return false;
  698. OBJECT_TO_NPVARIANT (newObj, *result);
  699. return true;
  700. }
  701. NPObject* NPObjectWrappingDynamicObject::create (NPP npp, const var& objectToWrap)
  702. {
  703. jassert (objectToWrap.getDynamicObject() != nullptr);
  704. NPObject* const nppObject = browser.createobject (npp, &sNPObjectWrappingDynamicObject_NPClass);
  705. if (nppObject != nullptr)
  706. ((NPObjectWrappingDynamicObject*) nppObject)->object = objectToWrap;
  707. return nppObject;
  708. }
  709. //==============================================================================
  710. static var createValueFromNPVariant (NPP npp, const NPVariant& v)
  711. {
  712. if (NPVARIANT_IS_BOOLEAN (v))
  713. return var (NPVARIANT_TO_BOOLEAN (v));
  714. else if (NPVARIANT_IS_INT32 (v))
  715. return var (NPVARIANT_TO_INT32 (v));
  716. else if (NPVARIANT_IS_DOUBLE (v))
  717. return var (NPVARIANT_TO_DOUBLE (v));
  718. else if (NPVARIANT_IS_STRING (v))
  719. {
  720. return var (String::fromUTF8 ((const char*)
  721. #if JUCE_MAC
  722. (NPVARIANT_TO_STRING (v).UTF8Characters), (int) NPVARIANT_TO_STRING (v).UTF8Length));
  723. #else
  724. (NPVARIANT_TO_STRING (v).utf8characters), (int) NPVARIANT_TO_STRING (v).utf8length));
  725. #endif
  726. }
  727. else if (NPVARIANT_IS_OBJECT (v) && npp != nullptr)
  728. return var (new DynamicObjectWrappingNPObject (npp, NPVARIANT_TO_OBJECT (v)));
  729. return var();
  730. }
  731. static void createNPVariantFromValue (NPP npp, NPVariant& out, const var& v)
  732. {
  733. if (v.isInt())
  734. INT32_TO_NPVARIANT ((int) v, out);
  735. else if (v.isBool())
  736. BOOLEAN_TO_NPVARIANT ((bool) v, out);
  737. else if (v.isDouble())
  738. DOUBLE_TO_NPVARIANT ((double) v, out);
  739. else if (v.isString())
  740. {
  741. const String s (v.toString());
  742. const char* const utf8 = s.toUTF8();
  743. const int utf8Len = strlen (utf8) + 1;
  744. char* const stringCopy = (char*) browser.memalloc (utf8Len);
  745. memcpy (stringCopy, utf8, utf8Len);
  746. STRINGZ_TO_NPVARIANT (stringCopy, out);
  747. }
  748. else if (v.getDynamicObject() != nullptr && npp != nullptr)
  749. OBJECT_TO_NPVARIANT (NPObjectWrappingDynamicObject::create (npp, v), out);
  750. else
  751. VOID_TO_NPVARIANT (out);
  752. }
  753. //==============================================================================
  754. class JucePluginInstance
  755. {
  756. public:
  757. //==============================================================================
  758. JucePluginInstance (NPP npp_)
  759. : npp (npp_),
  760. scriptObject (nullptr)
  761. {
  762. }
  763. bool setWindow (NPWindow* window)
  764. {
  765. if (window != nullptr)
  766. {
  767. if (holderComp == nullptr)
  768. holderComp = new BrowserPluginHolderComponent (npp);
  769. holderComp->setWindow (window);
  770. }
  771. else
  772. {
  773. holderComp = nullptr;
  774. scriptObject = nullptr;
  775. }
  776. return true;
  777. }
  778. NPObject* getScriptableObject()
  779. {
  780. if (scriptObject == nullptr)
  781. scriptObject = NPObjectWrappingDynamicObject::create (npp, holderComp->getObject());
  782. if (scriptObject != nullptr && shouldRetainBrowserObject())
  783. browser.retainobject (scriptObject);
  784. return scriptObject;
  785. }
  786. //==============================================================================
  787. NPP npp;
  788. ScopedPointer<BrowserPluginHolderComponent> holderComp;
  789. NPObject* scriptObject;
  790. private:
  791. bool shouldRetainBrowserObject() const
  792. {
  793. const String version (browser.uagent (npp));
  794. if (! version.containsIgnoreCase (" AppleWebKit/"))
  795. return true;
  796. int versionNum = version.fromFirstOccurrenceOf (" AppleWebKit/", false, true).getIntValue();
  797. return versionNum == 0 || versionNum >= 420;
  798. }
  799. };
  800. //==============================================================================
  801. static NPP currentlyInitialisingNPP = nullptr;
  802. static int numPluginInstances = 0;
  803. NPError NPP_New (NPMIMEType pluginType, NPP npp, ::uint16 mode, ::int16 argc, char* argn[], char* argv[], NPSavedData* saved)
  804. {
  805. log ("NPP_New");
  806. if (npp == nullptr)
  807. return NPERR_INVALID_INSTANCE_ERROR;
  808. #if JUCE_MAC
  809. browser.setvalue (npp, (NPPVariable) NPNVpluginDrawingModel, (void*) NPDrawingModelCoreGraphics);
  810. #if JUCE_USE_NPAPI_CARBON_UI
  811. browser.setvalue (npp, (NPPVariable) 1001 /*NPPVpluginEventModel*/, 0 /*NPEventModelCarbon*/);
  812. #else
  813. browser.setvalue (npp, (NPPVariable) 1001 /*NPPVpluginEventModel*/, (void*) 1 /*NPEventModelCocoa*/);
  814. #endif
  815. #endif
  816. if (numPluginInstances++ == 0)
  817. {
  818. log ("initialiseJuce_GUI()");
  819. initialiseJuce_GUI();
  820. }
  821. currentlyInitialisingNPP = npp;
  822. JucePluginInstance* p = new JucePluginInstance (npp);
  823. currentlyInitialisingNPP = nullptr;
  824. npp->pdata = (void*) p;
  825. return NPERR_NO_ERROR;
  826. }
  827. NPError NPP_Destroy (NPP npp, NPSavedData** save)
  828. {
  829. log ("NPP_Destroy");
  830. if (npp == nullptr)
  831. return NPERR_INVALID_INSTANCE_ERROR;
  832. JucePluginInstance* const p = (JucePluginInstance*) npp->pdata;
  833. if (p != nullptr)
  834. {
  835. delete p;
  836. if (--numPluginInstances == 0)
  837. {
  838. log ("shutdownJuce_GUI()");
  839. shutdownJuce_GUI();
  840. browserVersionDesc.clear();
  841. }
  842. }
  843. return NPERR_NO_ERROR;
  844. }
  845. NPError NPP_SetWindow (NPP npp, NPWindow* pNPWindow)
  846. {
  847. if (npp == nullptr)
  848. return NPERR_INVALID_INSTANCE_ERROR;
  849. if (pNPWindow == nullptr)
  850. return NPERR_GENERIC_ERROR;
  851. JucePluginInstance* const p = (JucePluginInstance*) npp->pdata;
  852. if (p == nullptr)
  853. return NPERR_GENERIC_ERROR;
  854. currentlyInitialisingNPP = npp;
  855. NPError result = p->setWindow (pNPWindow) ? NPERR_NO_ERROR
  856. : NPERR_MODULE_LOAD_FAILED_ERROR;
  857. currentlyInitialisingNPP = nullptr;
  858. return result;
  859. }
  860. //==============================================================================
  861. NPError NPP_GetValue (NPP npp, NPPVariable variable, void* value)
  862. {
  863. if (npp == nullptr)
  864. return NPERR_INVALID_INSTANCE_ERROR;
  865. JucePluginInstance* const p = (JucePluginInstance*) npp->pdata;
  866. if (p == nullptr)
  867. return NPERR_GENERIC_ERROR;
  868. switch (variable)
  869. {
  870. case NPPVpluginNameString: *((const char**) value) = JuceBrowserPlugin_Name; break;
  871. case NPPVpluginDescriptionString: *((const char**) value) = JuceBrowserPlugin_Desc; break;
  872. case NPPVpluginScriptableNPObject: *((NPObject**) value) = p->getScriptableObject(); break;
  873. default: return NPERR_GENERIC_ERROR;
  874. }
  875. return NPERR_NO_ERROR;
  876. }
  877. NPError NPP_NewStream (NPP npp, NPMIMEType type, NPStream* stream, NPBool seekable, ::uint16* stype)
  878. {
  879. if (npp == nullptr)
  880. return NPERR_INVALID_INSTANCE_ERROR;
  881. return NPERR_NO_ERROR;
  882. }
  883. ::int32 NPP_WriteReady (NPP npp, NPStream *stream)
  884. {
  885. if (npp == nullptr)
  886. return NPERR_INVALID_INSTANCE_ERROR;
  887. return 0x0fffffff;
  888. }
  889. ::int32 NPP_Write (NPP npp, NPStream *stream, ::int32 offset, ::int32 len, void *buffer)
  890. {
  891. if (npp == nullptr)
  892. return NPERR_INVALID_INSTANCE_ERROR;
  893. return len;
  894. }
  895. NPError NPP_DestroyStream (NPP npp, NPStream *stream, NPError reason)
  896. {
  897. if (npp == nullptr)
  898. return NPERR_INVALID_INSTANCE_ERROR;
  899. return NPERR_NO_ERROR;
  900. }
  901. void NPP_StreamAsFile (NPP npp, NPStream* stream, const char* fname)
  902. {
  903. if (npp == nullptr)
  904. return;
  905. }
  906. void NPP_Print (NPP npp, NPPrint* printInfo)
  907. {
  908. if (npp == nullptr)
  909. return;
  910. }
  911. void NPP_URLNotify (NPP npp, const char* url, NPReason reason, void* notifyData)
  912. {
  913. if (npp == nullptr)
  914. return;
  915. }
  916. NPError NPP_SetValue (NPP npp, NPNVariable variable, void* value)
  917. {
  918. if (npp == nullptr)
  919. return NPERR_INVALID_INSTANCE_ERROR;
  920. return NPERR_NO_ERROR;
  921. }
  922. ::int16 NPP_HandleEvent (NPP npp, void* ev)
  923. {
  924. if (npp != nullptr)
  925. {
  926. //JucePluginInstance* const p = (JucePluginInstance*) npp->pdata;
  927. }
  928. return 0;
  929. }
  930. //==============================================================================
  931. static NPP getInstance (const BrowserPluginComponent* bpc)
  932. {
  933. BrowserPluginHolderComponent* holder = dynamic_cast <BrowserPluginHolderComponent*> (bpc->getParentComponent());
  934. if (holder != nullptr)
  935. return holder->npp;
  936. return currentlyInitialisingNPP;
  937. }
  938. //==============================================================================
  939. BrowserPluginComponent::BrowserPluginComponent()
  940. {
  941. }
  942. BrowserPluginComponent::~BrowserPluginComponent()
  943. {
  944. }
  945. String BrowserPluginComponent::getBrowserVersion() const
  946. {
  947. if (browserVersionDesc.isEmpty())
  948. {
  949. if (getInstance (this) != nullptr)
  950. browserVersionDesc << browser.uagent (getInstance (this));
  951. else
  952. browserVersionDesc << "Netscape Plugin V" << (int) ((browser.version >> 8) & 0xff)
  953. << "." << (int) (browser.version & 0xff);
  954. }
  955. return browserVersionDesc;
  956. }
  957. //==============================================================================
  958. #if JUCE_WINDOWS
  959. extern String getActiveXBrowserURL (const BrowserPluginComponent* comp);
  960. #endif
  961. String BrowserPluginComponent::getBrowserURL() const
  962. {
  963. String result;
  964. #if JUCE_WINDOWS
  965. result = getActiveXBrowserURL (this);
  966. if (result.isNotEmpty())
  967. return result;
  968. #endif
  969. // (FireFox doesn't seem happy if you call this from a background thread..)
  970. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  971. NPP npp = getInstance (this);
  972. if (npp != nullptr)
  973. {
  974. NPObject* windowObj = nullptr;
  975. browser.getvalue (npp, NPNVWindowNPObject, &windowObj);
  976. if (windowObj != nullptr)
  977. {
  978. NPVariant location;
  979. bool ok = browser.getproperty (npp, windowObj,
  980. browser.getstringidentifier ("location"), &location);
  981. browser.releaseobject (windowObj);
  982. jassert (ok);
  983. if (ok)
  984. {
  985. NPVariant href;
  986. ok = browser.getproperty (npp, location.value.objectValue,
  987. browser.getstringidentifier ("href"), &href);
  988. browser.releasevariantvalue (&location);
  989. jassert (ok);
  990. if (ok)
  991. {
  992. result = URL::removeEscapeChars (createValueFromNPVariant (npp, href).toString());
  993. browser.releasevariantvalue (&href);
  994. }
  995. }
  996. }
  997. }
  998. return result;
  999. }
  1000. #undef log