PageRenderTime 111ms CodeModel.GetById 16ms app.highlight 83ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/media_plugins/webkit/media_plugin_webkit.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1481 lines | 1088 code | 188 blank | 205 comment | 249 complexity | 6b7a9d3e83388018b8532dc550608c4c MD5 | raw file
   1/** 
   2 * @file media_plugin_webkit.cpp
   3 * @brief Webkit plugin for LLMedia API plugin system
   4 *
   5 * @cond
   6 * $LicenseInfo:firstyear=2008&license=viewerlgpl$
   7 * Second Life Viewer Source Code
   8 * Copyright (C) 2010, Linden Research, Inc.
   9 * 
  10 * This library is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU Lesser General Public
  12 * License as published by the Free Software Foundation;
  13 * version 2.1 of the License only.
  14 * 
  15 * This library is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * Lesser General Public License for more details.
  19 * 
  20 * You should have received a copy of the GNU Lesser General Public
  21 * License along with this library; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  23 * 
  24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  25 * $/LicenseInfo$
  26 * @endcond
  27 */
  28#include "llqtwebkit.h"
  29#include "linden_common.h"
  30#include "indra_constants.h" // for indra keyboard codes
  31
  32#include "lltimer.h"
  33#include "llgl.h"
  34
  35#include "llplugininstance.h"
  36#include "llpluginmessage.h"
  37#include "llpluginmessageclasses.h"
  38#include "media_plugin_base.h"
  39
  40// set to 1 if you're using the version of llqtwebkit that's QPixmap-ified
  41#if LL_LINUX
  42# define LL_QTWEBKIT_USES_PIXMAPS 0
  43extern "C" {
  44# include <glib.h>
  45# include <glib-object.h>
  46}
  47#else
  48# define LL_QTWEBKIT_USES_PIXMAPS 0
  49#endif // LL_LINUX
  50
  51# include "volume_catcher.h"
  52
  53#if LL_WINDOWS
  54# include <direct.h>
  55#else
  56# include <unistd.h>
  57# include <stdlib.h>
  58#endif
  59
  60#if LL_WINDOWS
  61	// *NOTE:Mani - This captures the module handle for the dll. This is used below
  62	// to get the path to this dll for webkit initialization.
  63	// I don't know how/if this can be done with apr...
  64	namespace {	HMODULE gModuleHandle;};
  65	BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
  66	{
  67		gModuleHandle = (HMODULE) hinstDLL;
  68		return TRUE;
  69	}
  70#endif
  71
  72////////////////////////////////////////////////////////////////////////////////
  73//
  74class MediaPluginWebKit : 
  75		public MediaPluginBase,
  76		public LLEmbeddedBrowserWindowObserver
  77{
  78public:
  79	MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
  80	~MediaPluginWebKit();
  81
  82	/*virtual*/ void receiveMessage(const char *message_string);
  83
  84private:
  85
  86	std::string mProfileDir;
  87	std::string mHostLanguage;
  88	std::string mUserAgent;
  89	bool mCookiesEnabled;
  90	bool mJavascriptEnabled;
  91	bool mPluginsEnabled;
  92	bool mEnableMediaPluginDebugging;
  93
  94	enum
  95	{
  96		INIT_STATE_UNINITIALIZED,		// LLQtWebkit hasn't been set up yet
  97		INIT_STATE_INITIALIZED,			// LLQtWebkit has been set up, but no browser window has been created yet.
  98		INIT_STATE_NAVIGATING,			// Browser instance has been set up and initial navigate to about:blank has been issued
  99		INIT_STATE_NAVIGATE_COMPLETE,	// initial navigate to about:blank has completed
 100		INIT_STATE_WAIT_REDRAW,			// First real navigate begin has been received, waiting for page changed event to start handling redraws
 101		INIT_STATE_WAIT_COMPLETE,		// Waiting for first real navigate complete event
 102		INIT_STATE_RUNNING				// All initialization gymnastics are complete.
 103	};
 104	int mBrowserWindowId;
 105	int mInitState;
 106	std::string mInitialNavigateURL;
 107	bool mNeedsUpdate;
 108
 109	bool	mCanCut;
 110	bool	mCanCopy;
 111	bool	mCanPaste;
 112	int mLastMouseX;
 113	int mLastMouseY;
 114	bool mFirstFocus;
 115	F32 mBackgroundR;
 116	F32 mBackgroundG;
 117	F32 mBackgroundB;
 118	std::string mTarget;
 119	LLTimer mElapsedTime;
 120		
 121	VolumeCatcher mVolumeCatcher;
 122
 123	void postDebugMessage( const std::string& msg )
 124	{
 125		if ( mEnableMediaPluginDebugging )
 126		{
 127			std::stringstream str;
 128			str << "@Media Msg> " << "[" << (double)mElapsedTime.getElapsedTimeF32()  << "] -- " << msg;
 129
 130			LLPluginMessage debug_message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "debug_message");
 131			debug_message.setValue("message_text", str.str());
 132			debug_message.setValue("message_level", "info");
 133			sendMessage(debug_message);
 134		}
 135	}
 136	
 137	void setInitState(int state)
 138	{
 139//		std::cerr << "changing init state to " << state << std::endl;
 140		mInitState = state;
 141	}
 142	
 143	////////////////////////////////////////////////////////////////////////////////
 144	//
 145	void update(int milliseconds)
 146	{
 147#if LL_QTLINUX_DOESNT_HAVE_GLIB
 148		// pump glib generously, as Linux browser plugins are on the
 149		// glib main loop, even if the browser itself isn't - ugh
 150		// This is NOT NEEDED if Qt itself was built with glib
 151		// mainloop integration.
 152		GMainContext *mainc = g_main_context_default();
 153		while(g_main_context_iteration(mainc, FALSE));
 154#endif // LL_QTLINUX_DOESNT_HAVE_GLIB
 155
 156		// pump qt
 157		LLQtWebKit::getInstance()->pump( milliseconds );
 158		
 159		mVolumeCatcher.pump();
 160
 161		checkEditState();
 162		
 163		if(mInitState == INIT_STATE_NAVIGATE_COMPLETE)
 164		{
 165			if(!mInitialNavigateURL.empty())
 166			{
 167				// We already have the initial navigate URL -- kick off the navigate.
 168				LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, mInitialNavigateURL );
 169				mInitialNavigateURL.clear();
 170			}
 171		}
 172		
 173		if ( (mInitState > INIT_STATE_WAIT_REDRAW) && mNeedsUpdate )
 174		{
 175			const unsigned char* browser_pixels = LLQtWebKit::getInstance()->grabBrowserWindow( mBrowserWindowId );
 176
 177			unsigned int rowspan = LLQtWebKit::getInstance()->getBrowserRowSpan( mBrowserWindowId );
 178			unsigned int height = LLQtWebKit::getInstance()->getBrowserHeight( mBrowserWindowId );
 179#if !LL_QTWEBKIT_USES_PIXMAPS
 180			unsigned int buffer_size = rowspan * height;
 181#endif // !LL_QTWEBKIT_USES_PIXMAPS
 182			
 183//			std::cerr << "webkit plugin: updating" << std::endl;
 184			
 185			// TODO: should get rid of this memcpy if possible
 186			if ( mPixels && browser_pixels )
 187			{
 188//				std::cerr << "    memcopy of " << buffer_size << " bytes" << std::endl;
 189
 190#if LL_QTWEBKIT_USES_PIXMAPS
 191				// copy the pixel data upside-down because of the co-ord system
 192				for (int y=0; y<height; ++y)
 193				{
 194					memcpy( &mPixels[(height-y-1)*rowspan], &browser_pixels[y*rowspan], rowspan );
 195				}
 196#else
 197				memcpy( mPixels, browser_pixels, buffer_size );
 198#endif // LL_QTWEBKIT_USES_PIXMAPS
 199			}
 200
 201			if ( mWidth > 0 && mHeight > 0 )
 202			{
 203//				std::cerr << "Setting dirty, " << mWidth << " x " << mHeight << std::endl;
 204				setDirty( 0, 0, mWidth, mHeight );
 205			}
 206
 207			mNeedsUpdate = false;
 208		};
 209	};
 210
 211	////////////////////////////////////////////////////////////////////////////////
 212	//
 213	bool initBrowser()
 214	{
 215		// already initialized
 216		if ( mInitState > INIT_STATE_UNINITIALIZED )
 217			return true;
 218
 219		// set up directories
 220		char cwd[ FILENAME_MAX ];	// I *think* this is defined on all platforms we use
 221		if (NULL == getcwd( cwd, FILENAME_MAX - 1 ))
 222		{
 223			llwarns << "Couldn't get cwd - probably too long - failing to init." << llendl;
 224			return false;
 225		}
 226		std::string application_dir = std::string( cwd );
 227
 228#if LL_LINUX
 229		// take care to initialize glib properly, because some
 230		// versions of Qt don't, and we indirectly need it for (some
 231		// versions of) Flash to not crash the browser.
 232		if (!g_thread_supported ()) g_thread_init (NULL);
 233		g_type_init();
 234#endif
 235
 236#if LL_DARWIN
 237		// When running under the Xcode debugger, there's a setting called "Break on Debugger()/DebugStr()" which defaults to being turned on.
 238		// This causes the environment variable USERBREAK to be set to 1, which causes these legacy calls to break into the debugger.
 239		// 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
 240		// which gets hit when the plugin is probed by webkit.
 241		// Unsetting the environment variable here works around this issue.
 242		unsetenv("USERBREAK");
 243#endif
 244
 245#if LL_WINDOWS
 246		//*NOTE:Mani - On windows, at least, the component path is the
 247		// location of this dll's image file. 
 248		std::string component_dir;
 249		char dll_path[_MAX_PATH];
 250		DWORD len = GetModuleFileNameA(gModuleHandle, (LPCH)&dll_path, _MAX_PATH);
 251		while(len && dll_path[ len ] != ('\\') )
 252		{
 253			len--;
 254		}
 255		if(len >= 0)
 256		{
 257			dll_path[len] = 0;
 258			component_dir = dll_path;
 259		}
 260		else
 261		{
 262			// *NOTE:Mani - This case should be an rare exception. 
 263			// GetModuleFileNameA should always give you a full path, no?
 264			component_dir = application_dir;
 265		}
 266#else
 267		std::string component_dir = application_dir;
 268#endif
 269
 270		// debug spam sent to viewer and displayed in the log as usual
 271		postDebugMessage( "Component dir set to: " + component_dir );
 272
 273		// window handle - needed on Windows and must be app window.
 274#if LL_WINDOWS
 275		char window_title[ MAX_PATH ];
 276		GetConsoleTitleA( window_title, MAX_PATH );
 277		void* native_window_handle = (void*)FindWindowA( NULL, window_title );
 278#else
 279		void* native_window_handle = 0;
 280#endif
 281
 282		// main browser initialization
 283		bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, mProfileDir, native_window_handle );
 284		if ( result )
 285		{
 286			mInitState = INIT_STATE_INITIALIZED;
 287
 288			// debug spam sent to viewer and displayed in the log as usual
 289			postDebugMessage( "browser initialized okay" );
 290
 291			return true;
 292		};
 293
 294		// debug spam sent to viewer and displayed in the log as usual
 295		postDebugMessage( "browser nOT initialized." );
 296
 297		return false;
 298	};
 299
 300	////////////////////////////////////////////////////////////////////////////////
 301	//
 302	bool initBrowserWindow()
 303	{
 304		// already initialized
 305		if ( mInitState > INIT_STATE_INITIALIZED )
 306			return true;
 307
 308		// not enough information to initialize the browser yet.
 309		if ( mWidth < 0 || mHeight < 0 || mDepth < 0 || 
 310				mTextureWidth < 0 || mTextureHeight < 0 )
 311		{
 312			return false;
 313		};
 314		
 315		// Set up host language before creating browser window
 316		if(!mHostLanguage.empty())
 317		{
 318			LLQtWebKit::getInstance()->setHostLanguage(mHostLanguage);
 319			postDebugMessage( "Setting language to " + mHostLanguage );
 320		}
 321
 322		// turn on/off cookies based on what host app tells us
 323		LLQtWebKit::getInstance()->enableCookies( mCookiesEnabled );
 324		
 325		// turn on/off plugins based on what host app tells us
 326		LLQtWebKit::getInstance()->enablePlugins( mPluginsEnabled );
 327
 328		// turn on/off Javascript based on what host app tells us
 329#if LLQTWEBKIT_API_VERSION >= 11
 330		LLQtWebKit::getInstance()->enableJavaScript( mJavascriptEnabled );
 331#else
 332		LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled );
 333#endif
 334
 335		std::stringstream str;
 336		str << "Cookies enabled = " << mCookiesEnabled << ", plugins enabled = " << mPluginsEnabled << ", Javascript enabled = " << mJavascriptEnabled;
 337		postDebugMessage( str.str() );
 338
 339		// create single browser window
 340		mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight, mTarget);
 341
 342		str.str("");
 343		str.clear();
 344		str << "Setting browser window size to " << mWidth << " x " << mHeight;
 345		postDebugMessage( str.str() );
 346
 347		// tell LLQtWebKit about the size of the browser window
 348		LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight );
 349
 350		// observer events that LLQtWebKit emits
 351		LLQtWebKit::getInstance()->addObserver( mBrowserWindowId, this );
 352
 353		// append details to agent string
 354		LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent );
 355		postDebugMessage( "Updating user agent with " + mUserAgent );
 356
 357#if !LL_QTWEBKIT_USES_PIXMAPS
 358		// don't flip bitmap
 359		LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true );
 360#endif // !LL_QTWEBKIT_USES_PIXMAPS
 361
 362		// set background color
 363		// convert background color channels from [0.0, 1.0] to [0, 255];
 364		LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, int(mBackgroundR * 255.0f), int(mBackgroundG * 255.0f), int(mBackgroundB * 255.0f) );
 365
 366		// Set state _before_ starting the navigate, since onNavigateBegin might get called before this call returns.
 367		setInitState(INIT_STATE_NAVIGATING);
 368
 369		// Don't do this here -- it causes the dreaded "white flash" when loading a browser instance.
 370		// FIXME: Re-added this because navigating to a "page" initializes things correctly - especially
 371		// for the HTTP AUTH dialog issues (DEV-41731). Will fix at a later date.
 372		// Build a data URL like this: "data:text/html,%3Chtml%3E%3Cbody bgcolor=%22#RRGGBB%22%3E%3C/body%3E%3C/html%3E"
 373		// where RRGGBB is the background color in HTML style
 374		std::stringstream url;
 375		
 376		url << "data:text/html,%3Chtml%3E%3Cbody%20bgcolor=%22#";
 377		// convert background color channels from [0.0, 1.0] to [0, 255];
 378		url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundR * 255.0f);
 379		url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundG * 255.0f);
 380		url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundB * 255.0f);
 381		url << "%22%3E%3C/body%3E%3C/html%3E";
 382		
 383		//lldebugs << "data url is: " << url.str() << llendl;
 384
 385		// always display loading overlay now
 386#if LLQTWEBKIT_API_VERSION >= 16
 387		LLQtWebKit::getInstance()->enableLoadingOverlay(mBrowserWindowId, true);
 388#else
 389		llwarns << "Ignoring enableLoadingOverlay() call (llqtwebkit version is too old)." << llendl;
 390#endif
 391		str.clear();
 392		str << "Loading overlay enabled = " << mEnableMediaPluginDebugging << " for mBrowserWindowId = " << mBrowserWindowId;
 393		postDebugMessage( str.str() );
 394
 395		LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, url.str() );
 396//		LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" );
 397
 398		return true;	
 399	}
 400
 401	void setVolume(F32 vol);
 402
 403	////////////////////////////////////////////////////////////////////////////////
 404	// virtual
 405	void onCursorChanged(const EventType& event)
 406	{
 407		LLQtWebKit::ECursor llqt_cursor = (LLQtWebKit::ECursor)event.getIntValue();
 408		std::string name;
 409
 410		switch(llqt_cursor)
 411		{
 412			case LLQtWebKit::C_ARROW:
 413				name = "arrow";
 414			break;
 415			case LLQtWebKit::C_IBEAM:
 416				name = "ibeam";
 417			break;
 418			case LLQtWebKit::C_SPLITV:
 419				name = "splitv";
 420			break;
 421			case LLQtWebKit::C_SPLITH:
 422				name = "splith";
 423			break;
 424			case LLQtWebKit::C_POINTINGHAND:
 425				name = "hand";
 426			break;
 427			
 428			default:
 429				llwarns << "Unknown cursor ID: " << (int)llqt_cursor << llendl;
 430			break;
 431		}
 432		
 433		LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "cursor_changed");
 434		message.setValue("name", name);
 435		sendMessage(message);
 436	}
 437
 438	////////////////////////////////////////////////////////////////////////////////
 439	// virtual
 440	void onPageChanged( const EventType& event )
 441	{
 442		if(mInitState == INIT_STATE_WAIT_REDRAW)
 443		{
 444			setInitState(INIT_STATE_WAIT_COMPLETE);
 445		}
 446		
 447		// flag that an update is required
 448		mNeedsUpdate = true;
 449	};
 450
 451	////////////////////////////////////////////////////////////////////////////////
 452	// virtual
 453	void onNavigateBegin(const EventType& event)
 454	{
 455		if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
 456		{
 457			LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin");
 458			message.setValue("uri", event.getEventUri());
 459			message.setValueBoolean("history_back_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK));
 460			message.setValueBoolean("history_forward_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD));
 461			sendMessage(message);
 462
 463			// debug spam sent to viewer and displayed in the log as usual
 464			postDebugMessage( "Navigate begin event at: " + event.getEventUri() );
 465
 466			setStatus(STATUS_LOADING);
 467		}
 468
 469		if(mInitState == INIT_STATE_NAVIGATE_COMPLETE)
 470		{
 471			// Skip the WAIT_REDRAW state now -- with the right background color set, it should no longer be necessary.
 472//			setInitState(INIT_STATE_WAIT_REDRAW);
 473			setInitState(INIT_STATE_WAIT_COMPLETE);
 474		}
 475		
 476	}
 477
 478	////////////////////////////////////////////////////////////////////////////////
 479	// virtual
 480	void onNavigateComplete(const EventType& event)
 481	{
 482		if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
 483		{
 484			if(mInitState < INIT_STATE_RUNNING)
 485			{
 486				setInitState(INIT_STATE_RUNNING);
 487				
 488				// Clear the history, so the "back" button doesn't take you back to "about:blank".
 489				LLQtWebKit::getInstance()->clearHistory(mBrowserWindowId);
 490			}
 491			
 492			LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete");
 493			message.setValue("uri", event.getEventUri());
 494			message.setValueS32("result_code", event.getIntValue());
 495			message.setValue("result_string", event.getStringValue());
 496			message.setValueBoolean("history_back_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK));
 497			message.setValueBoolean("history_forward_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD));
 498			sendMessage(message);
 499			
 500			setStatus(STATUS_LOADED);
 501		}
 502		else if(mInitState == INIT_STATE_NAVIGATING)
 503		{
 504			setInitState(INIT_STATE_NAVIGATE_COMPLETE);
 505		}
 506
 507		// debug spam sent to viewer and displayed in the log as usual
 508		postDebugMessage( "Navigate complete event at: " + event.getEventUri() );
 509	}
 510
 511	////////////////////////////////////////////////////////////////////////////////
 512	// virtual
 513	void onUpdateProgress(const EventType& event)
 514	{
 515		if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
 516		{
 517			LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "progress");
 518			message.setValueS32("percent", event.getIntValue());
 519			sendMessage(message);
 520		}
 521	}
 522	
 523	////////////////////////////////////////////////////////////////////////////////
 524	// virtual
 525	void onStatusTextChange(const EventType& event)
 526	{
 527		if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
 528		{
 529			LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "status_text");
 530			message.setValue("status", event.getStringValue());
 531			sendMessage(message);
 532		}
 533	}
 534
 535	////////////////////////////////////////////////////////////////////////////////
 536	// virtual
 537	void onTitleChange(const EventType& event)
 538	{
 539		if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
 540		{
 541			LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text");
 542			message.setValue("name", event.getStringValue());
 543			sendMessage(message);
 544		}
 545	}
 546
 547	////////////////////////////////////////////////////////////////////////////////
 548	// virtual
 549	void onNavigateErrorPage(const EventType& event)
 550	{
 551		LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_error_page");
 552		message.setValueS32("status_code", event.getIntValue());
 553		sendMessage(message);
 554	}
 555	
 556	////////////////////////////////////////////////////////////////////////////////
 557	// virtual
 558	void onLocationChange(const EventType& event)
 559	{
 560		if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
 561		{
 562			LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "location_changed");
 563			message.setValue("uri", event.getEventUri());
 564			sendMessage(message);
 565		}
 566	}
 567	
 568	////////////////////////////////////////////////////////////////////////////////
 569	// virtual
 570	void onClickLinkHref(const EventType& event)
 571	{
 572		LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href");
 573		message.setValue("uri", event.getEventUri());
 574		message.setValue("target", event.getStringValue());
 575		message.setValue("uuid", event.getStringValue2());
 576		sendMessage(message);
 577	}
 578	
 579	////////////////////////////////////////////////////////////////////////////////
 580	// virtual
 581	void onClickLinkNoFollow(const EventType& event)
 582	{
 583		LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow");
 584		message.setValue("uri", event.getEventUri());
 585#if LLQTWEBKIT_API_VERSION >= 7
 586		message.setValue("nav_type", event.getNavigationType());
 587#else
 588		message.setValue("nav_type", "clicked");
 589#endif
 590		sendMessage(message);
 591	}
 592	
 593
 594	////////////////////////////////////////////////////////////////////////////////
 595	// virtual
 596	void onCookieChanged(const EventType& event)
 597	{
 598		LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "cookie_set");
 599		message.setValue("cookie", event.getStringValue());
 600		// These could be passed through as well, but aren't really needed.
 601//		message.setValue("uri", event.getEventUri());
 602//		message.setValueBoolean("dead", (event.getIntValue() != 0))
 603
 604		// debug spam
 605		postDebugMessage( "Sending cookie_set message from plugin: " + event.getStringValue() );
 606
 607		sendMessage(message);
 608	}
 609
 610	////////////////////////////////////////////////////////////////////////////////
 611	// virtual
 612	void onWindowCloseRequested(const EventType& event)
 613	{
 614		LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "close_request");
 615		message.setValue("uuid", event.getStringValue());
 616		sendMessage(message);
 617	}
 618
 619	////////////////////////////////////////////////////////////////////////////////
 620	// virtual
 621	void onWindowGeometryChangeRequested(const EventType& event)
 622	{
 623		int x, y, width, height;
 624		event.getRectValue(x, y, width, height);
 625
 626		// This sometimes gets called with a zero-size request.  Don't pass these along.
 627		if(width > 0 && height > 0)
 628		{
 629			LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "geometry_change");
 630			message.setValue("uuid", event.getStringValue());
 631			message.setValueS32("x", x);
 632			message.setValueS32("y", y);
 633			message.setValueS32("width", width);
 634			message.setValueS32("height", height);
 635			sendMessage(message);
 636		}
 637	}
 638
 639	////////////////////////////////////////////////////////////////////////////////
 640	// virtual
 641	std::string onRequestFilePicker( const EventType& eventIn )
 642	{
 643		return blockingPickFile();
 644	}
 645	
 646	std::string mAuthUsername;
 647	std::string mAuthPassword;
 648	bool mAuthOK;
 649	
 650	////////////////////////////////////////////////////////////////////////////////
 651	// virtual
 652	bool onAuthRequest(const std::string &in_url, const std::string &in_realm, std::string &out_username, std::string &out_password)
 653	{
 654		mAuthOK = false;
 655
 656		LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "auth_request");
 657		message.setValue("url", in_url);
 658		message.setValue("realm", in_realm);
 659		message.setValueBoolean("blocking_request", true);
 660				
 661		// The "blocking_request" key in the message means this sendMessage call will block until a response is received.
 662		sendMessage(message);
 663		
 664		if(mAuthOK)
 665		{
 666			out_username = mAuthUsername;
 667			out_password = mAuthPassword;
 668		}
 669		
 670		return mAuthOK;
 671	}
 672	
 673	void authResponse(LLPluginMessage &message)
 674	{
 675		mAuthOK = message.getValueBoolean("ok");
 676		if(mAuthOK)
 677		{
 678			mAuthUsername = message.getValue("username");
 679			mAuthPassword = message.getValue("password");
 680		}
 681	}
 682	
 683	////////////////////////////////////////////////////////////////////////////////
 684	// virtual
 685	void onLinkHovered(const EventType& event)
 686	{
 687		if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
 688		{
 689			LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "link_hovered");
 690			message.setValue("link", event.getEventUri());
 691			message.setValue("title", event.getStringValue());
 692			message.setValue("text", event.getStringValue2());
 693			sendMessage(message);
 694		}
 695	}
 696	
 697	LLQtWebKit::EKeyboardModifier decodeModifiers(std::string &modifiers)
 698	{
 699		int result = 0;
 700		
 701		if(modifiers.find("shift") != std::string::npos)
 702			result |= LLQtWebKit::KM_MODIFIER_SHIFT;
 703
 704		if(modifiers.find("alt") != std::string::npos)
 705			result |= LLQtWebKit::KM_MODIFIER_ALT;
 706		
 707		if(modifiers.find("control") != std::string::npos)
 708			result |= LLQtWebKit::KM_MODIFIER_CONTROL;
 709		
 710		if(modifiers.find("meta") != std::string::npos)
 711			result |= LLQtWebKit::KM_MODIFIER_META;
 712		
 713		return (LLQtWebKit::EKeyboardModifier)result;
 714	}
 715	
 716	////////////////////////////////////////////////////////////////////////////////
 717	//
 718	void deserializeKeyboardData( LLSD native_key_data, uint32_t& native_scan_code, uint32_t& native_virtual_key, uint32_t& native_modifiers )
 719	{
 720		native_scan_code = 0;
 721		native_virtual_key = 0;
 722		native_modifiers = 0;
 723		
 724		if( native_key_data.isMap() )
 725		{
 726#if LL_DARWIN
 727			native_scan_code = (uint32_t)(native_key_data["char_code"].asInteger());
 728			native_virtual_key = (uint32_t)(native_key_data["key_code"].asInteger());
 729			native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger());
 730#elif LL_WINDOWS
 731			native_scan_code = (uint32_t)(native_key_data["scan_code"].asInteger());
 732			native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger());
 733			// TODO: I don't think we need to do anything with native modifiers here -- please verify
 734#elif LL_LINUX
 735			native_scan_code = (uint32_t)(native_key_data["scan_code"].asInteger());
 736			native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger());
 737			native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger());
 738#else
 739			// Add other platforms here as needed
 740#endif
 741		};
 742	};
 743
 744	////////////////////////////////////////////////////////////////////////////////
 745	//
 746	void keyEvent(LLQtWebKit::EKeyEvent key_event, int key, LLQtWebKit::EKeyboardModifier modifiers, LLSD native_key_data = LLSD::emptyMap())
 747	{
 748		// The incoming values for 'key' will be the ones from indra_constants.h
 749		std::string utf8_text;
 750		
 751		if(key < KEY_SPECIAL)
 752		{
 753			// Low-ascii characters need to get passed through.
 754			utf8_text = (char)key;
 755		}
 756		
 757		// Any special-case handling we want to do for particular keys...
 758		switch((KEY)key)
 759		{
 760			// ASCII codes for some standard keys
 761			case LLQtWebKit::KEY_BACKSPACE:		utf8_text = (char)8;		break;
 762			case LLQtWebKit::KEY_TAB:			utf8_text = (char)9;		break;
 763			case LLQtWebKit::KEY_RETURN:		utf8_text = (char)13;		break;
 764			case LLQtWebKit::KEY_PAD_RETURN:	utf8_text = (char)13;		break;
 765			case LLQtWebKit::KEY_ESCAPE:		utf8_text = (char)27;		break;
 766			
 767			default:  
 768			break;
 769		}
 770		
 771//		std::cerr << "key event " << (int)key_event << ", native_key_data = " << native_key_data << std::endl;
 772		
 773		uint32_t native_scan_code = 0;
 774		uint32_t native_virtual_key = 0;
 775		uint32_t native_modifiers = 0;
 776		deserializeKeyboardData( native_key_data, native_scan_code, native_virtual_key, native_modifiers );
 777		
 778		LLQtWebKit::getInstance()->keyboardEvent( mBrowserWindowId, key_event, (uint32_t)key, utf8_text.c_str(), modifiers, native_scan_code, native_virtual_key, native_modifiers);
 779
 780		checkEditState();
 781	};
 782
 783	////////////////////////////////////////////////////////////////////////////////
 784	//
 785	void unicodeInput( const std::string &utf8str, LLQtWebKit::EKeyboardModifier modifiers, LLSD native_key_data = LLSD::emptyMap())
 786	{		
 787		uint32_t key = LLQtWebKit::KEY_NONE;
 788		
 789//		std::cerr << "unicode input, native_key_data = " << native_key_data << std::endl;
 790		
 791		if(utf8str.size() == 1)
 792		{
 793			// The only way a utf8 string can be one byte long is if it's actually a single 7-bit ascii character.
 794			// In this case, use it as the key value.
 795			key = utf8str[0];
 796		}
 797
 798		uint32_t native_scan_code = 0;
 799		uint32_t native_virtual_key = 0;
 800		uint32_t native_modifiers = 0;
 801		deserializeKeyboardData( native_key_data, native_scan_code, native_virtual_key, native_modifiers );
 802		
 803		LLQtWebKit::getInstance()->keyboardEvent( mBrowserWindowId, LLQtWebKit::KE_KEY_DOWN, (uint32_t)key, utf8str.c_str(), modifiers, native_scan_code, native_virtual_key, native_modifiers);
 804		LLQtWebKit::getInstance()->keyboardEvent( mBrowserWindowId, LLQtWebKit::KE_KEY_UP, (uint32_t)key, utf8str.c_str(), modifiers, native_scan_code, native_virtual_key, native_modifiers);
 805
 806		checkEditState();
 807	};
 808	
 809	void checkEditState(void)
 810	{
 811		bool can_cut = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT);
 812		bool can_copy = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY);
 813		bool can_paste = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE);
 814					
 815		if((can_cut != mCanCut) || (can_copy != mCanCopy) || (can_paste != mCanPaste))
 816		{
 817			LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_state");
 818			
 819			if(can_cut != mCanCut)
 820			{
 821				mCanCut = can_cut;
 822				message.setValueBoolean("cut", can_cut);
 823			}
 824
 825			if(can_copy != mCanCopy)
 826			{
 827				mCanCopy = can_copy;
 828				message.setValueBoolean("copy", can_copy);
 829			}
 830
 831			if(can_paste != mCanPaste)
 832			{
 833				mCanPaste = can_paste;
 834				message.setValueBoolean("paste", can_paste);
 835			}
 836			
 837			sendMessage(message);
 838			
 839		}
 840	}
 841	
 842	std::string mPickedFile;
 843	
 844	std::string blockingPickFile(void)
 845	{
 846		mPickedFile.clear();
 847		
 848		LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "pick_file");
 849		message.setValueBoolean("blocking_request", true);
 850		
 851		// The "blocking_request" key in the message means this sendMessage call will block until a response is received.
 852		sendMessage(message);
 853		
 854		return mPickedFile;
 855	}
 856
 857	void onPickFileResponse(const std::string &file)
 858	{
 859		mPickedFile = file;
 860	}
 861
 862};
 863
 864MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) :
 865	MediaPluginBase(host_send_func, host_user_data)
 866{
 867//	std::cerr << "MediaPluginWebKit constructor" << std::endl;
 868
 869	mBrowserWindowId = 0;
 870	mInitState = INIT_STATE_UNINITIALIZED;
 871	mNeedsUpdate = true;
 872	mCanCut = false;
 873	mCanCopy = false;
 874	mCanPaste = false;
 875	mLastMouseX = 0;
 876	mLastMouseY = 0;
 877	mFirstFocus = true;
 878	mBackgroundR = 0.0f;
 879	mBackgroundG = 0.0f;
 880	mBackgroundB = 0.0f;
 881
 882	mHostLanguage = "en";		// default to english
 883	mJavascriptEnabled = true;	// default to on
 884	mPluginsEnabled = true;		// default to on
 885	mEnableMediaPluginDebugging = false;
 886	mUserAgent = "LLPluginMedia Web Browser";
 887
 888	mElapsedTime.reset();
 889}
 890
 891MediaPluginWebKit::~MediaPluginWebKit()
 892{
 893	// unhook observer
 894	LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this );
 895
 896	// clean up
 897	LLQtWebKit::getInstance()->reset();
 898
 899//	std::cerr << "MediaPluginWebKit destructor" << std::endl;
 900}
 901
 902void MediaPluginWebKit::receiveMessage(const char *message_string)
 903{
 904//	std::cerr << "MediaPluginWebKit::receiveMessage: received message: \"" << message_string << "\"" << std::endl;
 905	LLPluginMessage message_in;
 906	
 907	if(message_in.parse(message_string) >= 0)
 908	{
 909		std::string message_class = message_in.getClass();
 910		std::string message_name = message_in.getName();
 911		if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE)
 912		{
 913			if(message_name == "init")
 914			{
 915				LLPluginMessage message("base", "init_response");
 916				LLSD versions = LLSD::emptyMap();
 917				versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
 918				versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
 919				versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION;
 920				message.setValueLLSD("versions", versions);
 921
 922				std::string plugin_version = "Webkit media plugin, Webkit version ";
 923				plugin_version += LLQtWebKit::getInstance()->getVersion();
 924				message.setValue("plugin_version", plugin_version);
 925				sendMessage(message);
 926			}
 927			else if(message_name == "idle")
 928			{
 929				// no response is necessary here.
 930				F64 time = message_in.getValueReal("time");
 931				
 932				// Convert time to milliseconds for update()
 933				update((int)(time * 1000.0f));
 934			}
 935			else if(message_name == "cleanup")
 936			{
 937				// DTOR most likely won't be called but the recent change to the way this process
 938				// is (not) killed means we see this message and can do what we need to here.
 939				// Note: this cleanup is ultimately what writes cookies to the disk
 940				LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this );
 941				LLQtWebKit::getInstance()->reset();
 942			}
 943			else if(message_name == "shm_added")
 944			{
 945				SharedSegmentInfo info;
 946				info.mAddress = message_in.getValuePointer("address");
 947				info.mSize = (size_t)message_in.getValueS32("size");
 948				std::string name = message_in.getValue("name");
 949				
 950//				std::cerr << "MediaPluginWebKit::receiveMessage: shared memory added, name: " << name 
 951//					<< ", size: " << info.mSize 
 952//					<< ", address: " << info.mAddress 
 953//					<< std::endl;
 954
 955				mSharedSegments.insert(SharedSegmentMap::value_type(name, info));
 956			
 957			}
 958			else if(message_name == "shm_remove")
 959			{
 960				std::string name = message_in.getValue("name");
 961				
 962//				std::cerr << "MediaPluginWebKit::receiveMessage: shared memory remove, name = " << name << std::endl;
 963
 964				SharedSegmentMap::iterator iter = mSharedSegments.find(name);
 965				if(iter != mSharedSegments.end())
 966				{
 967					if(mPixels == iter->second.mAddress)
 968					{
 969						// This is the currently active pixel buffer.  Make sure we stop drawing to it.
 970						mPixels = NULL;
 971						mTextureSegmentName.clear();
 972					}
 973					mSharedSegments.erase(iter);
 974				}
 975				else
 976				{
 977//					std::cerr << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl;
 978				}
 979
 980				// Send the response so it can be cleaned up.
 981				LLPluginMessage message("base", "shm_remove_response");
 982				message.setValue("name", name);
 983				sendMessage(message);
 984			}
 985			else
 986			{
 987//				std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl;
 988			}
 989		}
 990		else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
 991		{
 992			if(message_name == "set_volume")
 993			{
 994				F32 volume = message_in.getValueReal("volume");
 995				setVolume(volume);
 996			}
 997		}
 998		else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
 999		{
1000			if(message_name == "init")
1001			{
1002				mTarget = message_in.getValue("target");
1003				
1004				// This is the media init message -- all necessary data for initialization should have been received.
1005				if(initBrowser())
1006				{
1007					
1008					// Plugin gets to decide the texture parameters to use.
1009					mDepth = 4;
1010
1011					LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params");
1012					message.setValueS32("default_width", 1024);
1013					message.setValueS32("default_height", 1024);
1014					message.setValueS32("depth", mDepth);
1015					message.setValueU32("internalformat", GL_RGBA);
1016	#if LL_QTWEBKIT_USES_PIXMAPS
1017					message.setValueU32("format", GL_BGRA_EXT); // I hope this isn't system-dependant... is it?  If so, we'll have to check the root window's pixel layout or something... yuck.
1018	#else
1019					message.setValueU32("format", GL_RGBA);
1020	#endif // LL_QTWEBKIT_USES_PIXMAPS
1021					message.setValueU32("type", GL_UNSIGNED_BYTE);
1022					message.setValueBoolean("coords_opengl", true);
1023					sendMessage(message);
1024				}
1025				else
1026				{
1027					// if initialization failed, we're done.
1028					mDeleteMe = true;
1029				}
1030
1031			}
1032			else if(message_name == "set_user_data_path")
1033			{
1034				std::string user_data_path = message_in.getValue("path"); // n.b. always has trailing platform-specific dir-delimiter
1035				mProfileDir = user_data_path + "browser_profile";
1036
1037				// FIXME: Should we do anything with this if it comes in after the browser has been initialized?
1038			}
1039			else if(message_name == "set_language_code")
1040			{
1041				mHostLanguage = message_in.getValue("language");
1042
1043				// FIXME: Should we do anything with this if it comes in after the browser has been initialized?
1044			}
1045			else if(message_name == "plugins_enabled")
1046			{
1047				mPluginsEnabled = message_in.getValueBoolean("enable");
1048			}
1049			else if(message_name == "javascript_enabled")
1050			{
1051				mJavascriptEnabled = message_in.getValueBoolean("enable");
1052			}
1053			else if(message_name == "size_change")
1054			{
1055				std::string name = message_in.getValue("name");
1056				S32 width = message_in.getValueS32("width");
1057				S32 height = message_in.getValueS32("height");
1058				S32 texture_width = message_in.getValueS32("texture_width");
1059				S32 texture_height = message_in.getValueS32("texture_height");
1060				mBackgroundR = message_in.getValueReal("background_r");
1061				mBackgroundG = message_in.getValueReal("background_g");
1062				mBackgroundB = message_in.getValueReal("background_b");
1063//				mBackgroundA = message_in.setValueReal("background_a");		// Ignore any alpha
1064								
1065				if(!name.empty())
1066				{
1067					// Find the shared memory region with this name
1068					SharedSegmentMap::iterator iter = mSharedSegments.find(name);
1069					if(iter != mSharedSegments.end())
1070					{
1071						mPixels = (unsigned char*)iter->second.mAddress;
1072						mWidth = width;
1073						mHeight = height;
1074
1075						if(initBrowserWindow())
1076						{
1077
1078							// size changed so tell the browser
1079							LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight );
1080							
1081	//						std::cerr << "webkit plugin: set size to " << mWidth << " x " << mHeight 
1082	//								<< ", rowspan is " << LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl;
1083									
1084							S32 real_width = LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) / LLQtWebKit::getInstance()->getBrowserDepth(mBrowserWindowId); 
1085							
1086							// The actual width the browser will be drawing to is probably smaller... let the host know by modifying texture_width in the response.
1087							if(real_width <= texture_width)
1088							{
1089								texture_width = real_width;
1090							}
1091							else
1092							{
1093								// This won't work -- it'll be bigger than the allocated memory.  This is a fatal error.
1094	//							std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl;
1095								mDeleteMe = true;
1096								return;
1097							}
1098						}
1099						else
1100						{
1101							// Setting up the browser window failed.  This is a fatal error.
1102							mDeleteMe = true;
1103						}
1104
1105						
1106						mTextureWidth = texture_width;
1107						mTextureHeight = texture_height;
1108						
1109					};
1110				};
1111
1112				LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response");
1113				message.setValue("name", name);
1114				message.setValueS32("width", width);
1115				message.setValueS32("height", height);
1116				message.setValueS32("texture_width", texture_width);
1117				message.setValueS32("texture_height", texture_height);
1118				sendMessage(message);
1119
1120			}
1121			else if(message_name == "load_uri")
1122			{
1123				std::string uri = message_in.getValue("uri");
1124
1125//				std::cout << "loading URI: " << uri << std::endl;
1126				
1127				if(!uri.empty())
1128				{
1129					if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
1130					{
1131						LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, uri );
1132					}
1133					else
1134					{
1135						mInitialNavigateURL = uri;
1136					}
1137				}
1138			}
1139			else if(message_name == "mouse_event")
1140			{
1141				std::string event = message_in.getValue("event");
1142				S32 button = message_in.getValueS32("button");
1143				mLastMouseX = message_in.getValueS32("x");
1144				mLastMouseY = message_in.getValueS32("y");
1145				std::string modifiers = message_in.getValue("modifiers");
1146				
1147				// Treat unknown mouse events as mouse-moves.
1148				LLQtWebKit::EMouseEvent mouse_event = LLQtWebKit::ME_MOUSE_MOVE;
1149				if(event == "down")
1150				{
1151					mouse_event = LLQtWebKit::ME_MOUSE_DOWN;
1152				}
1153				else if(event == "up")
1154				{
1155					mouse_event = LLQtWebKit::ME_MOUSE_UP;
1156				}
1157				else if(event == "double_click")
1158				{
1159					mouse_event = LLQtWebKit::ME_MOUSE_DOUBLE_CLICK;
1160				}
1161				
1162				LLQtWebKit::getInstance()->mouseEvent( mBrowserWindowId, mouse_event, button, mLastMouseX, mLastMouseY, decodeModifiers(modifiers));
1163				checkEditState();
1164			}
1165			else if(message_name == "scroll_event")
1166			{
1167				S32 x = message_in.getValueS32("x");
1168				S32 y = message_in.getValueS32("y");
1169				std::string modifiers = message_in.getValue("modifiers");
1170				
1171				// Incoming scroll events are adjusted so that 1 detent is approximately 1 unit.
1172				// Qt expects 1 detent to be 120 units.
1173				// It also seems that our y scroll direction is inverted vs. what Qt expects.
1174				
1175				x *= 120;
1176				y *= -120;
1177				
1178				LLQtWebKit::getInstance()->scrollWheelEvent(mBrowserWindowId, mLastMouseX, mLastMouseY, x, y, decodeModifiers(modifiers));
1179			}
1180			else if(message_name == "key_event")
1181			{
1182				std::string event = message_in.getValue("event");
1183				S32 key = message_in.getValueS32("key");
1184				std::string modifiers = message_in.getValue("modifiers");
1185				LLSD native_key_data = message_in.getValueLLSD("native_key_data");
1186				
1187				// Treat unknown events as key-up for safety.
1188				LLQtWebKit::EKeyEvent key_event = LLQtWebKit::KE_KEY_UP;
1189				if(event == "down")
1190				{
1191					key_event = LLQtWebKit::KE_KEY_DOWN;
1192				}
1193				else if(event == "repeat")
1194				{
1195					key_event = LLQtWebKit::KE_KEY_REPEAT;
1196				}
1197				
1198				keyEvent(key_event, key, decodeModifiers(modifiers), native_key_data);
1199			}
1200			else if(message_name == "text_event")
1201			{
1202				std::string text = message_in.getValue("text");
1203				std::string modifiers = message_in.getValue("modifiers");
1204				LLSD native_key_data = message_in.getValueLLSD("native_key_data");
1205				
1206				unicodeInput(text, decodeModifiers(modifiers), native_key_data);
1207			}
1208			if(message_name == "edit_cut")
1209			{
1210				LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT );
1211				checkEditState();
1212			}
1213			if(message_name == "edit_copy")
1214			{
1215				LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY );
1216				checkEditState();
1217			}
1218			if(message_name == "edit_paste")
1219			{
1220				LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE );
1221				checkEditState();
1222			}
1223			if(message_name == "pick_file_response")
1224			{
1225				onPickFileResponse(message_in.getValue("file"));
1226			}
1227			if(message_name == "auth_response")
1228			{
1229				authResponse(message_in);
1230			}
1231			else
1232			if(message_name == "enable_media_plugin_debugging")
1233			{
1234				mEnableMediaPluginDebugging = message_in.getValueBoolean( "enable" );
1235			}
1236			else
1237			if(message_name == "js_enable_object")
1238			{
1239#if LLQTWEBKIT_API_VERSION >= 9
1240				bool enable = message_in.getValueBoolean( "enable" );
1241				LLQtWebKit::getInstance()->setSLObjectEnabled( enable );
1242#endif
1243			}
1244			else
1245			if(message_name == "js_agent_location")
1246			{
1247#if LLQTWEBKIT_API_VERSION >= 9
1248				F32 x = message_in.getValueReal("x");
1249				F32 y = message_in.getValueReal("y");
1250				F32 z = message_in.getValueReal("z");
1251				LLQtWebKit::getInstance()->setAgentLocation( x, y, z );
1252				LLQtWebKit::getInstance()->emitLocation();
1253#endif
1254			}
1255			else
1256			if(message_name == "js_agent_global_location")
1257			{
1258#if LLQTWEBKIT_API_VERSION >= 9
1259				F32 x = message_in.getValueReal("x");
1260				F32 y = message_in.getValueReal("y");
1261				F32 z = message_in.getValueReal("z");
1262				LLQtWebKit::getInstance()->setAgentGlobalLocation( x, y, z );
1263				LLQtWebKit::getInstance()->emitLocation();
1264#endif
1265			}
1266			else			
1267			if(message_name == "js_agent_orientation")
1268			{
1269#if LLQTWEBKIT_API_VERSION >= 9
1270				F32 angle = message_in.getValueReal("angle");
1271				LLQtWebKit::getInstance()->setAgentOrientation( angle );
1272				LLQtWebKit::getInstance()->emitLocation();
1273#endif
1274			}
1275			else
1276			if(message_name == "js_agent_region")
1277			{
1278#if LLQTWEBKIT_API_VERSION >= 9
1279				const std::string& region = message_in.getValue("region");
1280				LLQtWebKit::getInstance()->setAgentRegion( region );
1281				LLQtWebKit::getInstance()->emitLocation();
1282#endif
1283			}
1284			else
1285				if(message_name == "js_agent_maturity")
1286				{
1287#if LLQTWEBKIT_API_VERSION >= 9
1288					const std::string& maturity = message_in.getValue("maturity");
1289					LLQtWebKit::getInstance()->setAgentMaturity( maturity );
1290					LLQtWebKit::getInstance()->emitMaturity();
1291#endif
1292				}
1293			else
1294			if(message_name == "js_agent_language")
1295			{
1296#if LLQTWEBKIT_API_VERSION >= 9
1297				const std::string& language = message_in.getValue("language");
1298				LLQtWebKit::getInstance()->setAgentLanguage( language );
1299				LLQtWebKit::getInstance()->emitLanguage();
1300#endif
1301			}
1302			else
1303			{
1304//				std::cerr << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl;
1305			}
1306		}
1307		else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER)
1308		{
1309			if(message_name == "focus")
1310			{
1311				bool val = message_in.getValueBoolean("focused");
1312				LLQtWebKit::getInstance()->focusBrowser( mBrowserWindowId, val );
1313				
1314				if(mFirstFocus && val)
1315				{
1316					// On the first focus, post a tab key event.  This fixes a problem with initial focus.
1317					std::string empty;
1318					keyEvent(LLQtWebKit::KE_KEY_DOWN, KEY_TAB, decodeModifiers(empty));
1319					keyEvent(LLQtWebKit::KE_KEY_UP, KEY_TAB, decodeModifiers(empty));
1320					mFirstFocus = false;
1321				}
1322			}
1323			else if(message_name == "set_page_zoom_factor")
1324			{
1325#if LLQTWEBKIT_API_VERSION >= 15
1326				F32 factor = message_in.getValueReal("factor");
1327				LLQtWebKit::getInstance()->setPageZoomFactor(factor);
1328#else
1329				llwarns << "Ignoring setPageZoomFactor message (llqtwebkit version is too old)." << llendl;
1330#endif
1331			}
1332			else if(message_name == "clear_cache")
1333			{
1334				LLQtWebKit::getInstance()->clearCache();
1335			}
1336			else if(message_name == "clear_cookies")
1337			{
1338				LLQtWebKit::getInstance()->clearAllCookies();
1339			}
1340			else if(message_name == "enable_cookies")
1341			{
1342				mCookiesEnabled = message_in.getValueBoolean("enable");
1343				LLQtWebKit::getInstance()->enableCookies( mCookiesEnabled );
1344			}
1345			else if(message_name == "enable_plugins")
1346			{
1347				mPluginsEnabled = message_in.getValueBoolean("enable");
1348				LLQtWebKit::getInstance()->enablePlugins( mPluginsEnabled );
1349			}
1350			else if(message_name == "enable_javascript")
1351			{
1352				mJavascriptEnabled = message_in.getValueBoolean("enable");
1353				//LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled );
1354			}
1355			else if(message_name == "set_cookies")
1356			{
1357				LLQtWebKit::getInstance()->setCookies(message_in.getValue("cookies"));
1358
1359				// debug spam
1360				postDebugMessage( "Plugin setting cookie: " + message_in.getValue("cookies") );
1361			}
1362			else if(message_name == "proxy_setup")
1363			{
1364				bool val = message_in.getValueBoolean("enable");
1365				std::string host = message_in.getValue("host");
1366				int port = message_in.getValueS32("port");
1367				LLQtWebKit::getInstance()->enableProxy( val, host, port );
1368			}
1369			else if(message_name == "browse_stop")
1370			{
1371				LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_STOP );
1372			}
1373			else if(message_name == "browse_reload")
1374			{
1375				// foo = message_in.getValueBoolean("ignore_cache");
1376				LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_RELOAD );
1377			}
1378			else if(message_name == "browse_forward")
1379			{
1380				LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD );
1381			}
1382			else if(message_name == "browse_back")
1383			{
1384				LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK );
1385			}
1386			else if(message_name == "set_status_redirect")
1387			{
1388				int code = message_in.getValueS32("code");
1389				std::string url = message_in.getValue("url");
1390				if ( 404 == code )	// browser lib only supports 404 right now
1391				{
1392#if LLQTWEBKIT_API_VERSION < 8
1393				 	LLQtWebKit::getInstance()->set404RedirectUrl( mBrowserWindowId, url );
1394#endif
1395				};
1396			}
1397			else if(message_name == "set_user_agent")
1398			{
1399				mUserAgent = message_in.getValue("user_agent");
1400				LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent );
1401			}
1402			else if(message_name == "show_web_inspector")
1403			{
1404#if LLQTWEBKIT_API_VERSION >= 10
1405				bool val = message_in.getValueBoolean("show");
1406				LLQtWebKit::getInstance()->showWebInspector( val );
1407#else
1408				llwarns << "Ignoring showWebInspector message (llqtwebkit version is too old)." << llendl;
1409#endif
1410			}
1411			else if(message_name == "ignore_ssl_cert_errors")
1412			{
1413#if LLQTWEBKIT_API_VERSION >= 3
1414				LLQtWebKit::getInstance()->setIgnoreSSLCertErrors( message_in.getValueBoolean("ignore") );
1415#else
1416				llwarns << "Ignoring ignore_ssl_cert_errors message (llqtwebkit version is too old)." << llendl;
1417#endif
1418			}
1419			else if(message_name == "add_certificate_file_path")
1420			{
1421#if LLQTWEBKIT_API_VERSION >= 6
1422				LLQtWebKit::getInstance()->setCAFile( message_in.getValue("path") );
1423#else
1424				llwarns << "Ignoring add_certificate_file_path message (llqtwebkit version is too old)." << llendl;
1425#endif
1426			}
1427			else if(message_name == "init_history")
1428			{
1429				// Initialize browser history
1430				LLSD history = message_in.getValueLLSD("history");
1431				// First, clear the URL history
1432				LLQtWebKit::getInstance()->clearHistory(mBrowserWindowId);
1433				// Then, add the history items in order
1434				LLSD::array_iterator iter_history = history.beginArray();
1435				LLSD::array_iterator end_history = history.endArray();
1436				for(; iter_history != end_history; ++iter_history)
1437				{
1438					std::string url = (*iter_history).asString();
1439					if(! url.empty()) {
1440						LLQtWebKit::getInstance()->prependHistoryUrl(mBrowserWindowId, url);
1441					}
1442				}
1443			}
1444			else if(message_name == "proxy_window_opened")
1445			{
1446				std::string target = message_in.getValue("target");
1447				std::string uuid = message_in.getValue("uuid");
1448				LLQtWebKit::getInstance()->proxyWindowOpened(mBrowserWindowId, target, uuid);
1449			}
1450			else if(message_name == "proxy_window_closed")
1451			{
1452				std::string uuid = message_in.getValue("uuid");
1453				LLQtWebKit::getInstance()->proxyWindowClosed(mBrowserWindowId, uuid);
1454			}
1455			else
1456			{
1457//				std::cerr << "MediaPluginWebKit::receiveMessage: unknown media_browser message: " << message_string << std::endl;
1458			};
1459		}
1460		else
1461		{
1462//			std::cerr << "MediaPluginWebKit::receiveMessage: unknown message class: " << message_class << std::endl;
1463		};
1464	}
1465}
1466
1467void MediaPluginWebKit::setVolume(F32 volume)
1468{
1469	mVolumeCatcher.setVolume(volume);
1470}
1471
1472int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
1473{
1474	MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data);
1475	*plugin_send_func = MediaPluginWebKit::staticReceiveMessage;
1476	*plugin_user_data = (void*)self;
1477
1478	return 0;
1479}
1480
1481