From ce242821dcf83bde15b49895c24019347b266d97 Mon Sep 17 00:00:00 2001 From: Monroe Linden Date: Tue, 16 Mar 2010 16:42:31 -0700 Subject: Added an "init" message in LLPLUGIN_MESSAGE_CLASS_MEDIA, and made LLPluginClassMedia queue it up before initializing its LLPluginProcessParent. Made all existing plugins send their texture_params message from this init message instead of the LLPLUGIN_MESSAGE_CLASS_BASE "init" message. (This ensures that they won't start to receive 'size_change' messages until after the init has happened.) Added "set_user_data_path" and "set_language_code" messages to LLPluginClassMedia. Made webkit plugin deal with the new messages, when they're sent before it receives the media "init". Removed the user_data_path and language_code arguments from the init function calls throughout the hierarchy. Made LLViewerMediaImpl queue up the language code and user data path messages before initializing the media. Reviewed by Callum at http://codereview.lindenlab.com/687006 . --- indra/media_plugins/webkit/media_plugin_webkit.cpp | 255 ++++++++++++--------- 1 file changed, 153 insertions(+), 102 deletions(-) (limited to 'indra/media_plugins/webkit/media_plugin_webkit.cpp') diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp index afde904be6..24c53638d2 100644 --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -88,10 +88,12 @@ public: private: std::string mProfileDir; + std::string mHostLanguage; enum { - INIT_STATE_UNINITIALIZED, // Browser instance hasn't been set up yet + INIT_STATE_UNINITIALIZED, // LLQtWebkit hasn't been set up yet + INIT_STATE_INITIALIZED, // LLQtWebkit has been set up, but no browser window has been created yet. INIT_STATE_NAVIGATING, // Browser instance has been set up and initial navigate to about:blank has been issued INIT_STATE_NAVIGATE_COMPLETE, // initial navigate to about:blank has completed INIT_STATE_WAIT_REDRAW, // First real navigate begin has been received, waiting for page changed event to start handling redraws @@ -191,13 +193,6 @@ private: if ( mInitState > INIT_STATE_UNINITIALIZED ) return true; - // not enough information to initialize the browser yet. - if ( mWidth < 0 || mHeight < 0 || mDepth < 0 || - mTextureWidth < 0 || mTextureHeight < 0 ) - { - return false; - }; - // set up directories char cwd[ FILENAME_MAX ]; // I *think* this is defined on all platforms we use if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) @@ -208,12 +203,12 @@ private: std::string application_dir = std::string( cwd ); #if LL_DARWIN - // When running under the Xcode debugger, there's a setting called "Break on Debugger()/DebugStr()" which defaults to being turned on. - // This causes the environment variable USERBREAK to be set to 1, which causes these legacy calls to break into the debugger. - // 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 - // which gets hit when the plugin is probed by webkit. - // Unsetting the environment variable here works around this issue. - unsetenv("USERBREAK"); + // When running under the Xcode debugger, there's a setting called "Break on Debugger()/DebugStr()" which defaults to being turned on. + // This causes the environment variable USERBREAK to be set to 1, which causes these legacy calls to break into the debugger. + // 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 + // which gets hit when the plugin is probed by webkit. + // Unsetting the environment variable here works around this issue. + unsetenv("USERBREAK"); #endif #if LL_WINDOWS @@ -254,66 +249,92 @@ private: bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, mProfileDir, native_window_handle ); if ( result ) { - // create single browser window - mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight ); + mInitState = INIT_STATE_INITIALIZED; + + return true; + }; + + return false; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + bool initBrowserWindow() + { + // already initialized + if ( mInitState > INIT_STATE_INITIALIZED ) + return true; + + // not enough information to initialize the browser yet. + if ( mWidth < 0 || mHeight < 0 || mDepth < 0 || + mTextureWidth < 0 || mTextureHeight < 0 ) + { + return false; + }; + + // Set up host language before creating browser window + if(!mHostLanguage.empty()) + { + LLQtWebKit::getInstance()->setHostLanguage(mHostLanguage); + } + + // create single browser window + mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight ); #if LL_WINDOWS - // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(true); + // Enable plugins + LLQtWebKit::getInstance()->enablePlugins(true); #elif LL_DARWIN - // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(true); + // Enable plugins + LLQtWebKit::getInstance()->enablePlugins(true); #elif LL_LINUX - // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(true); + // Enable plugins + LLQtWebKit::getInstance()->enablePlugins(true); #endif - // Enable cookies - LLQtWebKit::getInstance()->enableCookies( true ); + // Enable cookies + LLQtWebKit::getInstance()->enableCookies( true ); - // tell LLQtWebKit about the size of the browser window - LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); + // tell LLQtWebKit about the size of the browser window + LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); - // observer events that LLQtWebKit emits - LLQtWebKit::getInstance()->addObserver( mBrowserWindowId, this ); + // observer events that LLQtWebKit emits + LLQtWebKit::getInstance()->addObserver( mBrowserWindowId, this ); - // append details to agent string - LLQtWebKit::getInstance()->setBrowserAgentId( "LLPluginMedia Web Browser" ); + // append details to agent string + LLQtWebKit::getInstance()->setBrowserAgentId( "LLPluginMedia Web Browser" ); #if !LL_QTWEBKIT_USES_PIXMAPS - // don't flip bitmap - LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true ); + // don't flip bitmap + LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true ); #endif // !LL_QTWEBKIT_USES_PIXMAPS - - // set background color - // convert background color channels from [0.0, 1.0] to [0, 255]; - LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, int(mBackgroundR * 255.0f), int(mBackgroundG * 255.0f), int(mBackgroundB * 255.0f) ); - - // Set state _before_ starting the navigate, since onNavigateBegin might get called before this call returns. - setInitState(INIT_STATE_NAVIGATING); - - // Don't do this here -- it causes the dreaded "white flash" when loading a browser instance. - // FIXME: Re-added this because navigating to a "page" initializes things correctly - especially - // for the HTTP AUTH dialog issues (DEV-41731). Will fix at a later date. - // Build a data URL like this: "data:text/html,%3Chtml%3E%3Cbody bgcolor=%22#RRGGBB%22%3E%3C/body%3E%3C/html%3E" - // where RRGGBB is the background color in HTML style - std::stringstream url; - - url << "data:text/html,%3Chtml%3E%3Cbody%20bgcolor=%22#"; - // convert background color channels from [0.0, 1.0] to [0, 255]; - url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundR * 255.0f); - url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundG * 255.0f); - url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundB * 255.0f); - url << "%22%3E%3C/body%3E%3C/html%3E"; - - lldebugs << "data url is: " << url.str() << llendl; - - LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, url.str() ); -// LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" ); - - return true; - }; + + // set background color + // convert background color channels from [0.0, 1.0] to [0, 255]; + LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, int(mBackgroundR * 255.0f), int(mBackgroundG * 255.0f), int(mBackgroundB * 255.0f) ); + + // Set state _before_ starting the navigate, since onNavigateBegin might get called before this call returns. + setInitState(INIT_STATE_NAVIGATING); + + // Don't do this here -- it causes the dreaded "white flash" when loading a browser instance. + // FIXME: Re-added this because navigating to a "page" initializes things correctly - especially + // for the HTTP AUTH dialog issues (DEV-41731). Will fix at a later date. + // Build a data URL like this: "data:text/html,%3Chtml%3E%3Cbody bgcolor=%22#RRGGBB%22%3E%3C/body%3E%3C/html%3E" + // where RRGGBB is the background color in HTML style + std::stringstream url; + + url << "data:text/html,%3Chtml%3E%3Cbody%20bgcolor=%22#"; + // convert background color channels from [0.0, 1.0] to [0, 255]; + url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundR * 255.0f); + url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundG * 255.0f); + url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundB * 255.0f); + url << "%22%3E%3C/body%3E%3C/html%3E"; + + lldebugs << "data url is: " << url.str() << llendl; + + LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, url.str() ); +// LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" ); - return false; - }; + return true; + } void setVolume(F32 vol); @@ -676,9 +697,6 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) { if(message_name == "init") { - std::string user_data_path = message_in.getValue("user_data_path"); // n.b. always has trailing platform-specific dir-delimiter - mProfileDir = user_data_path + "browser_profile"; - LLPluginMessage message("base", "init_response"); LLSD versions = LLSD::emptyMap(); versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; @@ -690,23 +708,6 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) plugin_version += LLQtWebKit::getInstance()->getVersion(); message.setValue("plugin_version", plugin_version); sendMessage(message); - - // Plugin gets to decide the texture parameters to use. - mDepth = 4; - - message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); - message.setValueS32("default_width", 1024); - message.setValueS32("default_height", 1024); - message.setValueS32("depth", mDepth); - message.setValueU32("internalformat", GL_RGBA); -#if LL_QTWEBKIT_USES_PIXMAPS - 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. -#else - message.setValueU32("format", GL_RGBA); -#endif // LL_QTWEBKIT_USES_PIXMAPS - message.setValueU32("type", GL_UNSIGNED_BYTE); - message.setValueBoolean("coords_opengl", true); - sendMessage(message); } else if(message_name == "idle") { @@ -771,7 +772,7 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) // std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; } } - else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) { if(message_name == "set_volume") { @@ -781,7 +782,50 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) } else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) { - if(message_name == "size_change") + if(message_name == "init") + { + // This is the media init message -- all necessary data for initialization should have been received. + if(initBrowser()) + { + + // Plugin gets to decide the texture parameters to use. + mDepth = 4; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); + message.setValueS32("default_width", 1024); + message.setValueS32("default_height", 1024); + message.setValueS32("depth", mDepth); + message.setValueU32("internalformat", GL_RGBA); + #if LL_QTWEBKIT_USES_PIXMAPS + 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. + #else + message.setValueU32("format", GL_RGBA); + #endif // LL_QTWEBKIT_USES_PIXMAPS + message.setValueU32("type", GL_UNSIGNED_BYTE); + message.setValueBoolean("coords_opengl", true); + sendMessage(message); + } + else + { + // if initialization failed, we're done. + mDeleteMe = true; + } + + } + else if(message_name == "set_user_data_path") + { + std::string user_data_path = message_in.getValue("path"); // n.b. always has trailing platform-specific dir-delimiter + mProfileDir = user_data_path + "browser_profile"; + + // FIXME: Should we do anything with this if it comes in after the browser has been initialized? + } + else if(message_name == "set_language_code") + { + mHostLanguage = message_in.getValue("language"); + + // FIXME: Should we do anything with this if it comes in after the browser has been initialized? + } + else if(message_name == "size_change") { std::string name = message_in.getValue("name"); S32 width = message_in.getValueS32("width"); @@ -803,29 +847,36 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) mWidth = width; mHeight = height; - // initialize (only gets called once) - initBrowser(); - - // size changed so tell the browser - LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); - -// std::cerr << "webkit plugin: set size to " << mWidth << " x " << mHeight -// << ", rowspan is " << LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl; - - S32 real_width = LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) / LLQtWebKit::getInstance()->getBrowserDepth(mBrowserWindowId); - - // The actual width the browser will be drawing to is probably smaller... let the host know by modifying texture_width in the response. - if(real_width <= texture_width) + if(initBrowserWindow()) { - texture_width = real_width; + + // size changed so tell the browser + LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); + + // std::cerr << "webkit plugin: set size to " << mWidth << " x " << mHeight + // << ", rowspan is " << LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl; + + S32 real_width = LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) / LLQtWebKit::getInstance()->getBrowserDepth(mBrowserWindowId); + + // The actual width the browser will be drawing to is probably smaller... let the host know by modifying texture_width in the response. + if(real_width <= texture_width) + { + texture_width = real_width; + } + else + { + // This won't work -- it'll be bigger than the allocated memory. This is a fatal error. + // std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl; + mDeleteMe = true; + return; + } } else { - // This won't work -- it'll be bigger than the allocated memory. This is a fatal error. -// std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl; + // Setting up the browser window failed. This is a fatal error. mDeleteMe = true; - return; } + mTextureWidth = texture_width; mTextureHeight = texture_height; -- cgit v1.3 From cb7fbc8a2093fd1ed5440d0f3184d9080cecad48 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 18 Mar 2010 16:53:27 -0700 Subject: EXT-6466 "Move "Enable Cookies" checkbox...", EXT-6402 "Add feature to disable Web plugins via prefs" and EXT-6401 "Add feature to disable Javascript via prefs" Apologies for the multiple commit of Jiras - these changes are closely related and impossible to commit deparately Reviewed by Monroe --- indra/llplugin/llpluginclassmedia.cpp | 15 +++++ indra/llplugin/llpluginclassmedia.h | 2 + indra/media_plugins/webkit/media_plugin_webkit.cpp | 50 +++++++++++---- indra/newview/app_settings/settings.xml | 22 +++++++ indra/newview/llviewermedia.cpp | 13 ++++ .../default/xui/en/panel_preferences_privacy.xml | 9 --- .../default/xui/en/panel_preferences_setup.xml | 75 +++++++++++++++++----- install.xml | 4 +- 8 files changed, 150 insertions(+), 40 deletions(-) (limited to 'indra/media_plugins/webkit/media_plugin_webkit.cpp') diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index cb62e46271..bf0e19473e 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -696,6 +696,20 @@ void LLPluginClassMedia::setLanguageCode(const std::string &language_code) sendMessage(message); } +void LLPluginClassMedia::setPluginsEnabled(const bool enabled) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "plugins_enabled"); + message.setValueBoolean("enable", enabled); + sendMessage(message); +} + +void LLPluginClassMedia::setJavascriptEnabled(const bool enabled) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "javascript_enabled"); + message.setValueBoolean("enable", enabled); + sendMessage(message); +} + LLPluginClassMedia::ETargetType getTargetTypeFromLLQtWebkit(int target_type) { // convert a LinkTargetType value from llqtwebkit to an ETargetType @@ -1065,6 +1079,7 @@ void LLPluginClassMedia::clear_cookies() void LLPluginClassMedia::enable_cookies(bool enable) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "enable_cookies"); + message.setValueBoolean("enable", enable); sendMessage(message); } diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 6318c67f12..79356beb68 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -179,6 +179,8 @@ public: // These can be called before init(), and they will be queued and sent before the media init message. void setUserDataPath(const std::string &user_data_path); void setLanguageCode(const std::string &language_code); + void setPluginsEnabled(const bool enabled); + void setJavascriptEnabled(const bool enabled); /////////////////////////////////// // media browser class functions diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp index 24c53638d2..0462fce236 100644 --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -89,6 +89,9 @@ private: std::string mProfileDir; std::string mHostLanguage; + bool mCookiesEnabled; + bool mJavascriptEnabled; + bool mPluginsEnabled; enum { @@ -277,21 +280,18 @@ private: { LLQtWebKit::getInstance()->setHostLanguage(mHostLanguage); } + + // turn on/off cookies based on what host app tells us + LLQtWebKit::getInstance()->enableCookies( mCookiesEnabled ); + + // turn on/off plugins based on what host app tells us + LLQtWebKit::getInstance()->enablePlugins( mPluginsEnabled ); + + // turn on/off Javascript based on what host app tells us + LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled ); // create single browser window mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight ); -#if LL_WINDOWS - // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(true); -#elif LL_DARWIN - // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(true); -#elif LL_LINUX - // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(true); -#endif - // Enable cookies - LLQtWebKit::getInstance()->enableCookies( true ); // tell LLQtWebKit about the size of the browser window LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); @@ -671,6 +671,10 @@ MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_ mBackgroundR = 0.0f; mBackgroundG = 0.0f; mBackgroundB = 0.0f; + + mHostLanguage = "en"; // default to english + mJavascriptEnabled = true; // default to on + mPluginsEnabled = true; // default to on } MediaPluginWebKit::~MediaPluginWebKit() @@ -825,6 +829,14 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) // FIXME: Should we do anything with this if it comes in after the browser has been initialized? } + else if(message_name == "plugins_enabled") + { + mPluginsEnabled = message_in.getValueBoolean("enable"); + } + else if(message_name == "javascript_enabled") + { + mJavascriptEnabled = message_in.getValueBoolean("enable"); + } else if(message_name == "size_change") { std::string name = message_in.getValue("name"); @@ -1026,8 +1038,18 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) } else if(message_name == "enable_cookies") { - bool val = message_in.getValueBoolean("enable"); - LLQtWebKit::getInstance()->enableCookies( val ); + mCookiesEnabled = message_in.getValueBoolean("enable"); + LLQtWebKit::getInstance()->enableCookies( mCookiesEnabled ); + } + else if(message_name == "enable_plugins") + { + mPluginsEnabled = message_in.getValueBoolean("enable"); + LLQtWebKit::getInstance()->enablePlugins( mPluginsEnabled ); + } + else if(message_name == "enable_javascript") + { + mJavascriptEnabled = message_in.getValueBoolean("enable"); + //LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled ); } else if(message_name == "proxy_setup") { diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 8edf766132..f8d556bb80 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1563,6 +1563,28 @@ Value 1 + BrowserJavascriptEnabled + + Comment + Enable Javascript in the built-in Web browser? + Persist + 1 + Type + Boolean + Value + 1 + + BrowserPluginsEnabled + + Comment + Enable Web plugins in the built-in Web browser? + Persist + 1 + Type + Boolean + Value + 1 + CreateToolCopyCenters Comment diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index b9509a98f5..99bfad9b45 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1260,6 +1260,19 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ media_source->setSize(default_width, default_height); media_source->setUserDataPath(user_data_path); media_source->setLanguageCode(LLUI::getLanguage()); + + // collect 'cookies enabled' setting from prefs and send to embedded browser + bool cookies_enabled = gSavedSettings.getBOOL( "CookiesEnabled" ); + media_source->enable_cookies( cookies_enabled ); + + // collect 'plugins enabled' setting from prefs and send to embedded browser + bool plugins_enabled = gSavedSettings.getBOOL( "BrowserPluginsEnabled" ); + media_source->setPluginsEnabled( plugins_enabled ); + + // collect 'javascript enabled' setting from prefs and send to embedded browser + bool javascript_enabled = gSavedSettings.getBOOL( "BrowserJavascriptEnabled" ); + media_source->setJavascriptEnabled( javascript_enabled ); + if (media_source->init(launcher_name, plugin_name, gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"))) { return media_source; diff --git a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml index f232a69482..3d7f392404 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml @@ -68,15 +68,6 @@ left="30" name="auto_disengage_mic_check" top_pad="10" - width="350" /> - - + + + + + + + Proxy location: @@ -335,7 +380,7 @@ increment="1" initial_value="80" label="Port number:" - label_width="75" + label_width="70" layout="topleft" left_delta="230" max_val="12000" diff --git a/install.xml b/install.xml index 7337241cf8..e8f0e5d318 100644 --- a/install.xml +++ b/install.xml @@ -962,9 +962,9 @@ anguage Infrstructure (CLI) international standard windows md5sum - ee5d242ea468ba9a82d2bf8581aa308d + 93b49cfea365fe082eda3e466a9beec3 url - http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/llqtwebkit-windows-qt4.6-20100312.tar.bz2 + http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/llqtwebkit-windows-qt4.6-20100318.tar.bz2 -- cgit v1.3 From 4e7838fb004f67c51d1b9991ba6782be7036bd7e Mon Sep 17 00:00:00 2001 From: Monroe Linden Date: Fri, 26 Mar 2010 15:39:21 -0700 Subject: Implemented central storage mechanism for media plugin cookies. Added LLPluginCookieStore, which manages the central list of cookies. New Mac and Windows versions of llqtwebkit, built from the tip of the cookie-api branch on http://bitbucket.org/lindenlab/llqtwebkit/ (currently revision f35a5eab8c2f). Added "set_cookies" and "cookie_set" messages to the media_browser message class in the plugin API, and made the webkit plugin use them appropriately. Added methods to LLViewerMedia to read/write the cookie file and add/remove individual cookies. Added hooks to read/write the cookie file (plugin_cookies.txt) in the same places as the location history (idle_startup() in llstartup.cpp and LLAppViewer::cleanup(), respectively). Reviewed by Richard at http://codereview.lindenlab.com/1006003 --- indra/llplugin/CMakeLists.txt | 2 + indra/llplugin/llpluginclassmedia.cpp | 14 + indra/llplugin/llpluginclassmedia.h | 1 + indra/llplugin/llpluginclassmediaowner.h | 2 + indra/llplugin/llplugincookiestore.cpp | 600 +++++++++++++++++++++ indra/llplugin/llplugincookiestore.h | 122 +++++ indra/media_plugins/webkit/media_plugin_webkit.cpp | 17 + indra/newview/llappviewer.cpp | 2 + indra/newview/llstartup.cpp | 3 + indra/newview/llviewermedia.cpp | 150 +++++- indra/newview/llviewermedia.h | 11 + install.xml | 8 +- 12 files changed, 927 insertions(+), 5 deletions(-) create mode 100644 indra/llplugin/llplugincookiestore.cpp create mode 100644 indra/llplugin/llplugincookiestore.h (limited to 'indra/media_plugins/webkit/media_plugin_webkit.cpp') diff --git a/indra/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt index 6706775d4f..def9fcbeae 100644 --- a/indra/llplugin/CMakeLists.txt +++ b/indra/llplugin/CMakeLists.txt @@ -23,6 +23,7 @@ include_directories( set(llplugin_SOURCE_FILES llpluginclassmedia.cpp + llplugincookiestore.cpp llplugininstance.cpp llpluginmessage.cpp llpluginmessagepipe.cpp @@ -36,6 +37,7 @@ set(llplugin_HEADER_FILES llpluginclassmedia.h llpluginclassmediaowner.h + llplugincookiestore.h llplugininstance.h llpluginmessage.h llpluginmessageclasses.h diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index bf0e19473e..e09b511a6e 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -993,6 +993,13 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) mClickTargetType = TARGET_NONE; mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLICK_LINK_NOFOLLOW); } + else if(message_name == "cookie_set") + { + if(mOwner) + { + mOwner->handleCookieSet(this, message.getValue("cookie")); + } + } else { LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL; @@ -1076,6 +1083,13 @@ void LLPluginClassMedia::clear_cookies() sendMessage(message); } +void LLPluginClassMedia::set_cookies(const std::string &cookies) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "set_cookies"); + message.setValue("cookies", cookies); + sendMessage(message); +} + void LLPluginClassMedia::enable_cookies(bool enable) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "enable_cookies"); diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 79356beb68..8c7b00f45b 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -189,6 +189,7 @@ public: void focus(bool focused); void clear_cache(); void clear_cookies(); + void set_cookies(const std::string &cookies); void enable_cookies(bool enable); void proxy_setup(bool enable, const std::string &host = LLStringUtil::null, int port = 0); void browse_stop(); diff --git a/indra/llplugin/llpluginclassmediaowner.h b/indra/llplugin/llpluginclassmediaowner.h index 6d369cd51a..5669b81fd1 100644 --- a/indra/llplugin/llpluginclassmediaowner.h +++ b/indra/llplugin/llpluginclassmediaowner.h @@ -39,6 +39,7 @@ #include class LLPluginClassMedia; +class LLPluginCookieStore; class LLPluginClassMediaOwner { @@ -78,6 +79,7 @@ public: virtual ~LLPluginClassMediaOwner() {}; virtual void handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent /*event*/) {}; + virtual void handleCookieSet(LLPluginClassMedia* /*self*/, const std::string &/*cookie*/) {}; }; #endif // LL_LLPLUGINCLASSMEDIAOWNER_H diff --git a/indra/llplugin/llplugincookiestore.cpp b/indra/llplugin/llplugincookiestore.cpp new file mode 100644 index 0000000000..1964b8d789 --- /dev/null +++ b/indra/llplugin/llplugincookiestore.cpp @@ -0,0 +1,600 @@ +/** + * @file llplugincookiestore.cpp + * @brief LLPluginCookieStore provides central storage for http cookies used by plugins + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * @endcond + */ + +#include "linden_common.h" +#include "indra_constants.h" + +#include "llplugincookiestore.h" +#include + +// for curl_getdate() (apparently parsing RFC 1123 dates is hard) +#include + +LLPluginCookieStore::LLPluginCookieStore(): + mHasChangedCookies(false) +{ +} + + +LLPluginCookieStore::~LLPluginCookieStore() +{ + clearCookies(); +} + + +LLPluginCookieStore::Cookie::Cookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end): + mCookie(s, cookie_start, cookie_end), + mNameStart(0), mNameEnd(0), + mValueStart(0), mValueEnd(0), + mDomainStart(0), mDomainEnd(0), + mPathStart(0), mPathEnd(0), + mDead(false), mChanged(true) +{ +} + +LLPluginCookieStore::Cookie *LLPluginCookieStore::Cookie::createFromString(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end) +{ + Cookie *result = new Cookie(s, cookie_start, cookie_end); + + if(!result->parse()) + { + delete result; + result = NULL; + } + + return result; +} + +std::string LLPluginCookieStore::Cookie::getKey() const +{ + std::string result; + if(mDomainEnd > mDomainStart) + { + result += mCookie.substr(mDomainStart, mDomainEnd - mDomainStart); + } + result += ';'; + if(mPathEnd > mPathStart) + { + result += mCookie.substr(mPathStart, mPathEnd - mPathStart); + } + result += ';'; + result += mCookie.substr(mNameStart, mNameEnd - mNameStart); + return result; +} + +bool LLPluginCookieStore::Cookie::parse() +{ + bool first_field = true; + + std::string::size_type cookie_end = mCookie.size(); + std::string::size_type field_start = 0; + + lldebugs << "parsing cookie: " << mCookie << llendl; + while(field_start < cookie_end) + { + // Finding the start of the next field requires honoring special quoting rules + // see the definition of 'quoted-string' in rfc2616 for details + std::string::size_type next_field_start = findFieldEnd(field_start); + + // The end of this field should not include the terminating ';' or any trailing whitespace + std::string::size_type field_end = mCookie.find_last_not_of("; ", next_field_start); + if(field_end == std::string::npos || field_end < field_start) + { + // This field was empty or all whitespace. Set end = start so it shows as empty. + field_end = field_start; + } + else if (field_end < next_field_start) + { + // we actually want the index of the char _after_ what 'last not of' found + ++field_end; + } + + // find the start of the actual name (skip separator and possible whitespace) + std::string::size_type name_start = mCookie.find_first_not_of("; ", field_start); + if(name_start == std::string::npos || name_start > next_field_start) + { + // Again, nothing but whitespace. + name_start = field_start; + } + + // the name and value are separated by the first equals sign + std::string::size_type name_value_sep = mCookie.find_first_of("=", name_start); + if(name_value_sep == std::string::npos || name_value_sep > field_end) + { + // No separator found, so this is a field without an = + name_value_sep = field_end; + } + + // the name end is before the name-value separator + std::string::size_type name_end = mCookie.find_last_not_of("= ", name_value_sep); + if(name_end == std::string::npos || name_end < name_start) + { + // I'm not sure how we'd hit this case... it seems like it would have to be an empty name. + name_end = name_start; + } + else if (name_end < name_value_sep) + { + // we actually want the index of the char _after_ what 'last not of' found + ++name_end; + } + + // Value is between the name-value sep and the end of the field. + std::string::size_type value_start = mCookie.find_first_not_of("= ", name_value_sep); + if(value_start == std::string::npos || value_start > field_end) + { + // All whitespace or empty value + value_start = field_end; + } + std::string::size_type value_end = mCookie.find_last_not_of("; ", field_end); + if(value_end == std::string::npos || value_end < value_start) + { + // All whitespace or empty value + value_end = value_start; + } + else if (value_end < field_end) + { + // we actually want the index of the char _after_ what 'last not of' found + ++value_end; + } + + lldebugs + << " field name: \"" << mCookie.substr(name_start, name_end - name_start) + << "\", value: \"" << mCookie.substr(value_start, value_end - value_start) << "\"" + << llendl; + + // See whether this field is one we know + if(first_field) + { + // The first field is the name=value pair + mNameStart = name_start; + mNameEnd = name_end; + mValueStart = value_start; + mValueEnd = value_end; + first_field = false; + } + else + { + // Subsequent fields must come from the set in rfc2109 + if(matchName(name_start, name_end, "expires")) + { + std::string date_string(mCookie, value_start, value_end - value_start); + // If the cookie contains an "expires" field, it MUST contain a parsable date. + + // HACK: LLDate apparently can't PARSE an rfc1123-format date, even though it can GENERATE one. + // The curl function curl_getdate can do this, but I'm hesitant to unilaterally introduce a curl dependency in LLDate. +#if 1 + time_t date = curl_getdate(date_string.c_str(), NULL ); + mDate.secondsSinceEpoch((F64)date); + lldebugs << " expire date parsed to: " << mDate.asRFC1123() << llendl; +#else + // This doesn't work (rfc1123-format dates cause it to fail) + if(!mDate.fromString(date_string)) + { + // Date failed to parse. + llwarns << "failed to parse cookie's expire date: " << date << llendl; + return false; + } +#endif + } + else if(matchName(name_start, name_end, "domain")) + { + mDomainStart = value_start; + mDomainEnd = value_end; + } + else if(matchName(name_start, name_end, "path")) + { + mPathStart = value_start; + mPathEnd = value_end; + } + else if(matchName(name_start, name_end, "max-age")) + { + // TODO: how should we handle this? + } + else if(matchName(name_start, name_end, "secure")) + { + // We don't care about the value of this field (yet) + } + else if(matchName(name_start, name_end, "version")) + { + // We don't care about the value of this field (yet) + } + else if(matchName(name_start, name_end, "comment")) + { + // We don't care about the value of this field (yet) + } + else + { + // An unknown field is a parse failure + return false; + } + + } + + + // move on to the next field, skipping this field's separator and any leading whitespace + field_start = mCookie.find_first_not_of("; ", next_field_start); + } + + // The cookie MUST have a name + if(mNameEnd <= mNameStart) + return false; + + return true; +} + +std::string::size_type LLPluginCookieStore::Cookie::findFieldEnd(std::string::size_type start, std::string::size_type end) +{ + std::string::size_type result = start; + + if(end == std::string::npos) + end = mCookie.size(); + + bool in_quotes = false; + for(; (result < end); result++) + { + switch(mCookie[result]) + { + case '\\': + if(in_quotes) + result++; // The next character is backslash-quoted. Skip over it. + break; + case '"': + in_quotes = !in_quotes; + break; + case ';': + if(!in_quotes) + return result; + break; + } + } + + // If we got here, no ';' was found. + return end; +} + +bool LLPluginCookieStore::Cookie::matchName(std::string::size_type start, std::string::size_type end, const char *name) +{ + // NOTE: this assumes 'name' is already in lowercase. The code which uses it should be able to arrange this... + + while((start < end) && (*name != '\0')) + { + if(tolower(mCookie[start]) != *name) + return false; + + start++; + name++; + } + + // iff both strings hit the end at the same time, they're equal. + return ((start == end) && (*name == '\0')); +} + +std::string LLPluginCookieStore::getAllCookies() +{ + std::stringstream result; + writeAllCookies(result); + return result.str(); +} + +void LLPluginCookieStore::writeAllCookies(std::ostream& s) +{ + cookie_map_t::iterator iter; + for(iter = mCookies.begin(); iter != mCookies.end(); iter++) + { + // Don't return expired cookies + if(!iter->second->isDead()) + { + s << (iter->second->getCookie()) << "\n"; + } + } + +} + +std::string LLPluginCookieStore::getPersistentCookies() +{ + std::stringstream result; + writePersistentCookies(result); + return result.str(); +} + +void LLPluginCookieStore::writePersistentCookies(std::ostream& s) +{ + cookie_map_t::iterator iter; + for(iter = mCookies.begin(); iter != mCookies.end(); iter++) + { + // Don't return expired cookies or session cookies + if(!iter->second->isDead() && !iter->second->isSessionCookie()) + { + s << iter->second->getCookie() << "\n"; + } + } +} + +std::string LLPluginCookieStore::getChangedCookies(bool clear_changed) +{ + std::stringstream result; + writeChangedCookies(result, clear_changed); + + return result.str(); +} + +void LLPluginCookieStore::writeChangedCookies(std::ostream& s, bool clear_changed) +{ + if(mHasChangedCookies) + { + lldebugs << "returning changed cookies: " << llendl; + cookie_map_t::iterator iter; + for(iter = mCookies.begin(); iter != mCookies.end(); ) + { + cookie_map_t::iterator next = iter; + next++; + + // Only return cookies marked as "changed" + if(iter->second->isChanged()) + { + s << iter->second->getCookie() << "\n"; + + lldebugs << " " << iter->second->getCookie() << llendl; + + // If requested, clear the changed mark + if(clear_changed) + { + if(iter->second->isDead()) + { + // If this cookie was previously marked dead, it needs to be removed entirely. + delete iter->second; + mCookies.erase(iter); + } + else + { + // Not dead, just mark as not changed. + iter->second->setChanged(false); + } + } + } + + iter = next; + } + } + + if(clear_changed) + mHasChangedCookies = false; +} + +void LLPluginCookieStore::setAllCookies(const std::string &cookies, bool mark_changed) +{ + clearCookies(); + setCookies(cookies, mark_changed); +} + +void LLPluginCookieStore::readAllCookies(std::istream& s, bool mark_changed) +{ + clearCookies(); + readCookies(s, mark_changed); +} + +void LLPluginCookieStore::setCookies(const std::string &cookies, bool mark_changed) +{ + std::string::size_type start = 0; + + while(start != std::string::npos) + { + std::string::size_type end = cookies.find('\n', start); + if(end > start) + { + // The line is non-empty. Try to create a cookie from it. + setOneCookie(cookies, start, end, mark_changed); + } + start = cookies.find_first_not_of("\n ", end); + } +} + +void LLPluginCookieStore::readCookies(std::istream& s, bool mark_changed) +{ + std::string line; + while(s.good() && !s.eof()) + { + std::getline(s, line); + if(!line.empty()) + { + // Try to create a cookie from this line. + setOneCookie(line, 0, std::string::npos, mark_changed); + } + } +} + +std::string LLPluginCookieStore::quoteString(const std::string &s) +{ + std::stringstream result; + + result << '"'; + + for(std::string::size_type i = 0; i < s.size(); ++i) + { + char c = s[i]; + switch(c) + { + // All these separators need to be quoted in HTTP headers, according to section 2.2 of rfc 2616: + case '(': case ')': case '<': case '>': case '@': + case ',': case ';': case ':': case '\\': case '"': + case '/': case '[': case ']': case '?': case '=': + case '{': case '}': case ' ': case '\t': + result << '\\'; + break; + } + + result << c; + } + + result << '"'; + + return result.str(); +} + +std::string LLPluginCookieStore::unquoteString(const std::string &s) +{ + std::stringstream result; + + bool in_quotes = false; + + for(std::string::size_type i = 0; i < s.size(); ++i) + { + char c = s[i]; + switch(c) + { + case '\\': + if(in_quotes) + { + // The next character is backslash-quoted. Pass it through untouched. + ++i; + if(i < s.size()) + { + result << s[i]; + } + continue; + } + break; + case '"': + in_quotes = !in_quotes; + continue; + break; + } + + result << c; + } + + return result.str(); +} + +// The flow for deleting a cookie is non-obvious enough that I should call it out here... +// Deleting a cookie is done by setting a cookie with the same name, path, and domain, but with an expire timestamp in the past. +// (This is exactly how a web server tells a browser to delete a cookie.) +// When deleting with mark_changed set to true, this replaces the existing cookie in the list with an entry that's marked both dead and changed. +// Some time later when writeChangedCookies() is called with clear_changed set to true, the dead cookie is deleted from the list after being returned, so that the +// delete operation (in the form of the expired cookie) is passed along. +void LLPluginCookieStore::setOneCookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, bool mark_changed) +{ + Cookie *cookie = Cookie::createFromString(s, cookie_start, cookie_end); + if(cookie) + { + lldebugs << "setting cookie: " << cookie->getCookie() << llendl; + + // Create a key for this cookie + std::string key = cookie->getKey(); + + // Check to see whether this cookie should have expired + if(!cookie->isSessionCookie() && (cookie->getDate() < LLDate::now())) + { + // This cookie has expired. + if(mark_changed) + { + // If we're marking cookies as changed, we should keep it anyway since we'll need to send it out with deltas. + cookie->setDead(true); + lldebugs << " marking dead" << llendl; + } + else + { + // If we're not marking cookies as changed, we don't need to keep this cookie at all. + // If the cookie was already in the list, delete it. + removeCookie(key); + + delete cookie; + cookie = NULL; + + lldebugs << " removing" << llendl; + } + } + + if(cookie) + { + // If it already exists in the map, replace it. + cookie_map_t::iterator iter = mCookies.find(key); + if(iter != mCookies.end()) + { + if(iter->second->getCookie() == cookie->getCookie()) + { + // The new cookie is identical to the old -- don't mark as changed. + // Just leave the old one in the map. + delete cookie; + cookie = NULL; + + lldebugs << " unchanged" << llendl; + } + else + { + // A matching cookie was already in the map. Replace it. + delete iter->second; + iter->second = cookie; + + cookie->setChanged(mark_changed); + if(mark_changed) + mHasChangedCookies = true; + + lldebugs << " replacing" << llendl; + } + } + else + { + // The cookie wasn't in the map. Insert it. + mCookies.insert(std::make_pair(key, cookie)); + + cookie->setChanged(mark_changed); + if(mark_changed) + mHasChangedCookies = true; + + lldebugs << " adding" << llendl; + } + } + } +} + +void LLPluginCookieStore::clearCookies() +{ + while(!mCookies.empty()) + { + cookie_map_t::iterator iter = mCookies.begin(); + delete iter->second; + mCookies.erase(iter); + } +} + +void LLPluginCookieStore::removeCookie(const std::string &key) +{ + cookie_map_t::iterator iter = mCookies.find(key); + if(iter != mCookies.end()) + { + delete iter->second; + mCookies.erase(iter); + } +} + diff --git a/indra/llplugin/llplugincookiestore.h b/indra/llplugin/llplugincookiestore.h new file mode 100644 index 0000000000..5250f008b6 --- /dev/null +++ b/indra/llplugin/llplugincookiestore.h @@ -0,0 +1,122 @@ +/** + * @file llplugincookiestore.h + * @brief LLPluginCookieStore provides central storage for http cookies used by plugins + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * @endcond + */ + +#ifndef LL_LLPLUGINCOOKIESTORE_H +#define LL_LLPLUGINCOOKIESTORE_H + +#include "lldate.h" +#include +#include +#include + +class LLPluginCookieStore +{ + LOG_CLASS(LLPluginCookieStore); +public: + LLPluginCookieStore(); + ~LLPluginCookieStore(); + + // gets all cookies currently in storage -- use when initializing a plugin + std::string getAllCookies(); + void writeAllCookies(std::ostream& s); + + // gets only persistent cookies (i.e. not session cookies) -- use when writing cookies to a file + std::string getPersistentCookies(); + void writePersistentCookies(std::ostream& s); + + // gets cookies which are marked as "changed" -- use when sending periodic updates to plugins + std::string getChangedCookies(bool clear_changed = true); + void writeChangedCookies(std::ostream& s, bool clear_changed = true); + + // (re)initializes internal data structures and bulk-sets cookies -- use when reading cookies from a file + void setAllCookies(const std::string &cookies, bool mark_changed = false); + void readAllCookies(std::istream& s, bool mark_changed = false); + + // sets one or more cookies (without reinitializing anything) -- use when receiving cookies from a plugin + void setCookies(const std::string &cookies, bool mark_changed = true); + void readCookies(std::istream& s, bool mark_changed = true); + + // quote or unquote a string as per the definition of 'quoted-string' in rfc2616 + static std::string quoteString(const std::string &s); + static std::string unquoteString(const std::string &s); + +private: + + void setOneCookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, bool mark_changed); + + class Cookie + { + public: + static Cookie *createFromString(const std::string &s, std::string::size_type cookie_start = 0, std::string::size_type cookie_end = std::string::npos); + + // Construct a string from the cookie that uniquely represents it, to be used as a key in a std::map. + std::string getKey() const; + + const std::string &getCookie() const { return mCookie; }; + bool isSessionCookie() const { return mDate.isNull(); }; + + bool isDead() const { return mDead; }; + void setDead(bool dead) { mDead = dead; }; + + bool isChanged() const { return mChanged; }; + void setChanged(bool changed) { mChanged = changed; }; + + const LLDate &getDate() const { return mDate; }; + + private: + Cookie(const std::string &s, std::string::size_type cookie_start = 0, std::string::size_type cookie_end = std::string::npos); + bool parse(); + std::string::size_type findFieldEnd(std::string::size_type start = 0, std::string::size_type end = std::string::npos); + bool matchName(std::string::size_type start, std::string::size_type end, const char *name); + + std::string mCookie; // The full cookie, in RFC 2109 string format + LLDate mDate; // The expiration date of the cookie. For session cookies, this will be a null date (mDate.isNull() is true). + // Start/end indices of various parts of the cookie string. Stored as indices into the string to save space and time. + std::string::size_type mNameStart, mNameEnd; + std::string::size_type mValueStart, mValueEnd; + std::string::size_type mDomainStart, mDomainEnd; + std::string::size_type mPathStart, mPathEnd; + bool mDead; + bool mChanged; + }; + + typedef std::map cookie_map_t; + + cookie_map_t mCookies; + bool mHasChangedCookies; + + void clearCookies(); + void removeCookie(const std::string &key); +}; + +#endif // LL_LLPLUGINCOOKIESTORE_H diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp index 0462fce236..85d6b2f5ff 100644 --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -507,6 +507,19 @@ private: sendMessage(message); } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onCookieChanged(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "cookie_set"); + message.setValue("cookie", event.getStringValue()); + // These could be passed through as well, but aren't really needed. +// message.setValue("uri", event.getEventUri()); +// message.setValueBoolean("dead", (event.getIntValue() != 0)) + sendMessage(message); + } + LLQtWebKit::EKeyboardModifier decodeModifiers(std::string &modifiers) { int result = 0; @@ -1051,6 +1064,10 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) mJavascriptEnabled = message_in.getValueBoolean("enable"); //LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled ); } + else if(message_name == "set_cookies") + { + LLQtWebKit::getInstance()->setCookies(message_in.getValue("cookies")); + } else if(message_name == "proxy_setup") { bool val = message_in.getValueBoolean("enable"); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index d30d7fd26d..a3d0b8d8d9 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1526,6 +1526,8 @@ bool LLAppViewer::cleanup() LLLocationHistory::getInstance()->save(); LLAvatarIconIDCache::getInstance()->save(); + + LLViewerMedia::saveCookieFile(); llinfos << "Shutting down Threads" << llendflush; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index edd03dc836..59d118abe2 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -938,6 +938,9 @@ bool idle_startup() // Load Avatars icons cache LLAvatarIconIDCache::getInstance()->load(); + + // Load media plugin cookies + LLViewerMedia::loadCookieFile(); //------------------------------------------------- // Handle startup progress screen diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 2fcd3f1114..8863bf187d 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -45,6 +45,7 @@ #include "llviewertexturelist.h" #include "llvovolume.h" #include "llpluginclassmedia.h" +#include "llplugincookiestore.h" #include "llviewerwindow.h" #include "llfocusmgr.h" #include "llcallbacklist.h" @@ -256,6 +257,7 @@ public: LLViewerMediaImpl *mMediaImpl; bool mInitialized; }; +LLPluginCookieStore *LLViewerMedia::sCookieStore = NULL; static LLViewerMedia::impl_list sViewerMediaImplList; static LLViewerMedia::impl_id_map sViewerMediaTextureIDMap; static LLTimer sMediaCreateTimer; @@ -264,6 +266,8 @@ static F32 sGlobalVolume = 1.0f; static F64 sLowestLoadableImplInterest = 0.0f; static bool sAnyMediaShowing = false; static boost::signals2::connection sTeleportFinishConnection; +static std::string sUpdatedCookies; +static const char *PLUGIN_COOKIE_FILE_NAME = "plugin_cookies.txt"; ////////////////////////////////////////////////////////////////////////////////////////// static void add_media_impl(LLViewerMediaImpl* media) @@ -399,7 +403,6 @@ viewer_media_t LLViewerMedia::updateMediaImpl(LLMediaEntry* media_entry, const s media_impl->setHomeURL(media_entry->getHomeURL()); media_impl->mMediaAutoPlay = media_entry->getAutoPlay(); media_impl->mMediaEntryURL = media_entry->getCurrentURL(); - if(media_impl->isAutoPlayable()) { needs_navigate = true; @@ -698,6 +701,13 @@ static bool proximity_comparitor(const LLViewerMediaImpl* i1, const LLViewerMedi void LLViewerMedia::updateMedia(void *dummy_arg) { sAnyMediaShowing = false; + sUpdatedCookies = getCookieStore()->getChangedCookies(); + if(!sUpdatedCookies.empty()) + { + lldebugs << "updated cookies will be sent to all loaded plugins: " << llendl; + lldebugs << sUpdatedCookies << llendl; + } + impl_list::iterator iter = sViewerMediaImplList.begin(); impl_list::iterator end = sViewerMediaImplList.end(); @@ -1095,6 +1105,116 @@ void LLViewerMedia::setProxyConfig(bool enable, const std::string &host, int por ///////////////////////////////////////////////////////////////////////////////////////// // static +///////////////////////////////////////////////////////////////////////////////////////// +// static +LLPluginCookieStore *LLViewerMedia::getCookieStore() +{ + if(sCookieStore == NULL) + { + sCookieStore = new LLPluginCookieStore; + } + + return sCookieStore; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::loadCookieFile() +{ + // build filename for each user + std::string resolved_filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PLUGIN_COOKIE_FILE_NAME); + + if (resolved_filename.empty()) + { + llinfos << "can't get path to plugin cookie file - probably not logged in yet." << llendl; + return; + } + + // open the file for reading + llifstream file(resolved_filename); + if (!file.is_open()) + { + llwarns << "can't load plugin cookies from file \"" << PLUGIN_COOKIE_FILE_NAME << "\"" << llendl; + return; + } + + getCookieStore()->readAllCookies(file, true); + + file.close(); + + // TODO: send the clear_cookies message to all loaded plugins +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::saveCookieFile() +{ + // build filename for each user + std::string resolved_filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PLUGIN_COOKIE_FILE_NAME); + + if (resolved_filename.empty()) + { + llinfos << "can't get path to plugin cookie file - probably not logged in yet." << llendl; + return; + } + + // open a file for writing + llofstream file (resolved_filename); + if (!file.is_open()) + { + llwarns << "can't open plugin cookie file \"" << PLUGIN_COOKIE_FILE_NAME << "\" for writing" << llendl; + return; + } + + getCookieStore()->writePersistentCookies(file); + + file.close(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::addCookie(const std::string &name, const std::string &value, const std::string &domain, const LLDate &expires, const std::string &path, bool secure) +{ + std::stringstream cookie; + + cookie << name << "=" << LLPluginCookieStore::quoteString(value); + + if(expires.notNull()) + { + cookie << "; expires=" << expires.asRFC1123(); + } + + cookie << "; domain=" << domain; + + cookie << "; path=" << path; + + if(secure) + { + cookie << "; secure"; + } + + getCookieStore()->setCookies(cookie.str()); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::addSessionCookie(const std::string &name, const std::string &value, const std::string &domain, const std::string &path, bool secure) +{ + // A session cookie just has a NULL date. + addCookie(name, value, domain, LLDate(), path, secure); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::removeCookie(const std::string &name, const std::string &domain, const std::string &path ) +{ + // To remove a cookie, add one with the same name, domain, and path that expires in the past. + + addCookie(name, "", domain, LLDate(LLDate::now().secondsSinceEpoch() - 1.0), path); +} + + bool LLViewerMedia::hasInWorldMedia() { if (sInWorldMediaDisabled) return false; @@ -1455,6 +1575,17 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) media_source->clear_cache(); } + // TODO: Only send cookies to plugins that need them + // Ideally, the plugin should tell us whether it handles cookies or not -- either via the init response or through a separate message. + // Due to the ordering of messages, it's possible we wouldn't get that information back in time to send cookies before sending a navigate message, + // which could cause odd race conditions. + std::string all_cookies = LLViewerMedia::getCookieStore()->getAllCookies(); + lldebugs << "setting cookies: " << all_cookies << llendl; + if(!all_cookies.empty()) + { + media_source->set_cookies(all_cookies); + } + mMediaSource = media_source; updateVolume(); @@ -2152,6 +2283,16 @@ void LLViewerMediaImpl::update() } } } + else + { + // If we didn't just create the impl, it may need to get cookie updates. + if(!sUpdatedCookies.empty()) + { + // TODO: Only send cookies to plugins that need them + mMediaSource->set_cookies(sUpdatedCookies); + } + } + if(mMediaSource == NULL) { @@ -2612,6 +2753,13 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla emitEvent(plugin, event); } +//////////////////////////////////////////////////////////////////////////////// +// virtual +void LLViewerMediaImpl::handleCookieSet(LLPluginClassMedia* self, const std::string &cookie) +{ + LLViewerMedia::getCookieStore()->setCookies(cookie); +} + //////////////////////////////////////////////////////////////////////////////// // virtual void diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index f9870fb3b9..10dacf9532 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -50,6 +50,7 @@ class LLViewerMediaTexture; class LLMediaEntry; class LLVOVolume; class LLMimeDiscoveryResponder; +class LLPluginCookieStore; typedef LLPointer viewer_media_t; /////////////////////////////////////////////////////////////////////////////// @@ -145,8 +146,17 @@ public: // Set the proxy config for all loaded plugins static void setProxyConfig(bool enable, const std::string &host, int port); + static LLPluginCookieStore *getCookieStore(); + static void loadCookieFile(); + static void saveCookieFile(); + static void addCookie(const std::string &name, const std::string &value, const std::string &domain, const LLDate &expires, const std::string &path = std::string("/"), bool secure = false ); + static void addSessionCookie(const std::string &name, const std::string &value, const std::string &domain, const std::string &path = std::string("/"), bool secure = false ); + static void removeCookie(const std::string &name, const std::string &domain, const std::string &path = std::string("/") ); + private: static void onTeleportFinished(); + + static LLPluginCookieStore *sCookieStore; }; // Implementation functions not exported into header file @@ -294,6 +304,7 @@ public: // Inherited from LLPluginClassMediaOwner /*virtual*/ void handleMediaEvent(LLPluginClassMedia* plugin, LLPluginClassMediaOwner::EMediaEvent); + /*virtual*/ void handleCookieSet(LLPluginClassMedia* self, const std::string &cookie); // LLEditMenuHandler overrides /*virtual*/ void cut(); diff --git a/install.xml b/install.xml index 0f4ab373f5..25c9fdd045 100644 --- a/install.xml +++ b/install.xml @@ -948,9 +948,9 @@ anguage Infrstructure (CLI) international standard darwin md5sum - ada82d41467cdb7f8e25a442f58b6963 + 171bd85ebb81d319e1f15fab8092f8cd url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/llqtwebkit-4.6-darwin-20100318.tar.bz2 + http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/llqtwebkit-4.6-darwin-20100326.tar.bz2 linux @@ -962,9 +962,9 @@ anguage Infrstructure (CLI) international standard windows md5sum - 93b49cfea365fe082eda3e466a9beec3 + 04d86bb2eeed4f928d155cb5598ca6b5 url - http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/llqtwebkit-windows-qt4.6-20100318.tar.bz2 + http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/llqtwebkit-windows-qt4.6-20100326.tar.bz2 -- cgit v1.3