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
41KB

  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 "On the Mac, you can't compile this .cpp file directly - use juce_NPAPI_GlueCode.mm instead"
  26. #endif
  27. #define XPCOM_GLUE
  28. //==============================================================================
  29. #if _MSC_VER
  30. #define XP_WIN
  31. #define _X86_
  32. #include <windows.h>
  33. #include <windowsx.h>
  34. #include "npapi/npupp.h"
  35. // Cunning trick used to add functions to export list and avoid messing about with .def files.
  36. // (can't add a declspec because the functions have already been pre-declared in the npapi headers).
  37. #define EXPORTED_FUNCTION comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)
  38. //==============================================================================
  39. #elif defined (__APPLE__)
  40. #define XP_MACOSX
  41. #define OSCALL
  42. #include <WebKit/npapi.h>
  43. #include <WebKit/npfunctions.h>
  44. #include <WebKit/npruntime.h>
  45. //==============================================================================
  46. #else
  47. #define XP_UNIX
  48. #include "npapi.h"
  49. #include "npupp.h"
  50. #include "npruntime.h"
  51. #endif
  52. //==============================================================================
  53. #include "juce_IncludeBrowserPluginInfo.h"
  54. #include "../../../juce_amalgamated.h"
  55. #include "juce_BrowserPluginComponent.h"
  56. #if JUCE_MAC && JUCE_DEBUG && 0
  57. #include <fstream>
  58. static void log (const String& s)
  59. {
  60. std::ofstream file ("/Users/jules/Desktop/log.txt", std::ios::out | std::ios::app);
  61. file << s << std::endl;
  62. }
  63. #else
  64. #define log(a)
  65. #endif
  66. //==============================================================================
  67. #if JUCE_MAC
  68. static const String nsStringToJuce (NSString* s) { return String::fromUTF8 ([s UTF8String]); }
  69. static NSString* juceStringToNS (const String& s) { return [NSString stringWithUTF8String: s.toUTF8()]; }
  70. #pragma export on
  71. extern "C"
  72. {
  73. NPError NP_Initialize (NPNetscapeFuncs*);
  74. NPError NP_GetEntryPoints (NPPluginFuncs*);
  75. NPError NP_Shutdown();
  76. }
  77. #pragma export off
  78. #ifndef NP_CLASS_STRUCT_VERSION_ENUM // fill in some symbols that are missing from the OSX 10.4 SDK
  79. #define NPNVpluginDrawingModel 1000
  80. #define NPDrawingModelCoreGraphics 1
  81. typedef struct NP_CGContext
  82. {
  83. CGContextRef context;
  84. WindowRef window;
  85. } NP_CGContext;
  86. #endif
  87. #endif
  88. //==============================================================================
  89. static NPNetscapeFuncs browser;
  90. String browserVersionDesc;
  91. //==============================================================================
  92. NPError NP_GetValue (void* future, NPPVariable variable, void* value)
  93. {
  94. return NPP_GetValue ((NPP_t*) future, variable, value);
  95. }
  96. #if JUCE_WINDOWS || JUCE_MAC
  97. NPError OSCALL NP_GetEntryPoints (NPPluginFuncs* funcs)
  98. {
  99. #if JUCE_WINDOWS
  100. #pragma EXPORTED_FUNCTION
  101. #endif
  102. log ("NP_GetEntryPoints");
  103. if (funcs == 0 || (funcs->size > 0 && funcs->size < sizeof (NPPluginFuncs)))
  104. return NPERR_INVALID_FUNCTABLE_ERROR;
  105. funcs->size = sizeof (NPPluginFuncs);
  106. funcs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
  107. funcs->newp = NPP_New;
  108. funcs->destroy = NPP_Destroy;
  109. funcs->setwindow = NPP_SetWindow;
  110. funcs->newstream = NPP_NewStream;
  111. funcs->destroystream = NPP_DestroyStream;
  112. funcs->asfile = NPP_StreamAsFile;
  113. funcs->writeready = NPP_WriteReady;
  114. #if JUCE_MAC
  115. funcs->write = (NPP_WriteProcPtr) NPP_Write;
  116. #else
  117. funcs->write = NPP_Write;
  118. #endif
  119. funcs->print = NPP_Print;
  120. funcs->event = NPP_HandleEvent;
  121. funcs->urlnotify = NPP_URLNotify;
  122. funcs->getvalue = NPP_GetValue;
  123. funcs->setvalue = NPP_SetValue;
  124. funcs->javaClass = nullptr;
  125. return NPERR_NO_ERROR;
  126. }
  127. #endif
  128. NPError OSCALL NP_Initialize (NPNetscapeFuncs* funcs
  129. #ifdef XP_UNIX
  130. , NPPluginFuncs* pluginFuncs
  131. #endif
  132. )
  133. {
  134. #if JUCE_WINDOWS
  135. #pragma EXPORTED_FUNCTION
  136. #endif
  137. log ("NP_Initialize");
  138. if (funcs == 0)
  139. return NPERR_INVALID_FUNCTABLE_ERROR;
  140. if (((funcs->version >> 8) & 0xff) > NP_VERSION_MAJOR)
  141. return NPERR_INCOMPATIBLE_VERSION_ERROR;
  142. zerostruct (browser);
  143. memcpy (&browser, funcs, jmin ((size_t) funcs->size, sizeof (browser)));
  144. #ifdef XP_UNIX
  145. pluginFuncs->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
  146. pluginFuncs->size = sizeof (NPPluginFuncs);
  147. pluginFuncs->newp = NewNPP_NewProc (NPP_New);
  148. pluginFuncs->destroy = NewNPP_DestroyProc (NPP_Destroy);
  149. pluginFuncs->setwindow = NewNPP_SetWindowProc (NPP_SetWindow);
  150. pluginFuncs->newstream = NewNPP_NewStreamProc (NPP_NewStream);
  151. pluginFuncs->destroystream = NewNPP_DestroyStreamProc (NPP_DestroyStream);
  152. pluginFuncs->asfile = NewNPP_StreamAsFileProc (NPP_StreamAsFile);
  153. pluginFuncs->writeready = NewNPP_WriteReadyProc (NPP_WriteReady);
  154. pluginFuncs->write = NewNPP_WriteProc (NPP_Write);
  155. pluginFuncs->print = NewNPP_PrintProc (NPP_Print);
  156. pluginFuncs->urlnotify = NewNPP_URLNotifyProc (NPP_URLNotify);
  157. pluginFuncs->event = 0;
  158. pluginFuncs->getvalue = NewNPP_GetValueProc (NPP_GetValue);
  159. #ifdef OJI
  160. pluginFuncs->javaClass = NPP_GetJavaClass();
  161. #endif
  162. #endif
  163. return NPERR_NO_ERROR;
  164. }
  165. NPError OSCALL NP_Shutdown()
  166. {
  167. #if JUCE_WINDOWS
  168. #pragma EXPORTED_FUNCTION
  169. #endif
  170. log ("NP_Shutdown");
  171. return NPERR_NO_ERROR;
  172. }
  173. char* NP_GetMIMEDescription()
  174. {
  175. log ("NP_GetMIMEDescription");
  176. static String mimeDesc;
  177. mimeDesc = String (T(JuceBrowserPlugin_MimeType))
  178. + ":" + String (T(JuceBrowserPlugin_FileSuffix))
  179. + ":" + String (T(JuceBrowserPlugin_Name));
  180. return (char*) (const char*) mimeDesc.toUTF8();
  181. }
  182. //==============================================================================
  183. /*
  184. NPError NPN_GetURLNotify (NPP instance, const char *url, const char *target, void* notifyData)
  185. {
  186. return (browser.version & 0xFF) >= NPVERS_HAS_NOTIFICATION
  187. ? browser.geturlnotify (instance, url, target, notifyData);
  188. : NPERR_INCOMPATIBLE_VERSION_ERROR;
  189. }
  190. NPError NPN_PostURLNotify (NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file, void* notifyData)
  191. {
  192. return (browser.version & 0xFF) >= NPVERS_HAS_NOTIFICATION
  193. ? browser.posturlnotify (instance, url, window, len, buf, file, notifyData)
  194. : NPERR_INCOMPATIBLE_VERSION_ERROR;
  195. }
  196. NPError NPN_NewStream (NPP instance, NPMIMEType type, const char* target, NPStream** stream)
  197. {
  198. return (browser.version & 0xFF) >= NPVERS_HAS_STREAMOUTPUT
  199. ? browser.newstream (instance, type, target, stream)
  200. : NPERR_INCOMPATIBLE_VERSION_ERROR;
  201. }
  202. int32 NPN_Write (NPP instance, NPStream *stream, int32 len, void *buffer)
  203. {
  204. return (browser.version & 0xFF) >= NPVERS_HAS_STREAMOUTPUT
  205. ? browser.write (instance, stream, len, buffer)
  206. : -1;
  207. }
  208. NPError NPN_DestroyStream (NPP instance, NPStream* stream, NPError reason)
  209. {
  210. return (browser.version & 0xFF) >= NPVERS_HAS_STREAMOUTPUT
  211. ? browser.destroystream (instance, stream, reason)
  212. : NPERR_INCOMPATIBLE_VERSION_ERROR;
  213. }
  214. */
  215. //==============================================================================
  216. class BrowserPluginHolderComponent : public Component
  217. {
  218. public:
  219. //==============================================================================
  220. BrowserPluginHolderComponent (NPP npp_)
  221. : npp (npp_),
  222. isFirefox4 (false)
  223. {
  224. log ("BrowserPluginHolderComponent created");
  225. #if JUCE_WINDOWS
  226. parentHWND = 0;
  227. oldWinProc = 0;
  228. #else
  229. currentParentView = nil;
  230. #endif
  231. setOpaque (true);
  232. setWantsKeyboardFocus (false);
  233. addAndMakeVisible (child = createBrowserPlugin());
  234. jassert (child != nullptr); // You have to create one of these!
  235. #if JUCE_MAC
  236. const String browserVersion (child->getBrowserVersion());
  237. isFirefox4 = browserVersion.containsIgnoreCase("firefox/")
  238. && browserVersion.fromFirstOccurrenceOf ("firefox/", false, true).getIntValue() >= 4;
  239. #endif
  240. }
  241. ~BrowserPluginHolderComponent()
  242. {
  243. log ("BrowserPluginHolderComponent deleted");
  244. setWindow (nullptr);
  245. child = nullptr;
  246. }
  247. //==============================================================================
  248. void paint (Graphics& g)
  249. {
  250. if (child == nullptr || ! child->isOpaque())
  251. g.fillAll (Colours::white);
  252. }
  253. void resized()
  254. {
  255. if (child != nullptr && ! isFirefox4)
  256. child->setBounds (getLocalBounds());
  257. }
  258. const var getObject()
  259. {
  260. return child->getJavascriptObject();
  261. }
  262. //==============================================================================
  263. NPP npp;
  264. ScopedPointer<BrowserPluginComponent> child;
  265. private:
  266. bool isFirefox4;
  267. //==============================================================================
  268. #if JUCE_WINDOWS
  269. HWND parentHWND;
  270. WNDPROC oldWinProc;
  271. void resizeToParentWindow (const int requestedWidth = 0, const int requestedHeight = 0)
  272. {
  273. if (IsWindow (parentHWND))
  274. {
  275. RECT r;
  276. GetWindowRect (parentHWND, &r);
  277. int w = r.right - r.left;
  278. int h = r.bottom - r.top;
  279. if (w == 0 || h == 0)
  280. {
  281. w = requestedWidth; // On Safari, the HWND can have a zero-size, so we might need to
  282. h = requestedHeight; // force it to the size that the NPAPI call asked for..
  283. MoveWindow (parentHWND, r.left, r.top, w, h, TRUE);
  284. }
  285. setBounds (0, 0, w, h);
  286. }
  287. }
  288. static LRESULT CALLBACK interceptingWinProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  289. {
  290. switch (msg)
  291. {
  292. case WM_PAINT:
  293. {
  294. PAINTSTRUCT ps;
  295. HDC hdc = BeginPaint (hWnd, &ps);
  296. EndPaint (hWnd, &ps);
  297. }
  298. return 0;
  299. case WM_ERASEBKGND:
  300. return 1;
  301. case WM_WINDOWPOSCHANGING:
  302. case WM_WINDOWPOSCHANGED:
  303. //if ((((WINDOWPOS*) lParam)->flags & SWP_NOSIZE) == 0)
  304. {
  305. BrowserPluginHolderComponent* const comp = (BrowserPluginHolderComponent*) GetWindowLongPtr (hWnd, GWLP_USERDATA);
  306. comp->resizeToParentWindow();
  307. }
  308. break;
  309. default:
  310. break;
  311. }
  312. return DefWindowProc (hWnd, msg, wParam, lParam);
  313. }
  314. public:
  315. void setWindow (NPWindow* window)
  316. {
  317. HWND newHWND = (window != nullptr ? ((HWND) window->window) : 0);
  318. if (parentHWND != newHWND)
  319. {
  320. removeFromDesktop();
  321. setVisible (false);
  322. if (IsWindow (parentHWND))
  323. {
  324. SubclassWindow (parentHWND, oldWinProc); // restore the old winproc..
  325. oldWinProc = 0;
  326. }
  327. parentHWND = newHWND;
  328. if (parentHWND != 0)
  329. {
  330. addToDesktop (0, parentHWND);
  331. setVisible (true);
  332. oldWinProc = SubclassWindow (parentHWND, (WNDPROC) interceptingWinProc);
  333. jassert (GetWindowLongPtr (parentHWND, GWLP_USERDATA) == 0);
  334. SetWindowLongPtr (parentHWND, GWLP_USERDATA, (LONG_PTR) this);
  335. resizeToParentWindow (window->width, window->height);
  336. }
  337. }
  338. }
  339. //==============================================================================
  340. #else
  341. NSView* currentParentView;
  342. NSView* findViewAt (NSView* parent, float x, float y) const
  343. {
  344. NSRect frame = [parent frame];
  345. NSRect bounds = [parent bounds];
  346. x -= frame.origin.x;
  347. y -= frame.origin.y;
  348. Rectangle<int> rr (frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
  349. Rectangle<int> rr2 (bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
  350. //log (String ((int) x) + ", " + String ((int) y) + " - " + nsStringToJuce([parent description]) + " " + rr.toString() + " " + rr2.toString());
  351. if (x >= 0 && x < frame.size.width && y >= 0 && y < frame.size.height)
  352. {
  353. x += bounds.origin.x; // adjust for scrolling panels
  354. y += bounds.origin.y;
  355. for (int i = [[parent subviews] count]; --i >= 0;)
  356. {
  357. NSView* v = (NSView*) [[parent subviews] objectAtIndex: i];
  358. if (v != (NSView*) getWindowHandle() && ! [v isHidden])
  359. {
  360. NSView* found = findViewAt (v, x, y);
  361. if (found != nil)
  362. return found;
  363. }
  364. }
  365. if (isBrowserContentView (parent))
  366. return parent;
  367. }
  368. return nil;
  369. }
  370. public:
  371. static bool isBrowserContentView (NSView* v)
  372. {
  373. return [[v className] isEqualToString: @"WebNetscapePluginDocumentView"]
  374. || [[v className] isEqualToString: @"WebPluginDocumentView"]
  375. || ([[v className] isEqualToString: @"ChildView"] && ([v frame].origin.x != 0 && [v frame].origin.y != 0));
  376. }
  377. static bool contains (NSView* parent, NSView* child)
  378. {
  379. if (parent == child)
  380. return true;
  381. for (int i = [[parent subviews] count]; --i >= 0;)
  382. if (contains ((NSView*) [[parent subviews] objectAtIndex: i], child))
  383. return true;
  384. return false;
  385. }
  386. void setWindow (NPWindow* window)
  387. {
  388. JUCE_AUTORELEASEPOOL;
  389. log ("setWindow");
  390. NSView* parentView = nil;
  391. NP_CGContext* const cgContext = (window != nullptr) ? (NP_CGContext*) window->window : nullptr;
  392. log ("NP_CGContext: " + String::toHexString ((pointer_sized_int) cgContext));
  393. #ifndef __LP64__
  394. WindowRef windowRef = cgContext != nullptr ? (WindowRef) cgContext->window : 0;
  395. if (windowRef != 0)
  396. {
  397. NSWindow* win = [[[NSWindow alloc] initWithWindowRef: windowRef] autorelease];
  398. #else
  399. NSWindow* win = cgContext != nullptr ? (NSWindow*) cgContext->window : nil;
  400. if (win != nil)
  401. {
  402. #endif
  403. log ("window: " + nsStringToJuce ([win description]));
  404. const Rectangle<int> clip (window->clipRect.left, window->clipRect.top,
  405. window->clipRect.right - window->clipRect.left,
  406. window->clipRect.bottom - window->clipRect.top);
  407. const Rectangle<int> target ((int) window->x, (int) window->y, (int) window->width, (int) window->height);
  408. const Rectangle<int> intersection (clip.getIntersection (target));
  409. // in firefox the clip rect is usually out of step with the target rect, but in safari it matches
  410. log ("plugin window clip: " + clip.toString());
  411. log ("plugin window target: " + target.toString());
  412. log ("plugin window intersection: " + intersection.toString());
  413. NSView* content = [win contentView];
  414. if (! intersection.isEmpty())
  415. {
  416. log ("content: " + nsStringToJuce ([content description]));
  417. float wx = (float) intersection.getCentreX();
  418. float wy = (float) intersection.getCentreY();
  419. NSRect v = [content convertRect: [content frame] toView: nil];
  420. NSRect w = [win frame];
  421. log ("content: " + Rectangle<int> (v.origin.x, v.origin.y, v.size.width, v.size.height).toString()
  422. + " frame: " + Rectangle<int> (w.origin.x, w.origin.y, w.size.width, w.size.height).toString());
  423. // adjust the requested window pos to deal with the content view's origin within the window
  424. wy -= w.size.height - (v.origin.y + v.size.height);
  425. parentView = findViewAt (content, wx, wy);
  426. if (! isBrowserContentView (parentView))
  427. parentView = currentParentView;
  428. }
  429. else if (currentParentView != nil && ! target.isEmpty())
  430. {
  431. // Firefox can send lots of spurious resize messages when updating its pages, so this is a
  432. // bodge to avoid flickering caused by repeatedly removing and re-adding the view..
  433. if (content != nil && contains (content, currentParentView))
  434. parentView = currentParentView;
  435. }
  436. log ("parent: " + nsStringToJuce ([parentView description]));
  437. }
  438. if (parentView != currentParentView)
  439. {
  440. log ("new view: " + nsStringToJuce ([parentView description]));
  441. removeFromDesktop();
  442. setVisible (false);
  443. currentParentView = parentView;
  444. if (parentView != nil)
  445. {
  446. setSize (window->width, window->height);
  447. addToDesktop (0, parentView);
  448. setVisible (true);
  449. }
  450. }
  451. if (window != nullptr)
  452. {
  453. if (isFirefox4)
  454. {
  455. const Rectangle<int> clip (window->clipRect.left, window->clipRect.top,
  456. window->clipRect.right - window->clipRect.left,
  457. window->clipRect.bottom - window->clipRect.top);
  458. const Rectangle<int> target ((int) window->x, (int) window->y, (int) window->width, (int) window->height);
  459. const Rectangle<int> intersection (clip.getIntersection (target));
  460. if (child != nullptr)
  461. child->setBounds (target.withPosition (target.getPosition() - clip.getPosition()));
  462. // This voodoo is required to keep the plugin clipped to the correct region and
  463. // to stop it overwriting the toolbars in FF4.
  464. NSRect parentFrame = [parentView frame];
  465. setBounds (parentFrame.origin.x + target.getWidth() - clip.getRight(),
  466. parentFrame.origin.y + target.getHeight() - clip.getBottom(),
  467. intersection.getWidth(), intersection.getHeight());
  468. }
  469. else
  470. {
  471. setSize (window->width, window->height);
  472. }
  473. }
  474. }
  475. #endif
  476. };
  477. //==============================================================================
  478. static NPIdentifier getIdentifierFromString (const var::identifier& s) noexcept
  479. {
  480. return browser.getstringidentifier (s.toString().toUTF8());
  481. }
  482. static const var createValueFromNPVariant (NPP npp, const NPVariant& v);
  483. static void createNPVariantFromValue (NPP npp, NPVariant& out, const var& v);
  484. #if JUCE_DEBUG
  485. static int numDOWNP = 0, numJuceSO = 0;
  486. #endif
  487. //==============================================================================
  488. class DynamicObjectWrappingNPObject : public DynamicObject
  489. {
  490. NPP npp;
  491. NPObject* const source;
  492. public:
  493. DynamicObjectWrappingNPObject (NPP npp_, NPObject* const source_)
  494. : npp (npp_),
  495. source (browser.retainobject (source_))
  496. {
  497. DBG ("num NP wrapper objs: " + String (++numDOWNP));
  498. }
  499. ~DynamicObjectWrappingNPObject()
  500. {
  501. browser.releaseobject (source);
  502. DBG ("num NP wrapper objs: " + String (--numDOWNP));
  503. }
  504. var getProperty (const var::identifier& propertyName) const
  505. {
  506. NPVariant result;
  507. VOID_TO_NPVARIANT (result);
  508. browser.getproperty (npp, source, getIdentifierFromString (propertyName), &result);
  509. const var v (createValueFromNPVariant (npp, result));
  510. browser.releasevariantvalue (&result);
  511. return v;
  512. }
  513. bool hasProperty (const var::identifier& propertyName) const
  514. {
  515. NPVariant result;
  516. VOID_TO_NPVARIANT (result);
  517. const bool hasProp = browser.getproperty (npp, source, getIdentifierFromString (propertyName), &result);
  518. browser.releasevariantvalue (&result);
  519. return hasProp;
  520. }
  521. void setProperty (const var::identifier& propertyName, const var& newValue)
  522. {
  523. NPVariant value;
  524. createNPVariantFromValue (npp, value, newValue);
  525. browser.setproperty (npp, source, getIdentifierFromString (propertyName), &value);
  526. browser.releasevariantvalue (&value);
  527. }
  528. void removeProperty (const var::identifier& propertyName)
  529. {
  530. browser.removeproperty (npp, source, getIdentifierFromString (propertyName));
  531. }
  532. bool hasMethod (const var::identifier& methodName) const
  533. {
  534. return browser.hasmethod (npp, source, getIdentifierFromString (methodName));
  535. }
  536. var invokeMethod (const var::identifier& methodName,
  537. const var* parameters,
  538. int numParameters)
  539. {
  540. var returnVal;
  541. NPVariant result;
  542. VOID_TO_NPVARIANT (result);
  543. if (numParameters > 0)
  544. {
  545. HeapBlock <NPVariant> params (numParameters);
  546. int i;
  547. for (i = 0; i < numParameters; ++i)
  548. createNPVariantFromValue (npp, params[i], parameters[i]);
  549. if (browser.invoke (npp, source, getIdentifierFromString (methodName),
  550. params, numParameters, &result))
  551. {
  552. returnVal = createValueFromNPVariant (npp, result);
  553. browser.releasevariantvalue (&result);
  554. }
  555. for (i = 0; i < numParameters; ++i)
  556. browser.releasevariantvalue (&params[i]);
  557. }
  558. else
  559. {
  560. if (browser.invoke (npp, source, getIdentifierFromString (methodName), 0, 0, &result))
  561. {
  562. returnVal = createValueFromNPVariant (npp, result);
  563. browser.releasevariantvalue (&result);
  564. }
  565. }
  566. return returnVal;
  567. }
  568. };
  569. //==============================================================================
  570. class NPObjectWrappingDynamicObject : public NPObject
  571. {
  572. public:
  573. static NPObject* create (NPP npp, const var& objectToWrap);
  574. virtual ~NPObjectWrappingDynamicObject()
  575. {
  576. DBG ("num Juce wrapper objs: " + String (--numJuceSO));
  577. }
  578. private:
  579. NPObjectWrappingDynamicObject (NPP npp_)
  580. : npp (npp_)
  581. {
  582. DBG ("num Juce wrapper objs: " + String (++numJuceSO));
  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 var::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, params.params, 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 var::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 var::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 const var::identifier identifierToString (NPIdentifier id)
  657. {
  658. NPUTF8* const name = browser.utf8fromidentifier (id);
  659. const var::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 const 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::null;
  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. #ifdef __LP64__
  814. browser.setvalue (npp, (NPPVariable) 1001 /*NPPVpluginEventModel*/, (void*) 1 /*NPEventModelCocoa*/);
  815. #else
  816. browser.setvalue (npp, (NPPVariable) 1001 /*NPPVpluginEventModel*/, 0 /*NPEventModelCarbon*/);
  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 = String::empty;
  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. const 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 const String getActiveXBrowserURL (const BrowserPluginComponent* comp);
  963. #endif
  964. const 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. }