ppInstance.cxx 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. // Filename: ppInstance.cxx
  2. // Created by: drose (19Jun09)
  3. //
  4. ////////////////////////////////////////////////////////////////////
  5. //
  6. // PANDA 3D SOFTWARE
  7. // Copyright (c) Carnegie Mellon University. All rights reserved.
  8. //
  9. // All use of this software is subject to the terms of the revised BSD
  10. // license. You should have received a copy of this license along
  11. // with this source code in a file named "LICENSE."
  12. //
  13. ////////////////////////////////////////////////////////////////////
  14. #include "ppInstance.h"
  15. #include "ppPandaObject.h"
  16. #include "ppBrowserObject.h"
  17. #include "startup.h"
  18. #include "p3d_plugin_config.h"
  19. #include "find_root_dir.h"
  20. #include "mkdir_complete.h"
  21. #include <fstream>
  22. #include <string.h> // strcmp()
  23. ////////////////////////////////////////////////////////////////////
  24. // Function: PPInstance::Constructor
  25. // Access: Public
  26. // Description: Creates a new instance of a Panda3D plugin window.
  27. // The create_data structure is supplied from NPAPI, and
  28. // defines the initial parameters specified in the HTML
  29. // document.
  30. ////////////////////////////////////////////////////////////////////
  31. PPInstance::
  32. PPInstance(NPMIMEType pluginType, NPP instance, uint16 mode,
  33. int16 argc, char *argn[], char *argv[], NPSavedData *saved) {
  34. _p3d_inst = NULL;
  35. _npp_instance = instance;
  36. _npp_mode = mode;
  37. _script_object = NULL;
  38. // Copy the tokens and save them within this object.
  39. _tokens.reserve(argc);
  40. for (int i = 0; i < argc; ++i) {
  41. P3D_token token;
  42. token._keyword = strdup(argn[i]);
  43. token._value = strdup(argv[i]);
  44. _tokens.push_back(token);
  45. }
  46. _started_instance_data = false;
  47. _got_instance_data = false;
  48. _got_window = false;
  49. }
  50. ////////////////////////////////////////////////////////////////////
  51. // Function: PPInstance::Destructor
  52. // Access: Public
  53. // Description:
  54. ////////////////////////////////////////////////////////////////////
  55. PPInstance::
  56. ~PPInstance() {
  57. #ifdef _WIN32
  58. if (_got_window) {
  59. // Restore the parent window to its own window handler.
  60. HWND hwnd = (HWND)_window.window;
  61. SetWindowLongPtr(hwnd, GWL_WNDPROC, _orig_window_proc);
  62. }
  63. #endif // _WIN32
  64. if (_p3d_inst != NULL) {
  65. P3D_instance_finish(_p3d_inst);
  66. _p3d_inst = NULL;
  67. }
  68. if (_script_object != NULL) {
  69. browser->releaseobject(_script_object);
  70. }
  71. // Free the tokens we allocated.
  72. Tokens::iterator ti;
  73. for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
  74. free((char *)(*ti)._keyword);
  75. free((char *)(*ti)._value);
  76. }
  77. _tokens.clear();
  78. }
  79. ////////////////////////////////////////////////////////////////////
  80. // Function: PPInstance::begin
  81. // Access: Public
  82. // Description: Begins the initial download of the core API. This
  83. // should be called after constructing the PPInstance.
  84. // It is a separate method than the constructor, because
  85. // it initiates some callbacks that might rely on the
  86. // object having been fully constructed and its pointer
  87. // stored.
  88. ////////////////////////////////////////////////////////////////////
  89. void PPInstance::
  90. begin() {
  91. if (!is_plugin_loaded()) {
  92. // Go download the contents file, so we can download the core DLL.
  93. string url = P3D_PLUGIN_DOWNLOAD;
  94. url += "contents.xml";
  95. PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_contents_file);
  96. start_download(url, req);
  97. }
  98. }
  99. ////////////////////////////////////////////////////////////////////
  100. // Function: PPInstance::set_window
  101. // Access: Public
  102. // Description: Stores or updates the window parameters.
  103. ////////////////////////////////////////////////////////////////////
  104. void PPInstance::
  105. set_window(NPWindow *window) {
  106. if (_got_window &&
  107. window->x == _window.x &&
  108. window->y == _window.y &&
  109. window->width == _window.width &&
  110. window->height == _window.height) {
  111. // No changes.
  112. return;
  113. }
  114. if (_got_window) {
  115. // We don't expect the browser to change the window's parent
  116. // on-the-fly.
  117. assert(_window.window == window->window);
  118. }
  119. #ifdef _WIN32
  120. if (!_got_window) {
  121. _orig_window_proc = NULL;
  122. if (window->type == NPWindowTypeWindow) {
  123. // Subclass the window to make it call our own window_proc instead
  124. // of whatever window_proc it has already. This is just a dopey
  125. // trick to allow us to poll events in the main thread.
  126. HWND hwnd = (HWND)window->window;
  127. _orig_window_proc = SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)window_proc);
  128. }
  129. }
  130. #endif // _WIN32
  131. _window = *window;
  132. _got_window = true;
  133. if (_p3d_inst == NULL) {
  134. create_instance();
  135. } else {
  136. send_window();
  137. }
  138. }
  139. ////////////////////////////////////////////////////////////////////
  140. // Function: PPInstance::new_stream
  141. // Access: Public
  142. // Description: Receives notification of a new stream object, e.g. a
  143. // url request.
  144. ////////////////////////////////////////////////////////////////////
  145. NPError PPInstance::
  146. new_stream(NPMIMEType type, NPStream *stream, bool seekable, uint16 *stype) {
  147. if (stream->notifyData == NULL) {
  148. // This is an unsolicited stream. Assume the first unsolicited
  149. // stream we receive is the instance data; any other unsolicited
  150. // stream is an error.
  151. if (!_started_instance_data) {
  152. stream->notifyData = new PPDownloadRequest(PPDownloadRequest::RT_instance_data);
  153. *stype = NP_ASFILEONLY;
  154. _started_instance_data = true;
  155. return NPERR_NO_ERROR;
  156. }
  157. // This is an unexpected unsolicited stream. (Firefox seems to
  158. // give us the instance data twice for some reason.)
  159. return NPERR_GENERIC_ERROR;
  160. }
  161. PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
  162. switch (req->_rtype) {
  163. case PPDownloadRequest::RT_contents_file:
  164. // This is the initial contents.xml file. We'll just download
  165. // this directly to a file, since it is small and this is easy.
  166. *stype = NP_ASFILEONLY;
  167. return NPERR_NO_ERROR;
  168. case PPDownloadRequest::RT_core_dll:
  169. // This is the core API DLL (or dylib or whatever). We want to
  170. // download this to file for convenience.
  171. *stype = NP_ASFILEONLY;
  172. return NPERR_NO_ERROR;
  173. case PPDownloadRequest::RT_user:
  174. // This is a request from the plugin. We'll receive this as a
  175. // stream.
  176. *stype = NP_NORMAL;
  177. return NPERR_NO_ERROR;
  178. default:
  179. // Don't know what this is.
  180. logfile << "Unexpected request " << (int)req->_rtype << "\n";
  181. }
  182. return NPERR_GENERIC_ERROR;
  183. }
  184. ////////////////////////////////////////////////////////////////////
  185. // Function: PPInstance::write_stream
  186. // Access: Public
  187. // Description: Called by the browser to feed data read from a URL or
  188. // whatever.
  189. ////////////////////////////////////////////////////////////////////
  190. int PPInstance::
  191. write_stream(NPStream *stream, int offset, int len, void *buffer) {
  192. if (stream->notifyData == NULL) {
  193. logfile << "Unexpected write_stream on " << stream->url << "\n";
  194. return 0;
  195. }
  196. PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
  197. switch (req->_rtype) {
  198. case PPDownloadRequest::RT_user:
  199. P3D_instance_feed_url_stream(_p3d_inst, req->_user_id,
  200. P3D_RC_in_progress, 0,
  201. stream->end, buffer, len);
  202. return len;
  203. default:
  204. logfile << "Unexpected write_stream on " << stream->url << "\n";
  205. break;
  206. }
  207. return 0;
  208. }
  209. ////////////////////////////////////////////////////////////////////
  210. // Function: PPInstance::destroy_stream
  211. // Access: Public
  212. // Description: Called by the browser to mark the end of a stream;
  213. // the file has either been successfully downloaded or
  214. // failed.
  215. ////////////////////////////////////////////////////////////////////
  216. NPError PPInstance::
  217. destroy_stream(NPStream *stream, NPReason reason) {
  218. if (stream->notifyData == NULL) {
  219. logfile << "Unexpected destroy_stream on " << stream->url << "\n";
  220. return NPERR_GENERIC_ERROR;
  221. }
  222. PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
  223. switch (req->_rtype) {
  224. case PPDownloadRequest::RT_user:
  225. {
  226. P3D_result_code result_code = P3D_RC_done;
  227. if (reason != NPRES_DONE) {
  228. result_code = P3D_RC_generic_error;
  229. }
  230. assert(!req->_notified_done);
  231. P3D_instance_feed_url_stream(_p3d_inst, req->_user_id,
  232. result_code, 0, stream->end, NULL, 0);
  233. req->_notified_done = true;
  234. }
  235. break;
  236. default:
  237. break;
  238. }
  239. return NPERR_NO_ERROR;
  240. }
  241. ////////////////////////////////////////////////////////////////////
  242. // Function: PPInstance::url_notify
  243. // Access: Public
  244. // Description: Called by the browser to announce the end of a
  245. // stream. This normally follows destroy_stream(),
  246. // unless the stream was never created in the first
  247. // place.
  248. ////////////////////////////////////////////////////////////////////
  249. void PPInstance::
  250. url_notify(const char *url, NPReason reason, void *notifyData) {
  251. if (notifyData == NULL) {
  252. return;
  253. }
  254. PPDownloadRequest *req = (PPDownloadRequest *)notifyData;
  255. switch (req->_rtype) {
  256. case PPDownloadRequest::RT_user:
  257. if (!req->_notified_done) {
  258. // We shouldn't have gotten here without notifying the stream
  259. // unless the stream never got started (and hence we never
  260. // called destroy_stream().
  261. logfile << "Failure starting stream\n" << flush;
  262. assert(reason != NPRES_DONE);
  263. P3D_instance_feed_url_stream(_p3d_inst, req->_user_id,
  264. P3D_RC_generic_error, 0, 0, NULL, 0);
  265. req->_notified_done = true;
  266. }
  267. break;
  268. case PPDownloadRequest::RT_contents_file:
  269. if (reason != NPRES_DONE) {
  270. logfile << "Failure downloading " << url << "\n";
  271. // TODO: fail
  272. }
  273. break;
  274. default:
  275. break;
  276. }
  277. delete req;
  278. }
  279. ////////////////////////////////////////////////////////////////////
  280. // Function: PPInstance::stream_as_file
  281. // Access: Public
  282. // Description: Called by the browser to report the filename that
  283. // contains the fully-downloaded stream contents.
  284. ////////////////////////////////////////////////////////////////////
  285. void PPInstance::
  286. stream_as_file(NPStream *stream, const char *fname) {
  287. if (stream->notifyData == NULL) {
  288. logfile << "Unexpected stream_as_file on " << stream->url << "\n";
  289. return;
  290. }
  291. string filename = fname;
  292. #ifdef __APPLE__
  293. // Safari seems to want to report the filename in the old-style form
  294. // "Macintosh HD:blah:blah:blah" instead of the new-style form
  295. // "/blah/blah/blah". How annoying.
  296. // TODO: Is "Macintosh HD:" the only possible prefix?
  297. if (filename.substr(0, 13) == "Macintosh HD:") {
  298. string fname2;
  299. for (size_t p = 12; p < filename.size(); ++p) {
  300. if (filename[p] == ':') {
  301. fname2 += '/';
  302. } else {
  303. fname2 += filename[p];
  304. }
  305. }
  306. filename = fname2;
  307. // Here's another temporary hack. In addition to the weird
  308. // filename format, the file that Safari tells us about appears to
  309. // be a temporary file that Safari's about to delete. In order to
  310. // protect ourselves from this, we need to temporarily copy the
  311. // file somewhere else.
  312. char *name = tempnam(NULL, "p3d_");
  313. // We prefer just making a hard link; it's quick and easy.
  314. if (link(filename.c_str(), name) == 0) {
  315. logfile << "linked " << filename << " to " << name << "\n";
  316. } else {
  317. // But sometimes the hard link might fail, particularly if these
  318. // are two different file systems. In this case we have to open
  319. // the files and copy the data by hand.
  320. ifstream in(filename.c_str(), ios::in | ios::binary);
  321. ofstream out(name, ios::out | ios::binary);
  322. static const size_t buffer_size = 4096;
  323. char buffer[buffer_size];
  324. in.read(buffer, buffer_size);
  325. size_t count = in.gcount();
  326. while (count != 0) {
  327. out.write(buffer, count);
  328. in.read(buffer, buffer_size);
  329. count = in.gcount();
  330. }
  331. logfile << "copied " << filename << " to " << name << "\n";
  332. }
  333. filename = name;
  334. free(name);
  335. // TODO: remove this temporary file when we're done with it.
  336. }
  337. #endif // __APPLE__
  338. PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
  339. downloaded_file(req, filename);
  340. }
  341. ////////////////////////////////////////////////////////////////////
  342. // Function: PPInstance::handle_request
  343. // Access: Public
  344. // Description: Handles a request from the plugin, forwarding
  345. // it to the browser as appropriate.
  346. ////////////////////////////////////////////////////////////////////
  347. void PPInstance::
  348. handle_request(P3D_request *request) {
  349. assert(request->_instance == _p3d_inst);
  350. bool handled = false;
  351. switch (request->_request_type) {
  352. case P3D_RT_stop:
  353. if (_p3d_inst != NULL) {
  354. P3D_instance_finish(_p3d_inst);
  355. _p3d_inst = NULL;
  356. }
  357. // Guess the browser doesn't really care.
  358. handled = true;
  359. break;
  360. case P3D_RT_get_url:
  361. {
  362. PPDownloadRequest *req =
  363. new PPDownloadRequest(PPDownloadRequest::RT_user,
  364. request->_request._get_url._unique_id);
  365. start_download(request->_request._get_url._url, req);
  366. }
  367. break;
  368. case P3D_RT_notify:
  369. {
  370. if (_script_object != NULL &&
  371. strcmp(request->_request._notify._message, "onpythonload") == 0) {
  372. // Now that Python is running, initialize our script_object
  373. // with the proper P3D object pointer.
  374. P3D_object *obj = P3D_instance_get_panda_script_object(_p3d_inst);
  375. _script_object->set_p3d_object(obj);
  376. logfile << "got onpythonload\n";
  377. }
  378. }
  379. break;
  380. default:
  381. // Some request types are not handled.
  382. logfile << "Unhandled request: " << request->_request_type << "\n";
  383. break;
  384. };
  385. P3D_request_finish(request, handled);
  386. }
  387. ////////////////////////////////////////////////////////////////////
  388. // Function: PPInstance::handle_request_loop
  389. // Access: Public, Static
  390. // Description: Checks for any new requests from the plugin, and
  391. // dispatches them to the appropriate PPInstance. This
  392. // function is called only in the main thread.
  393. ////////////////////////////////////////////////////////////////////
  394. void PPInstance::
  395. handle_request_loop() {
  396. if (!is_plugin_loaded()) {
  397. return;
  398. }
  399. P3D_instance *p3d_inst = P3D_check_request(false);
  400. while (p3d_inst != (P3D_instance *)NULL) {
  401. P3D_request *request = P3D_instance_get_request(p3d_inst);
  402. if (request != (P3D_request *)NULL) {
  403. PPInstance *inst = (PPInstance *)(p3d_inst->_user_data);
  404. assert(inst != NULL);
  405. inst->handle_request(request);
  406. }
  407. p3d_inst = P3D_check_request(false);
  408. }
  409. }
  410. ////////////////////////////////////////////////////////////////////
  411. // Function: PPInstance::get_panda_script_object
  412. // Access: Public
  413. // Description: Returns a toplevel object that JavaScript or whatever
  414. // can read and/or modify to control the instance.
  415. ////////////////////////////////////////////////////////////////////
  416. NPObject *PPInstance::
  417. get_panda_script_object() {
  418. if (_script_object != NULL) {
  419. // NPRuntime "steals" a reference to this object.
  420. browser->retainobject(_script_object);
  421. return _script_object;
  422. }
  423. P3D_object *obj = NULL;
  424. if (_p3d_inst != NULL) {
  425. obj = P3D_instance_get_panda_script_object(_p3d_inst);
  426. }
  427. _script_object = PPPandaObject::make_new(this, obj);
  428. browser->retainobject(_script_object);
  429. return _script_object;
  430. }
  431. ////////////////////////////////////////////////////////////////////
  432. // Function: PPInstance::p3dobj_to_variant
  433. // Access: Public
  434. // Description: Converts the indicated P3D_object to the equivalent
  435. // NPVariant, and stores it in result.
  436. ////////////////////////////////////////////////////////////////////
  437. void PPInstance::
  438. p3dobj_to_variant(NPVariant *result, const P3D_object *object) {
  439. switch (P3D_OBJECT_GET_TYPE(object)) {
  440. case P3D_OT_undefined:
  441. VOID_TO_NPVARIANT(*result);
  442. break;
  443. case P3D_OT_none:
  444. NULL_TO_NPVARIANT(*result);
  445. break;
  446. case P3D_OT_bool:
  447. BOOLEAN_TO_NPVARIANT(P3D_OBJECT_GET_BOOL(object), *result);
  448. break;
  449. case P3D_OT_int:
  450. INT32_TO_NPVARIANT(P3D_OBJECT_GET_INT(object), *result);
  451. break;
  452. case P3D_OT_float:
  453. DOUBLE_TO_NPVARIANT(P3D_OBJECT_GET_FLOAT(object), *result);
  454. break;
  455. case P3D_OT_string:
  456. {
  457. int size = P3D_OBJECT_GET_STRING(object, NULL, 0);
  458. char *buffer = (char *)browser->memalloc(size);
  459. P3D_OBJECT_GET_STRING(object, buffer, size);
  460. STRINGN_TO_NPVARIANT(buffer, size, *result);
  461. }
  462. break;
  463. case P3D_OT_object:
  464. {
  465. PPPandaObject *ppobj = PPPandaObject::make_new(this, P3D_OBJECT_COPY(object));
  466. OBJECT_TO_NPVARIANT(ppobj, *result);
  467. }
  468. break;
  469. }
  470. }
  471. ////////////////////////////////////////////////////////////////////
  472. // Function: PPInstance::variant_to_p3dobj
  473. // Access: Public
  474. // Description: Converts the indicated NPVariant to the equivalent
  475. // P3D_object, and returns it (newly-allocated). The
  476. // caller is responsible for freeing the returned object
  477. // later.
  478. ////////////////////////////////////////////////////////////////////
  479. P3D_object *PPInstance::
  480. variant_to_p3dobj(const NPVariant *variant) {
  481. if (NPVARIANT_IS_VOID(*variant)) {
  482. return P3D_new_undefined_object();
  483. } else if (NPVARIANT_IS_NULL(*variant)) {
  484. return P3D_new_none_object();
  485. } else if (NPVARIANT_IS_BOOLEAN(*variant)) {
  486. return P3D_new_bool_object(NPVARIANT_TO_BOOLEAN(*variant));
  487. } else if (NPVARIANT_IS_INT32(*variant)) {
  488. return P3D_new_int_object(NPVARIANT_TO_INT32(*variant));
  489. } else if (NPVARIANT_IS_DOUBLE(*variant)) {
  490. return P3D_new_float_object(NPVARIANT_TO_DOUBLE(*variant));
  491. } else if (NPVARIANT_IS_STRING(*variant)) {
  492. NPString str = NPVARIANT_TO_STRING(*variant);
  493. return P3D_new_string_object(str.utf8characters, str.utf8length);
  494. } else if (NPVARIANT_IS_OBJECT(*variant)) {
  495. return new PPBrowserObject(this, NPVARIANT_TO_OBJECT(*variant));
  496. }
  497. // Hmm, none of the above?
  498. return P3D_new_none_object();
  499. }
  500. ////////////////////////////////////////////////////////////////////
  501. // Function: PPInstance::start_download
  502. // Access: Private
  503. // Description: Initiates a download request.
  504. ////////////////////////////////////////////////////////////////////
  505. void PPInstance::
  506. start_download(const string &url, PPDownloadRequest *req) {
  507. if (url.substr(0, 7) == "file://") {
  508. // If we're "downloading" a file URL, just go read the file directly.
  509. downloaded_file(req, get_filename_from_url(url));
  510. delete req;
  511. } else {
  512. // Otherwise, ask the browser to download it.
  513. browser->geturlnotify(_npp_instance, url.c_str(), NULL, req);
  514. }
  515. }
  516. ////////////////////////////////////////////////////////////////////
  517. // Function: PPInstance::read_contents_file
  518. // Access: Private
  519. // Description: Reads the contents.xml file and starts the core API
  520. // DLL downloading, if necessary.
  521. ////////////////////////////////////////////////////////////////////
  522. bool PPInstance::
  523. read_contents_file(const string &filename) {
  524. TiXmlDocument doc(filename.c_str());
  525. if (!doc.LoadFile()) {
  526. return false;
  527. }
  528. TiXmlElement *xpackage = doc.FirstChildElement("package");
  529. while (xpackage != NULL) {
  530. const char *name = xpackage->Attribute("name");
  531. if (name != NULL && strcmp(name, "coreapi") == 0) {
  532. get_core_api(xpackage);
  533. return true;
  534. }
  535. xpackage = xpackage->NextSiblingElement("package");
  536. }
  537. // Couldn't find the core package description.
  538. logfile << "No core package defined in contents file.\n" << flush;
  539. return false;
  540. }
  541. ////////////////////////////////////////////////////////////////////
  542. // Function: PPInstance::get_filename_from_url
  543. // Access: Private, Static
  544. // Description: Returns the actual filename referenced by a file://
  545. // url.
  546. ////////////////////////////////////////////////////////////////////
  547. string PPInstance::
  548. get_filename_from_url(const string &url) {
  549. string filename = url.substr(7);
  550. #ifdef _WIN32
  551. // On Windows, we have to munge the filename specially, because it's
  552. // been URL-munged. It might begin with a leading slash as well as
  553. // a drive letter. Clean up that nonsense.
  554. if (filename.length() >= 3 &&
  555. (filename[0] == '/' || filename[0] == '\\') &&
  556. isalpha(filename[1]) && filename[2] == ':') {
  557. filename = filename.substr(1);
  558. }
  559. #endif // _WIN32
  560. return filename;
  561. }
  562. ////////////////////////////////////////////////////////////////////
  563. // Function: PPInstance::downloaded_file
  564. // Access: Private
  565. // Description: Called to receive the fully-downloaded contents of a
  566. // URL.
  567. ////////////////////////////////////////////////////////////////////
  568. void PPInstance::
  569. downloaded_file(PPDownloadRequest *req, const string &filename) {
  570. switch (req->_rtype) {
  571. case PPDownloadRequest::RT_contents_file:
  572. // Now we have the contents.xml file. Read this to get the
  573. // filename and md5 hash of our core API DLL.
  574. if (!read_contents_file(filename)) {
  575. logfile << "Unable to read contents file\n";
  576. // TODO: fail
  577. }
  578. break;
  579. case PPDownloadRequest::RT_core_dll:
  580. // This is the core API DLL (or dylib or whatever). Now that
  581. // we've downloaded it, we can load it.
  582. downloaded_plugin(filename);
  583. break;
  584. case PPDownloadRequest::RT_instance_data:
  585. // This is the instance data, e.g. the p3d filename. Now we can
  586. // launch the instance.
  587. _got_instance_data = true;
  588. _p3d_filename = filename;
  589. create_instance();
  590. break;
  591. case PPDownloadRequest::RT_user:
  592. // Normally, RT_user requests won't come here, unless we
  593. // short-circuited the browser by "downloading" a file:// url. In
  594. // any case, we'll now open the file and feed it to the user.
  595. feed_file(req, filename);
  596. break;
  597. default:
  598. // Don't know what this is.
  599. logfile << "Unexpected downloaded file, type " << (int)req->_rtype << "\n";
  600. }
  601. }
  602. ////////////////////////////////////////////////////////////////////
  603. // Function: PPInstance::feed_file
  604. // Access: Private
  605. // Description: Opens the named file (extracted from a file:// URL)
  606. // and feeds its contents to the plugin.
  607. ////////////////////////////////////////////////////////////////////
  608. void PPInstance::
  609. feed_file(PPDownloadRequest *req, const string &filename) {
  610. ifstream file(filename.c_str(), ios::in | ios::binary);
  611. // First, seek to the end to get the file size.
  612. file.seekg(0, ios::end);
  613. size_t file_size = file.tellg();
  614. // Then return to the beginning to read the data.
  615. file.seekg(0, ios::beg);
  616. static const size_t buffer_size = 4096;
  617. char buffer[buffer_size];
  618. file.read(buffer, buffer_size);
  619. size_t count = file.gcount();
  620. size_t total_count = 0;
  621. while (count != 0) {
  622. bool download_ok = P3D_instance_feed_url_stream
  623. (_p3d_inst, req->_user_id, P3D_RC_in_progress,
  624. 0, file_size, (const unsigned char *)buffer, count);
  625. if (!download_ok) {
  626. // Never mind.
  627. return;
  628. }
  629. file.read(buffer, buffer_size);
  630. count = file.gcount();
  631. total_count += count;
  632. }
  633. P3D_result_code result = P3D_RC_done;
  634. if (file.fail() && !file.eof()) {
  635. // Got an error while reading.
  636. result = P3D_RC_generic_error;
  637. }
  638. P3D_instance_feed_url_stream
  639. (_p3d_inst, req->_user_id, result, 0, total_count, NULL, 0);
  640. }
  641. ////////////////////////////////////////////////////////////////////
  642. // Function: PPInstance::get_core_api
  643. // Access: Private
  644. // Description: Checks the core API DLL file against the
  645. // specification in the contents file, and downloads it
  646. // if necessary.
  647. ////////////////////////////////////////////////////////////////////
  648. void PPInstance::
  649. get_core_api(TiXmlElement *xpackage) {
  650. _core_api_dll.load_xml(xpackage);
  651. _root_dir = find_root_dir();
  652. if (_core_api_dll.quick_verify(_root_dir)) {
  653. // The DLL file is good. Just load it.
  654. do_load_plugin();
  655. } else {
  656. // The DLL file needs to be downloaded. Go get it.
  657. string url = P3D_PLUGIN_DOWNLOAD;
  658. url += _core_api_dll.get_filename();
  659. PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_core_dll);
  660. start_download(url, req);
  661. }
  662. }
  663. ////////////////////////////////////////////////////////////////////
  664. // Function: PPInstance::downloaded_plugin
  665. // Access: Private
  666. // Description: The core API DLL has been successfully downloaded;
  667. // copy it into place.
  668. ////////////////////////////////////////////////////////////////////
  669. void PPInstance::
  670. downloaded_plugin(const string &filename) {
  671. // We could have been downloading this file as a stream, but that
  672. // would cause problems with multiple instances downloading the
  673. // plugin at the same time. Instead, we let them all download the
  674. // file asfile, and then only one of them is allowed to copy it into
  675. // place.
  676. if (is_plugin_loaded()) {
  677. // Some other instance got there first. Just get started.
  678. create_instance();
  679. return;
  680. }
  681. // Copy the file onto the target.
  682. string pathname = _core_api_dll.get_pathname(_root_dir);
  683. mkfile_complete(pathname);
  684. ifstream in(filename.c_str(), ios::in | ios::binary);
  685. ofstream out(pathname.c_str(), ios::out | ios::binary);
  686. static const size_t buffer_size = 4096;
  687. static char buffer[buffer_size];
  688. in.read(buffer, buffer_size);
  689. size_t count = in.gcount();
  690. while (count != 0) {
  691. out.write(buffer, count);
  692. in.read(buffer, buffer_size);
  693. count = in.gcount();
  694. }
  695. if (!out) {
  696. logfile << "Could not write " << pathname << "\n";
  697. // TODO: fail
  698. return;
  699. }
  700. in.close();
  701. out.close();
  702. if (_core_api_dll.quick_verify(_root_dir)) {
  703. // We downloaded and installed it successfully. Now load it.
  704. do_load_plugin();
  705. return;
  706. }
  707. logfile << "After download, " << pathname << " is no good.\n";
  708. // TODO: fail
  709. }
  710. ////////////////////////////////////////////////////////////////////
  711. // Function: PPInstance::do_load_plugin
  712. // Access: Private
  713. // Description: Once the core API DLL has been downloaded, loads it
  714. // into memory and starts the instance.
  715. ////////////////////////////////////////////////////////////////////
  716. void PPInstance::
  717. do_load_plugin() {
  718. string pathname = _core_api_dll.get_pathname(_root_dir);
  719. #ifdef P3D_PLUGIN_P3D_PLUGIN
  720. // This is a convenience macro for development. If defined and
  721. // nonempty, it indicates the name of the plugin DLL that we will
  722. // actually run, even after downloading a possibly different
  723. // (presumably older) version. Its purpose is to simplify iteration
  724. // on the plugin DLL.
  725. string override_filename = P3D_PLUGIN_P3D_PLUGIN;
  726. if (!override_filename.empty()) {
  727. pathname = override_filename;
  728. }
  729. #endif // P3D_PLUGIN_P3D_PLUGIN
  730. if (!load_plugin(pathname)) {
  731. logfile << "Unable to launch core API in " << pathname << "\n" << flush;
  732. return;
  733. }
  734. create_instance();
  735. }
  736. ////////////////////////////////////////////////////////////////////
  737. // Function: PPInstance::create_instance
  738. // Access: Private
  739. // Description: Actually creates the internal P3D_instance object, if
  740. // possible and needed.
  741. ////////////////////////////////////////////////////////////////////
  742. void PPInstance::
  743. create_instance() {
  744. if (_p3d_inst != NULL) {
  745. // Already created.
  746. return;
  747. }
  748. if (!is_plugin_loaded()) {
  749. // Plugin is not loaded yet.
  750. return;
  751. }
  752. if (!_got_instance_data) {
  753. // No instance data yet.
  754. return;
  755. }
  756. if (!_got_window) {
  757. // No window yet.
  758. return;
  759. }
  760. _p3d_inst = P3D_new_instance(request_ready, this);
  761. if (_p3d_inst != NULL) {
  762. // Now get the browser's window object, to pass to the plugin.
  763. NPObject *window_object = NULL;
  764. if (browser->getvalue(_npp_instance, NPNVWindowNPObject,
  765. &window_object) == NPERR_NO_ERROR) {
  766. PPBrowserObject *pobj = new PPBrowserObject(this, window_object);
  767. P3D_instance_set_browser_script_object(_p3d_inst, pobj);
  768. browser->releaseobject(window_object);
  769. } else {
  770. logfile << "Couldn't get window_object\n" << flush;
  771. }
  772. const P3D_token *tokens = NULL;
  773. if (!_tokens.empty()) {
  774. tokens = &_tokens[0];
  775. }
  776. P3D_instance_start(_p3d_inst, _p3d_filename.c_str(), tokens, _tokens.size());
  777. send_window();
  778. }
  779. }
  780. ////////////////////////////////////////////////////////////////////
  781. // Function: PPInstance::send_window
  782. // Access: Private
  783. // Description: Actually issues the window parameters to the internal
  784. // P3D_instance object.
  785. ////////////////////////////////////////////////////////////////////
  786. void PPInstance::
  787. send_window() {
  788. assert(_p3d_inst != NULL);
  789. int x = _window.x;
  790. int y = _window.y;
  791. P3D_window_handle parent_window;
  792. #ifdef _WIN32
  793. if (_window.type == NPWindowTypeWindow) {
  794. // We have a "windowed" plugin. Parent our window to the one we
  795. // were given. In this case, we should also reset the offset to
  796. // (0, 0), since the window we were given is already placed in the
  797. // right spot.
  798. parent_window._hwnd = (HWND)(_window.window);
  799. x = 0;
  800. y = 0;
  801. } else {
  802. // We have a "windowless" plugin. Parent our window directly to
  803. // the browser window.
  804. parent_window._hwnd = 0;
  805. HWND hwnd;
  806. if (browser->getvalue(_npp_instance, NPNVnetscapeWindow,
  807. &hwnd) == NPERR_NO_ERROR) {
  808. parent_window._hwnd = hwnd;
  809. }
  810. }
  811. #endif
  812. P3D_instance_setup_window
  813. (_p3d_inst, P3D_WT_embedded,
  814. x, y, _window.width, _window.height,
  815. parent_window);
  816. }
  817. ////////////////////////////////////////////////////////////////////
  818. // Function: PPInstance::output_np_variant
  819. // Access: Public
  820. // Description: Outputs the variant value.
  821. ////////////////////////////////////////////////////////////////////
  822. void PPInstance::
  823. output_np_variant(ostream &out, const NPVariant &result) {
  824. if (NPVARIANT_IS_NULL(result)) {
  825. out << "null";
  826. } else if (NPVARIANT_IS_VOID(result)) {
  827. out << "void";
  828. } else if (NPVARIANT_IS_BOOLEAN(result)) {
  829. out << "bool " << NPVARIANT_TO_BOOLEAN(result);
  830. } else if (NPVARIANT_IS_INT32(result)) {
  831. out << "int " << NPVARIANT_TO_INT32(result);
  832. } else if (NPVARIANT_IS_DOUBLE(result)) {
  833. out << "double " << NPVARIANT_TO_DOUBLE(result);
  834. } else if (NPVARIANT_IS_STRING(result)) {
  835. NPString str = NPVARIANT_TO_STRING(result);
  836. out << "string " << string(str.utf8characters, str.utf8length);
  837. } else if (NPVARIANT_IS_OBJECT(result)) {
  838. NPObject *child = NPVARIANT_TO_OBJECT(result);
  839. out << "object " << child;
  840. }
  841. }
  842. #ifdef _WIN32
  843. ////////////////////////////////////////////////////////////////////
  844. // Function: window_proc
  845. // Access: Private, Static
  846. // Description: We bind this function to the parent window we were
  847. // given in set_window, so we can spin the request_loop
  848. // when needed. This is only in the Windows case; other
  849. // platforms rely on explicit windows events.
  850. ////////////////////////////////////////////////////////////////////
  851. LONG PPInstance::
  852. window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  853. // If this is a toplevel window event, but not something caused as a
  854. // result of something done by handle_request_loop(), then call
  855. // handle_request_loop() to see if there are any new requests to be
  856. // forwarded to the main thread.
  857. static int recursion_protect = 0;
  858. ++recursion_protect;
  859. if (recursion_protect == 1) {
  860. handle_request_loop();
  861. }
  862. --recursion_protect;
  863. switch (msg) {
  864. case WM_ERASEBKGND:
  865. // Eat the WM_ERASEBKGND message, so the browser's intervening
  866. // window won't overdraw on top of our own window.
  867. return true;
  868. }
  869. return DefWindowProc(hwnd, msg, wparam, lparam);
  870. }
  871. #endif // _WIN32