media_plugin_webkit.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:33k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file media_plugin_webkit.cpp
  3.  * @brief Webkit plugin for LLMedia API plugin system
  4.  *
  5.  * @cond
  6.  * $LicenseInfo:firstyear=2008&license=viewergpl$
  7.  * 
  8.  * Copyright (c) 2008-2010, Linden Research, Inc.
  9.  * 
  10.  * Second Life Viewer Source Code
  11.  * The source code in this file ("Source Code") is provided by Linden Lab
  12.  * to you under the terms of the GNU General Public License, version 2.0
  13.  * ("GPL"), unless you have obtained a separate licensing agreement
  14.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  15.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17.  * 
  18.  * There are special exceptions to the terms and conditions of the GPL as
  19.  * it is applied to this Source Code. View the full text of the exception
  20.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  21.  * online at
  22.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23.  * 
  24.  * By copying, modifying or distributing this software, you acknowledge
  25.  * that you have read and understood your obligations described above,
  26.  * and agree to abide by those obligations.
  27.  * 
  28.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30.  * COMPLETENESS OR PERFORMANCE.
  31.  * $/LicenseInfo$
  32.  * @endcond
  33.  */
  34. #include "llqtwebkit.h"
  35. #include "linden_common.h"
  36. #include "indra_constants.h" // for indra keyboard codes
  37. #include "llgl.h"
  38. #include "llplugininstance.h"
  39. #include "llpluginmessage.h"
  40. #include "llpluginmessageclasses.h"
  41. #include "media_plugin_base.h"
  42. #if LL_WINDOWS
  43. #include <direct.h>
  44. #else
  45. #include <unistd.h>
  46. #include <stdlib.h>
  47. #endif
  48. #if LL_WINDOWS
  49. // *NOTE:Mani - This captures the module handle fo rthe dll. This is used below
  50. // to get the path to this dll for webkit initialization.
  51. // I don't know how/if this can be done with apr...
  52. namespace { HMODULE gModuleHandle;};
  53. BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
  54. {
  55. gModuleHandle = (HMODULE) hinstDLL;
  56. return TRUE;
  57. }
  58. #endif
  59. ////////////////////////////////////////////////////////////////////////////////
  60. //
  61. class MediaPluginWebKit : 
  62. public MediaPluginBase,
  63. public LLEmbeddedBrowserWindowObserver
  64. {
  65. public:
  66. MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
  67. ~MediaPluginWebKit();
  68. /*virtual*/ void receiveMessage(const char *message_string);
  69. private:
  70. std::string mProfileDir;
  71. enum
  72. {
  73. INIT_STATE_UNINITIALIZED, // Browser instance hasn't been set up yet
  74. INIT_STATE_NAVIGATING, // Browser instance has been set up and initial navigate to about:blank has been issued
  75. INIT_STATE_NAVIGATE_COMPLETE, // initial navigate to about:blank has completed
  76. INIT_STATE_WAIT_REDRAW, // First real navigate begin has been received, waiting for page changed event to start handling redraws
  77. INIT_STATE_WAIT_COMPLETE, // Waiting for first real navigate complete event
  78. INIT_STATE_RUNNING // All initialization gymnastics are complete.
  79. };
  80. int mBrowserWindowId;
  81. int mInitState;
  82. std::string mInitialNavigateURL;
  83. bool mNeedsUpdate;
  84. bool mCanCut;
  85. bool mCanCopy;
  86. bool mCanPaste;
  87. int mLastMouseX;
  88. int mLastMouseY;
  89. bool mFirstFocus;
  90. F32 mBackgroundR;
  91. F32 mBackgroundG;
  92. F32 mBackgroundB;
  93. void setInitState(int state)
  94. {
  95. // std::cerr << "changing init state to " << state << std::endl;
  96. mInitState = state;
  97. }
  98. ////////////////////////////////////////////////////////////////////////////////
  99. //
  100. void update(int milliseconds)
  101. {
  102. LLQtWebKit::getInstance()->pump( milliseconds );
  103. checkEditState();
  104. if(mInitState == INIT_STATE_NAVIGATE_COMPLETE)
  105. {
  106. if(!mInitialNavigateURL.empty())
  107. {
  108. // We already have the initial navigate URL -- kick off the navigate.
  109. LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, mInitialNavigateURL );
  110. mInitialNavigateURL.clear();
  111. }
  112. }
  113. if ( (mInitState > INIT_STATE_WAIT_REDRAW) && mNeedsUpdate )
  114. {
  115. const unsigned char* browser_pixels = LLQtWebKit::getInstance()->grabBrowserWindow( mBrowserWindowId );
  116. unsigned int buffer_size = LLQtWebKit::getInstance()->getBrowserRowSpan( mBrowserWindowId ) * LLQtWebKit::getInstance()->getBrowserHeight( mBrowserWindowId );
  117. // std::cerr << "webkit plugin: updating" << std::endl;
  118. // TODO: should get rid of this memcpy if possible
  119. if ( mPixels && browser_pixels )
  120. {
  121. // std::cerr << "    memcopy of " << buffer_size << " bytes" << std::endl;
  122. memcpy( mPixels, browser_pixels, buffer_size );
  123. }
  124. if ( mWidth > 0 && mHeight > 0 )
  125. {
  126. // std::cerr << "Setting dirty, " << mWidth << " x " << mHeight << std::endl;
  127. setDirty( 0, 0, mWidth, mHeight );
  128. }
  129. mNeedsUpdate = false;
  130. };
  131. };
  132. ////////////////////////////////////////////////////////////////////////////////
  133. //
  134. bool initBrowser()
  135. {
  136. // already initialized
  137. if ( mInitState > INIT_STATE_UNINITIALIZED )
  138. return true;
  139. // not enough information to initialize the browser yet.
  140. if ( mWidth < 0 || mHeight < 0 || mDepth < 0 || 
  141. mTextureWidth < 0 || mTextureHeight < 0 )
  142. {
  143. return false;
  144. };
  145. // set up directories
  146. char cwd[ FILENAME_MAX ]; // I *think* this is defined on all platforms we use
  147. if (NULL == getcwd( cwd, FILENAME_MAX - 1 ))
  148. {
  149. llwarns << "Couldn't get cwd - probably too long - failing to init." << llendl;
  150. return false;
  151. }
  152. std::string application_dir = std::string( cwd );
  153. #if LL_DARWIN
  154. // When running under the Xcode debugger, there's a setting called "Break on Debugger()/DebugStr()" which defaults to being turned on.
  155. // This causes the environment variable USERBREAK to be set to 1, which causes these legacy calls to break into the debugger.
  156. // This wouldn't cause any problems except for the fact that the current release version of the Flash plugin has a call to Debugger() in it
  157. // which gets hit when the plugin is probed by webkit.
  158. // Unsetting the environment variable here works around this issue.
  159. unsetenv("USERBREAK");
  160. #endif
  161. #if LL_WINDOWS
  162. //*NOTE:Mani - On windows, at least, the component path is the
  163. // location of this dll's image file. 
  164. std::string component_dir;
  165. char dll_path[_MAX_PATH];
  166. DWORD len = GetModuleFileNameA(gModuleHandle, (LPCH)&dll_path, _MAX_PATH);
  167. while(len && dll_path[ len ] != ('\') )
  168. {
  169. len--;
  170. }
  171. if(len >= 0)
  172. {
  173. dll_path[len] = 0;
  174. component_dir = dll_path;
  175. }
  176. else
  177. {
  178. // *NOTE:Mani - This case should be an rare exception. 
  179. // GetModuleFileNameA should always give you a full path, no?
  180. component_dir = application_dir;
  181. }
  182. #else
  183. std::string component_dir = application_dir;
  184. #endif
  185. // window handle - needed on Windows and must be app window.
  186. #if LL_WINDOWS
  187. char window_title[ MAX_PATH ];
  188. GetConsoleTitleA( window_title, MAX_PATH );
  189. void* native_window_handle = (void*)FindWindowA( NULL, window_title );
  190. #else
  191. void* native_window_handle = 0;
  192. #endif
  193. // main browser initialization
  194. bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, mProfileDir, native_window_handle );
  195. if ( result )
  196. {
  197. // create single browser window
  198. mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight );
  199. #if LL_WINDOWS
  200. // Enable plugins
  201. LLQtWebKit::getInstance()->enablePlugins(true);
  202. #elif LL_DARWIN
  203. // Enable plugins
  204. LLQtWebKit::getInstance()->enablePlugins(true);
  205. #elif LL_LINUX
  206. // Enable plugins
  207. LLQtWebKit::getInstance()->enablePlugins(true);
  208. #endif
  209. // Enable cookies
  210. LLQtWebKit::getInstance()->enableCookies( true );
  211. // tell LLQtWebKit about the size of the browser window
  212. LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight );
  213. // observer events that LLQtWebKit emits
  214. LLQtWebKit::getInstance()->addObserver( mBrowserWindowId, this );
  215. // append details to agent string
  216. LLQtWebKit::getInstance()->setBrowserAgentId( "LLPluginMedia Web Browser" );
  217. // don't flip bitmap
  218. LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true );
  219. // set background color
  220. // convert background color channels from [0.0, 1.0] to [0, 255];
  221. LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, int(mBackgroundR * 255.0f), int(mBackgroundG * 255.0f), int(mBackgroundB * 255.0f) );
  222. // Set state _before_ starting the navigate, since onNavigateBegin might get called before this call returns.
  223. setInitState(INIT_STATE_NAVIGATING);
  224. // Don't do this here -- it causes the dreaded "white flash" when loading a browser instance.
  225. // FIXME: Re-added this because navigating to a "page" initializes things correctly - especially
  226. // for the HTTP AUTH dialog issues (DEV-41731). Will fix at a later date.
  227. // Build a data URL like this: "data:text/html,%3Chtml%3E%3Cbody bgcolor=%22#RRGGBB%22%3E%3C/body%3E%3C/html%3E"
  228. // where RRGGBB is the background color in HTML style
  229. std::stringstream url;
  230. url << "data:text/html,%3Chtml%3E%3Cbody%20bgcolor=%22#";
  231. // convert background color channels from [0.0, 1.0] to [0, 255];
  232. url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundR * 255.0f);
  233. url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundG * 255.0f);
  234. url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundB * 255.0f);
  235. url << "%22%3E%3C/body%3E%3C/html%3E";
  236. lldebugs << "data url is: " << url.str() << llendl;
  237. LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, url.str() );
  238. // LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" );
  239. return true;
  240. };
  241. return false;
  242. };
  243. ////////////////////////////////////////////////////////////////////////////////
  244. // virtual
  245. void onCursorChanged(const EventType& event)
  246. {
  247. LLQtWebKit::ECursor llqt_cursor = (LLQtWebKit::ECursor)event.getIntValue();
  248. std::string name;
  249. switch(llqt_cursor)
  250. {
  251. case LLQtWebKit::C_ARROW:
  252. name = "arrow";
  253. break;
  254. case LLQtWebKit::C_IBEAM:
  255. name = "ibeam";
  256. break;
  257. case LLQtWebKit::C_SPLITV:
  258. name = "splitv";
  259. break;
  260. case LLQtWebKit::C_SPLITH:
  261. name = "splith";
  262. break;
  263. case LLQtWebKit::C_POINTINGHAND:
  264. name = "hand";
  265. break;
  266. default:
  267. llwarns << "Unknown cursor ID: " << (int)llqt_cursor << llendl;
  268. break;
  269. }
  270. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "cursor_changed");
  271. message.setValue("name", name);
  272. sendMessage(message);
  273. }
  274. ////////////////////////////////////////////////////////////////////////////////
  275. // virtual
  276. void onPageChanged( const EventType& event )
  277. {
  278. if(mInitState == INIT_STATE_WAIT_REDRAW)
  279. {
  280. setInitState(INIT_STATE_WAIT_COMPLETE);
  281. }
  282. // flag that an update is required
  283. mNeedsUpdate = true;
  284. };
  285. ////////////////////////////////////////////////////////////////////////////////
  286. // virtual
  287. void onNavigateBegin(const EventType& event)
  288. {
  289. if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
  290. {
  291. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin");
  292. message.setValue("uri", event.getEventUri());
  293. sendMessage(message);
  294. setStatus(STATUS_LOADING);
  295. }
  296. if(mInitState == INIT_STATE_NAVIGATE_COMPLETE)
  297. {
  298. // Skip the WAIT_REDRAW state now -- with the right background color set, it should no longer be necessary.
  299. // setInitState(INIT_STATE_WAIT_REDRAW);
  300. setInitState(INIT_STATE_WAIT_COMPLETE);
  301. }
  302. }
  303. ////////////////////////////////////////////////////////////////////////////////
  304. // virtual
  305. void onNavigateComplete(const EventType& event)
  306. {
  307. if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
  308. {
  309. if(mInitState < INIT_STATE_RUNNING)
  310. {
  311. setInitState(INIT_STATE_RUNNING);
  312. // Clear the history, so the "back" button doesn't take you back to "about:blank".
  313. LLQtWebKit::getInstance()->clearHistory(mBrowserWindowId);
  314. }
  315. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete");
  316. message.setValue("uri", event.getEventUri());
  317. message.setValueS32("result_code", event.getIntValue());
  318. message.setValue("result_string", event.getStringValue());
  319. message.setValueBoolean("history_back_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK));
  320. message.setValueBoolean("history_forward_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD));
  321. sendMessage(message);
  322. setStatus(STATUS_LOADED);
  323. }
  324. else if(mInitState == INIT_STATE_NAVIGATING)
  325. {
  326. setInitState(INIT_STATE_NAVIGATE_COMPLETE);
  327. }
  328. }
  329. ////////////////////////////////////////////////////////////////////////////////
  330. // virtual
  331. void onUpdateProgress(const EventType& event)
  332. {
  333. if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
  334. {
  335. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "progress");
  336. message.setValueS32("percent", event.getIntValue());
  337. sendMessage(message);
  338. }
  339. }
  340. ////////////////////////////////////////////////////////////////////////////////
  341. // virtual
  342. void onStatusTextChange(const EventType& event)
  343. {
  344. if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
  345. {
  346. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "status_text");
  347. message.setValue("status", event.getStringValue());
  348. sendMessage(message);
  349. }
  350. }
  351. ////////////////////////////////////////////////////////////////////////////////
  352. // virtual
  353. void onTitleChange(const EventType& event)
  354. {
  355. if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
  356. {
  357. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text");
  358. message.setValue("name", event.getStringValue());
  359. sendMessage(message);
  360. }
  361. }
  362. ////////////////////////////////////////////////////////////////////////////////
  363. // virtual
  364. void onLocationChange(const EventType& event)
  365. {
  366. if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
  367. {
  368. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "location_changed");
  369. message.setValue("uri", event.getEventUri());
  370. sendMessage(message);
  371. }
  372. }
  373. ////////////////////////////////////////////////////////////////////////////////
  374. // virtual
  375. void onClickLinkHref(const EventType& event)
  376. {
  377. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href");
  378. message.setValue("uri", event.getStringValue());
  379. message.setValue("target", event.getStringValue2());
  380. message.setValueU32("target_type", event.getLinkType());
  381. sendMessage(message);
  382. }
  383. ////////////////////////////////////////////////////////////////////////////////
  384. // virtual
  385. void onClickLinkNoFollow(const EventType& event)
  386. {
  387. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow");
  388. message.setValue("uri", event.getStringValue());
  389. sendMessage(message);
  390. }
  391. LLQtWebKit::EKeyboardModifier decodeModifiers(std::string &modifiers)
  392. {
  393. int result = 0;
  394. if(modifiers.find("shift") != std::string::npos)
  395. result |= LLQtWebKit::KM_MODIFIER_SHIFT;
  396. if(modifiers.find("alt") != std::string::npos)
  397. result |= LLQtWebKit::KM_MODIFIER_ALT;
  398. if(modifiers.find("control") != std::string::npos)
  399. result |= LLQtWebKit::KM_MODIFIER_CONTROL;
  400. if(modifiers.find("meta") != std::string::npos)
  401. result |= LLQtWebKit::KM_MODIFIER_META;
  402. return (LLQtWebKit::EKeyboardModifier)result;
  403. }
  404. ////////////////////////////////////////////////////////////////////////////////
  405. //
  406. void deserializeKeyboardData( LLSD native_key_data, uint32_t& native_scan_code, uint32_t& native_virtual_key, uint32_t& native_modifiers )
  407. {
  408. native_scan_code = 0;
  409. native_virtual_key = 0;
  410. native_modifiers = 0;
  411. if( native_key_data.isMap() )
  412. {
  413. #if LL_DARWIN
  414. native_scan_code = (uint32_t)(native_key_data["char_code"].asInteger());
  415. native_virtual_key = (uint32_t)(native_key_data["key_code"].asInteger());
  416. native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger());
  417. #elif LL_WINDOWS
  418. native_scan_code = (uint32_t)(native_key_data["scan_code"].asInteger());
  419. native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger());
  420. // TODO: I don't think we need to do anything with native modifiers here -- please verify
  421. #elif LL_LINUX
  422. native_scan_code = (uint32_t)(native_key_data["scan_code"].asInteger());
  423. native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger());
  424. native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger());
  425. #else
  426. // Add other platforms here as needed
  427. #endif
  428. };
  429. };
  430. ////////////////////////////////////////////////////////////////////////////////
  431. //
  432. void keyEvent(LLQtWebKit::EKeyEvent key_event, int key, LLQtWebKit::EKeyboardModifier modifiers, LLSD native_key_data = LLSD::emptyMap())
  433. {
  434. // The incoming values for 'key' will be the ones from indra_constants.h
  435. std::string utf8_text;
  436. if(key < KEY_SPECIAL)
  437. {
  438. // Low-ascii characters need to get passed through.
  439. utf8_text = (char)key;
  440. }
  441. // Any special-case handling we want to do for particular keys...
  442. switch((KEY)key)
  443. {
  444. // ASCII codes for some standard keys
  445. case LLQtWebKit::KEY_BACKSPACE: utf8_text = (char)8; break;
  446. case LLQtWebKit::KEY_TAB: utf8_text = (char)9; break;
  447. case LLQtWebKit::KEY_RETURN: utf8_text = (char)13; break;
  448. case LLQtWebKit::KEY_PAD_RETURN: utf8_text = (char)13; break;
  449. case LLQtWebKit::KEY_ESCAPE: utf8_text = (char)27; break;
  450. default:  
  451. break;
  452. }
  453. // std::cerr << "key event " << (int)key_event << ", native_key_data = " << native_key_data << std::endl;
  454. uint32_t native_scan_code = 0;
  455. uint32_t native_virtual_key = 0;
  456. uint32_t native_modifiers = 0;
  457. deserializeKeyboardData( native_key_data, native_scan_code, native_virtual_key, native_modifiers );
  458. LLQtWebKit::getInstance()->keyboardEvent( mBrowserWindowId, key_event, (uint32_t)key, utf8_text.c_str(), modifiers, native_scan_code, native_virtual_key, native_modifiers);
  459. checkEditState();
  460. };
  461. ////////////////////////////////////////////////////////////////////////////////
  462. //
  463. void unicodeInput( const std::string &utf8str, LLQtWebKit::EKeyboardModifier modifiers, LLSD native_key_data = LLSD::emptyMap())
  464. {
  465. uint32_t key = LLQtWebKit::KEY_NONE;
  466. // std::cerr << "unicode input, native_key_data = " << native_key_data << std::endl;
  467. if(utf8str.size() == 1)
  468. {
  469. // The only way a utf8 string can be one byte long is if it's actually a single 7-bit ascii character.
  470. // In this case, use it as the key value.
  471. key = utf8str[0];
  472. }
  473. uint32_t native_scan_code = 0;
  474. uint32_t native_virtual_key = 0;
  475. uint32_t native_modifiers = 0;
  476. deserializeKeyboardData( native_key_data, native_scan_code, native_virtual_key, native_modifiers );
  477. LLQtWebKit::getInstance()->keyboardEvent( mBrowserWindowId, LLQtWebKit::KE_KEY_DOWN, (uint32_t)key, utf8str.c_str(), modifiers, native_scan_code, native_virtual_key, native_modifiers);
  478. LLQtWebKit::getInstance()->keyboardEvent( mBrowserWindowId, LLQtWebKit::KE_KEY_UP, (uint32_t)key, utf8str.c_str(), modifiers, native_scan_code, native_virtual_key, native_modifiers);
  479. checkEditState();
  480. };
  481. void checkEditState(void)
  482. {
  483. bool can_cut = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT);
  484. bool can_copy = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY);
  485. bool can_paste = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE);
  486. if((can_cut != mCanCut) || (can_copy != mCanCopy) || (can_paste != mCanPaste))
  487. {
  488. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_state");
  489. if(can_cut != mCanCut)
  490. {
  491. mCanCut = can_cut;
  492. message.setValueBoolean("cut", can_cut);
  493. }
  494. if(can_copy != mCanCopy)
  495. {
  496. mCanCopy = can_copy;
  497. message.setValueBoolean("copy", can_copy);
  498. }
  499. if(can_paste != mCanPaste)
  500. {
  501. mCanPaste = can_paste;
  502. message.setValueBoolean("paste", can_paste);
  503. }
  504. sendMessage(message);
  505. }
  506. }
  507. };
  508. MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) :
  509. MediaPluginBase(host_send_func, host_user_data)
  510. {
  511. // std::cerr << "MediaPluginWebKit constructor" << std::endl;
  512. mBrowserWindowId = 0;
  513. mInitState = INIT_STATE_UNINITIALIZED;
  514. mNeedsUpdate = true;
  515. mCanCut = false;
  516. mCanCopy = false;
  517. mCanPaste = false;
  518. mLastMouseX = 0;
  519. mLastMouseY = 0;
  520. mFirstFocus = true;
  521. mBackgroundR = 0.0f;
  522. mBackgroundG = 0.0f;
  523. mBackgroundB = 0.0f;
  524. }
  525. MediaPluginWebKit::~MediaPluginWebKit()
  526. {
  527. // unhook observer
  528. LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this );
  529. // clean up
  530. LLQtWebKit::getInstance()->reset();
  531. // std::cerr << "MediaPluginWebKit destructor" << std::endl;
  532. }
  533. void MediaPluginWebKit::receiveMessage(const char *message_string)
  534. {
  535. // std::cerr << "MediaPluginWebKit::receiveMessage: received message: "" << message_string << """ << std::endl;
  536. LLPluginMessage message_in;
  537. if(message_in.parse(message_string) >= 0)
  538. {
  539. std::string message_class = message_in.getClass();
  540. std::string message_name = message_in.getName();
  541. if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE)
  542. {
  543. if(message_name == "init")
  544. {
  545. std::string user_data_path = message_in.getValue("user_data_path"); // n.b. always has trailing platform-specific dir-delimiter
  546. mProfileDir = user_data_path + "browser_profile";
  547. LLPluginMessage message("base", "init_response");
  548. LLSD versions = LLSD::emptyMap();
  549. versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
  550. versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
  551. versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION;
  552. message.setValueLLSD("versions", versions);
  553. std::string plugin_version = "Webkit media plugin, Webkit version ";
  554. plugin_version += LLQtWebKit::getInstance()->getVersion();
  555. message.setValue("plugin_version", plugin_version);
  556. sendMessage(message);
  557. // Plugin gets to decide the texture parameters to use.
  558. mDepth = 4;
  559. message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params");
  560. message.setValueS32("default_width", 1024);
  561. message.setValueS32("default_height", 1024);
  562. message.setValueS32("depth", mDepth);
  563. message.setValueU32("internalformat", GL_RGBA);
  564. message.setValueU32("format", GL_RGBA);
  565. message.setValueU32("type", GL_UNSIGNED_BYTE);
  566. message.setValueBoolean("coords_opengl", true);
  567. sendMessage(message);
  568. }
  569. else if(message_name == "idle")
  570. {
  571. // no response is necessary here.
  572. F64 time = message_in.getValueReal("time");
  573. // Convert time to milliseconds for update()
  574. update((int)(time * 1000.0f));
  575. }
  576. else if(message_name == "cleanup")
  577. {
  578. // DTOR most likely won't be called but the recent change to the way this process
  579. // is (not) killed means we see this message and can do what we need to here.
  580. // Note: this cleanup is ultimately what writes cookies to the disk
  581. LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this );
  582. LLQtWebKit::getInstance()->reset();
  583. }
  584. else if(message_name == "shm_added")
  585. {
  586. SharedSegmentInfo info;
  587. info.mAddress = message_in.getValuePointer("address");
  588. info.mSize = (size_t)message_in.getValueS32("size");
  589. std::string name = message_in.getValue("name");
  590. // std::cerr << "MediaPluginWebKit::receiveMessage: shared memory added, name: " << name 
  591. // << ", size: " << info.mSize 
  592. // << ", address: " << info.mAddress 
  593. // << std::endl;
  594. mSharedSegments.insert(SharedSegmentMap::value_type(name, info));
  595. }
  596. else if(message_name == "shm_remove")
  597. {
  598. std::string name = message_in.getValue("name");
  599. // std::cerr << "MediaPluginWebKit::receiveMessage: shared memory remove, name = " << name << std::endl;
  600. SharedSegmentMap::iterator iter = mSharedSegments.find(name);
  601. if(iter != mSharedSegments.end())
  602. {
  603. if(mPixels == iter->second.mAddress)
  604. {
  605. // This is the currently active pixel buffer.  Make sure we stop drawing to it.
  606. mPixels = NULL;
  607. mTextureSegmentName.clear();
  608. }
  609. mSharedSegments.erase(iter);
  610. }
  611. else
  612. {
  613. // std::cerr << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl;
  614. }
  615. // Send the response so it can be cleaned up.
  616. LLPluginMessage message("base", "shm_remove_response");
  617. message.setValue("name", name);
  618. sendMessage(message);
  619. }
  620. else
  621. {
  622. // std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl;
  623. }
  624. }
  625. else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
  626. {
  627. if(message_name == "size_change")
  628. {
  629. std::string name = message_in.getValue("name");
  630. S32 width = message_in.getValueS32("width");
  631. S32 height = message_in.getValueS32("height");
  632. S32 texture_width = message_in.getValueS32("texture_width");
  633. S32 texture_height = message_in.getValueS32("texture_height");
  634. mBackgroundR = message_in.getValueReal("background_r");
  635. mBackgroundG = message_in.getValueReal("background_g");
  636. mBackgroundB = message_in.getValueReal("background_b");
  637. // mBackgroundA = message_in.setValueReal("background_a"); // Ignore any alpha
  638. if(!name.empty())
  639. {
  640. // Find the shared memory region with this name
  641. SharedSegmentMap::iterator iter = mSharedSegments.find(name);
  642. if(iter != mSharedSegments.end())
  643. {
  644. mPixels = (unsigned char*)iter->second.mAddress;
  645. mWidth = width;
  646. mHeight = height;
  647. // initialize (only gets called once)
  648. initBrowser();
  649. // size changed so tell the browser
  650. LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight );
  651. // std::cerr << "webkit plugin: set size to " << mWidth << " x " << mHeight 
  652. // << ", rowspan is " << LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl;
  653. S32 real_width = LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) / LLQtWebKit::getInstance()->getBrowserDepth(mBrowserWindowId); 
  654. // The actual width the browser will be drawing to is probably smaller... let the host know by modifying texture_width in the response.
  655. if(real_width <= texture_width)
  656. {
  657. texture_width = real_width;
  658. }
  659. else
  660. {
  661. // This won't work -- it'll be bigger than the allocated memory.  This is a fatal error.
  662. // std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl;
  663. mDeleteMe = true;
  664. return;
  665. }
  666. mTextureWidth = texture_width;
  667. mTextureHeight = texture_height;
  668. };
  669. };
  670. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response");
  671. message.setValue("name", name);
  672. message.setValueS32("width", width);
  673. message.setValueS32("height", height);
  674. message.setValueS32("texture_width", texture_width);
  675. message.setValueS32("texture_height", texture_height);
  676. sendMessage(message);
  677. }
  678. else if(message_name == "load_uri")
  679. {
  680. std::string uri = message_in.getValue("uri");
  681. // std::cout << "loading URI: " << uri << std::endl;
  682. if(!uri.empty())
  683. {
  684. if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
  685. {
  686. LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, uri );
  687. }
  688. else
  689. {
  690. mInitialNavigateURL = uri;
  691. }
  692. }
  693. }
  694. else if(message_name == "mouse_event")
  695. {
  696. std::string event = message_in.getValue("event");
  697. S32 button = message_in.getValueS32("button");
  698. mLastMouseX = message_in.getValueS32("x");
  699. mLastMouseY = message_in.getValueS32("y");
  700. std::string modifiers = message_in.getValue("modifiers");
  701. // Treat unknown mouse events as mouse-moves.
  702. LLQtWebKit::EMouseEvent mouse_event = LLQtWebKit::ME_MOUSE_MOVE;
  703. if(event == "down")
  704. {
  705. mouse_event = LLQtWebKit::ME_MOUSE_DOWN;
  706. }
  707. else if(event == "up")
  708. {
  709. mouse_event = LLQtWebKit::ME_MOUSE_UP;
  710. }
  711. else if(event == "double_click")
  712. {
  713. mouse_event = LLQtWebKit::ME_MOUSE_DOUBLE_CLICK;
  714. }
  715. LLQtWebKit::getInstance()->mouseEvent( mBrowserWindowId, mouse_event, button, mLastMouseX, mLastMouseY, decodeModifiers(modifiers));
  716. checkEditState();
  717. }
  718. else if(message_name == "scroll_event")
  719. {
  720. S32 x = message_in.getValueS32("x");
  721. S32 y = message_in.getValueS32("y");
  722. std::string modifiers = message_in.getValue("modifiers");
  723. // Incoming scroll events are adjusted so that 1 detent is approximately 1 unit.
  724. // Qt expects 1 detent to be 120 units.
  725. // It also seems that our y scroll direction is inverted vs. what Qt expects.
  726. x *= 120;
  727. y *= -120;
  728. LLQtWebKit::getInstance()->scrollWheelEvent(mBrowserWindowId, mLastMouseX, mLastMouseY, x, y, decodeModifiers(modifiers));
  729. }
  730. else if(message_name == "key_event")
  731. {
  732. std::string event = message_in.getValue("event");
  733. S32 key = message_in.getValueS32("key");
  734. std::string modifiers = message_in.getValue("modifiers");
  735. LLSD native_key_data = message_in.getValueLLSD("native_key_data");
  736. // Treat unknown events as key-up for safety.
  737. LLQtWebKit::EKeyEvent key_event = LLQtWebKit::KE_KEY_UP;
  738. if(event == "down")
  739. {
  740. key_event = LLQtWebKit::KE_KEY_DOWN;
  741. }
  742. else if(event == "repeat")
  743. {
  744. key_event = LLQtWebKit::KE_KEY_REPEAT;
  745. }
  746. keyEvent(key_event, key, decodeModifiers(modifiers), native_key_data);
  747. }
  748. else if(message_name == "text_event")
  749. {
  750. std::string text = message_in.getValue("text");
  751. std::string modifiers = message_in.getValue("modifiers");
  752. LLSD native_key_data = message_in.getValueLLSD("native_key_data");
  753. unicodeInput(text, decodeModifiers(modifiers), native_key_data);
  754. }
  755. if(message_name == "edit_cut")
  756. {
  757. LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT );
  758. checkEditState();
  759. }
  760. if(message_name == "edit_copy")
  761. {
  762. LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY );
  763. checkEditState();
  764. }
  765. if(message_name == "edit_paste")
  766. {
  767. LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE );
  768. checkEditState();
  769. }
  770. else
  771. {
  772. // std::cerr << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl;
  773. };
  774. }
  775. else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER)
  776. {
  777. if(message_name == "focus")
  778. {
  779. bool val = message_in.getValueBoolean("focused");
  780. LLQtWebKit::getInstance()->focusBrowser( mBrowserWindowId, val );
  781. if(mFirstFocus && val)
  782. {
  783. // On the first focus, post a tab key event.  This fixes a problem with initial focus.
  784. std::string empty;
  785. keyEvent(LLQtWebKit::KE_KEY_DOWN, KEY_TAB, decodeModifiers(empty));
  786. keyEvent(LLQtWebKit::KE_KEY_UP, KEY_TAB, decodeModifiers(empty));
  787. mFirstFocus = false;
  788. }
  789. }
  790. else if(message_name == "clear_cache")
  791. {
  792. LLQtWebKit::getInstance()->clearCache();
  793. }
  794. else if(message_name == "clear_cookies")
  795. {
  796. LLQtWebKit::getInstance()->clearAllCookies();
  797. }
  798. else if(message_name == "enable_cookies")
  799. {
  800. bool val = message_in.getValueBoolean("enable");
  801. LLQtWebKit::getInstance()->enableCookies( val );
  802. }
  803. else if(message_name == "proxy_setup")
  804. {
  805. bool val = message_in.getValueBoolean("enable");
  806. std::string host = message_in.getValue("host");
  807. int port = message_in.getValueS32("port");
  808. LLQtWebKit::getInstance()->enableProxy( val, host, port );
  809. }
  810. else if(message_name == "browse_stop")
  811. {
  812. LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_STOP );
  813. }
  814. else if(message_name == "browse_reload")
  815. {
  816. // foo = message_in.getValueBoolean("ignore_cache");
  817. LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_RELOAD );
  818. }
  819. else if(message_name == "browse_forward")
  820. {
  821. LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD );
  822. }
  823. else if(message_name == "browse_back")
  824. {
  825. LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK );
  826. }
  827. else if(message_name == "set_status_redirect")
  828. {
  829. int code = message_in.getValueS32("code");
  830. std::string url = message_in.getValue("url");
  831. if ( 404 == code ) // browser lib only supports 404 right now
  832. {
  833. LLQtWebKit::getInstance()->set404RedirectUrl( mBrowserWindowId, url );
  834. };
  835. }
  836. else if(message_name == "set_user_agent")
  837. {
  838. std::string user_agent = message_in.getValue("user_agent");
  839. LLQtWebKit::getInstance()->setBrowserAgentId( user_agent );
  840. }
  841. else if(message_name == "init_history")
  842. {
  843. // Initialize browser history
  844. LLSD history = message_in.getValueLLSD("history");
  845. // First, clear the URL history
  846. LLQtWebKit::getInstance()->clearHistory(mBrowserWindowId);
  847. // Then, add the history items in order
  848. LLSD::array_iterator iter_history = history.beginArray();
  849. LLSD::array_iterator end_history = history.endArray();
  850. for(; iter_history != end_history; ++iter_history)
  851. {
  852. std::string url = (*iter_history).asString();
  853. if(! url.empty()) {
  854. LLQtWebKit::getInstance()->prependHistoryUrl(mBrowserWindowId, url);
  855. }
  856. }
  857. }
  858. else
  859. {
  860. // std::cerr << "MediaPluginWebKit::receiveMessage: unknown media_browser message: " << message_string << std::endl;
  861. };
  862. }
  863. else
  864. {
  865. // std::cerr << "MediaPluginWebKit::receiveMessage: unknown message class: " << message_class << std::endl;
  866. };
  867. }
  868. }
  869. int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
  870. {
  871. MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data);
  872. *plugin_send_func = MediaPluginWebKit::staticReceiveMessage;
  873. *plugin_user_data = (void*)self;
  874. return 0;
  875. }