ppInstance.cxx 33 KB

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