Fixed web browser thanks to ArminW/Imprudence
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
project(llplugin)
|
||||
|
||||
include(00-Common)
|
||||
include(CURL)
|
||||
include(LLCommon)
|
||||
include(LLImage)
|
||||
include(LLMath)
|
||||
@@ -19,10 +20,12 @@ include_directories(
|
||||
${LLRENDER_INCLUDE_DIRS}
|
||||
${LLXML_INCLUDE_DIRS}
|
||||
${LLWINDOW_INCLUDE_DIRS}
|
||||
${LLQTWEBKIT_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
set(llplugin_SOURCE_FILES
|
||||
llpluginclassmedia.cpp
|
||||
llplugincookiestore.cpp
|
||||
llplugininstance.cpp
|
||||
llpluginmessage.cpp
|
||||
llpluginmessagepipe.cpp
|
||||
@@ -36,6 +39,7 @@ set(llplugin_HEADER_FILES
|
||||
|
||||
llpluginclassmedia.h
|
||||
llpluginclassmediaowner.h
|
||||
llplugincookiestore.h
|
||||
llplugininstance.h
|
||||
llpluginmessage.h
|
||||
llpluginmessageclasses.h
|
||||
@@ -48,20 +52,32 @@ set(llplugin_HEADER_FILES
|
||||
set_source_files_properties(${llplugin_HEADER_FILES}
|
||||
PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
if(WORD_SIZE EQUAL 64)
|
||||
if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
|
||||
if(WINDOWS)
|
||||
add_definitions(/FIXED:NO)
|
||||
else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
|
||||
add_definitions(-fPIC)
|
||||
endif(WINDOWS)
|
||||
endif (WORD_SIZE EQUAL 64)
|
||||
endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
|
||||
|
||||
list(APPEND llplugin_SOURCE_FILES ${llplugin_HEADER_FILES})
|
||||
|
||||
add_library (llplugin ${llplugin_SOURCE_FILES})
|
||||
|
||||
add_dependencies(llplugin
|
||||
prepare
|
||||
)
|
||||
|
||||
add_subdirectory(slplugin)
|
||||
|
||||
# # Add tests
|
||||
# include(LLAddBuildTest)
|
||||
# # UNIT TESTS
|
||||
# SET(llplugin_TEST_SOURCE_FILES
|
||||
# llplugincookiestore.cpp
|
||||
# )
|
||||
#
|
||||
# # llplugincookiestore has a dependency on curl, so we need to link the curl library into the test.
|
||||
# set_source_files_properties(
|
||||
# llplugincookiestore.cpp
|
||||
# PROPERTIES
|
||||
# LL_TEST_ADDITIONAL_LIBRARIES "${CURL_LIBRARIES}"
|
||||
# )
|
||||
#
|
||||
# LL_ADD_PROJECT_UNIT_TESTS(llplugin "${llplugin_TEST_SOURCE_FILES}")
|
||||
|
||||
93
indra/llplugin/llpluginclassmedia.cpp
Normal file → Executable file
93
indra/llplugin/llpluginclassmedia.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginclassmedia.cpp
|
||||
* @brief LLPluginClassMedia handles a plugin which knows about the "media" message class.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
@@ -36,6 +39,8 @@
|
||||
#include "llpluginclassmedia.h"
|
||||
#include "llpluginmessageclasses.h"
|
||||
|
||||
#include "llqtwebkit.h"
|
||||
|
||||
static int LOW_PRIORITY_TEXTURE_SIZE_DEFAULT = 256;
|
||||
|
||||
static int nextPowerOf2( int value )
|
||||
@@ -54,23 +59,31 @@ LLPluginClassMedia::LLPluginClassMedia(LLPluginClassMediaOwner *owner)
|
||||
mOwner = owner;
|
||||
mPlugin = NULL;
|
||||
reset();
|
||||
|
||||
//debug use
|
||||
mDeleteOK = true ;
|
||||
}
|
||||
|
||||
|
||||
LLPluginClassMedia::~LLPluginClassMedia()
|
||||
{
|
||||
llassert_always(mDeleteOK) ;
|
||||
reset();
|
||||
}
|
||||
|
||||
bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug, const std::string &user_data_path)
|
||||
bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug)
|
||||
{
|
||||
LL_DEBUGS("Plugin") << "launcher: " << launcher_filename << LL_ENDL;
|
||||
LL_DEBUGS("Plugin") << "plugin: " << plugin_filename << LL_ENDL;
|
||||
LL_DEBUGS("Plugin") << "user_data_path: " << user_data_path << LL_ENDL;
|
||||
|
||||
mPlugin = new LLPluginProcessParent(this);
|
||||
mPlugin->setSleepTime(mSleepTime);
|
||||
mPlugin->init(launcher_filename, plugin_filename, debug, user_data_path);
|
||||
|
||||
// Queue up the media init message -- it will be sent after all the currently queued messages.
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "init");
|
||||
sendMessage(message);
|
||||
|
||||
mPlugin->init(launcher_filename, plugin_filename, debug);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -101,6 +114,8 @@ void LLPluginClassMedia::reset()
|
||||
mSetMediaHeight = -1;
|
||||
mRequestedMediaWidth = 0;
|
||||
mRequestedMediaHeight = 0;
|
||||
mRequestedTextureWidth = 0;
|
||||
mRequestedTextureHeight = 0;
|
||||
mFullMediaWidth = 0;
|
||||
mFullMediaHeight = 0;
|
||||
mTextureWidth = 0;
|
||||
@@ -123,7 +138,8 @@ void LLPluginClassMedia::reset()
|
||||
mCanPaste = false;
|
||||
mMediaName.clear();
|
||||
mMediaDescription.clear();
|
||||
|
||||
mBackgroundColor = LLColor4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
// media_browser class
|
||||
mNavigateURI.clear();
|
||||
mNavigateResultCode = -1;
|
||||
@@ -132,6 +148,8 @@ void LLPluginClassMedia::reset()
|
||||
mHistoryForwardAvailable = false;
|
||||
mStatusText.clear();
|
||||
mProgressPercent = 0;
|
||||
mClickURL.clear();
|
||||
mClickTarget.clear();
|
||||
|
||||
// media_time class
|
||||
mCurrentTime = 0.0f;
|
||||
@@ -147,7 +165,7 @@ void LLPluginClassMedia::idle(void)
|
||||
mPlugin->idle();
|
||||
}
|
||||
|
||||
if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL))
|
||||
if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL) || (mPlugin->isBlocked()))
|
||||
{
|
||||
// Can't process a size change at this time
|
||||
}
|
||||
@@ -233,6 +251,10 @@ void LLPluginClassMedia::idle(void)
|
||||
message.setValueS32("height", mRequestedMediaHeight);
|
||||
message.setValueS32("texture_width", mRequestedTextureWidth);
|
||||
message.setValueS32("texture_height", mRequestedTextureHeight);
|
||||
message.setValueReal("background_r", mBackgroundColor.mV[VX]);
|
||||
message.setValueReal("background_g", mBackgroundColor.mV[VY]);
|
||||
message.setValueReal("background_b", mBackgroundColor.mV[VZ]);
|
||||
message.setValueReal("background_a", mBackgroundColor.mV[VW]);
|
||||
mPlugin->sendMessage(message); // DO NOT just use sendMessage() here -- we want this to jump ahead of the queue.
|
||||
|
||||
LL_DEBUGS("Plugin") << "Sending size_change" << LL_ENDL;
|
||||
@@ -420,6 +442,12 @@ void LLPluginClassMedia::mouseEvent(EMouseEventType type, int button, int x, int
|
||||
{
|
||||
if(type == MOUSE_EVENT_MOVE)
|
||||
{
|
||||
if(!mPlugin || !mPlugin->isRunning() || mPlugin->isBlocked())
|
||||
{
|
||||
// Don't queue up mouse move events that can't be delivered.
|
||||
return;
|
||||
}
|
||||
|
||||
if((x == mLastMouseX) && (y == mLastMouseY))
|
||||
{
|
||||
// Don't spam unnecessary mouse move events.
|
||||
@@ -458,7 +486,7 @@ void LLPluginClassMedia::mouseEvent(EMouseEventType type, int button, int x, int
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
bool LLPluginClassMedia::keyEvent(EKeyEventType type, int key_code, MASK modifiers)
|
||||
bool LLPluginClassMedia::keyEvent(EKeyEventType type, int key_code, MASK modifiers, LLSD native_key_data)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
@@ -515,6 +543,7 @@ bool LLPluginClassMedia::keyEvent(EKeyEventType type, int key_code, MASK modifie
|
||||
message.setValueS32("key", key_code);
|
||||
|
||||
message.setValue("modifiers", translateModifiers(modifiers));
|
||||
message.setValueLLSD("native_key_data", native_key_data);
|
||||
|
||||
sendMessage(message);
|
||||
}
|
||||
@@ -533,12 +562,13 @@ void LLPluginClassMedia::scrollEvent(int x, int y, MASK modifiers)
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
bool LLPluginClassMedia::textInput(const std::string &text, MASK modifiers)
|
||||
bool LLPluginClassMedia::textInput(const std::string &text, MASK modifiers, LLSD native_key_data)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "text_event");
|
||||
|
||||
message.setValue("text", text);
|
||||
message.setValue("modifiers", translateModifiers(modifiers));
|
||||
message.setValueLLSD("native_key_data", native_key_data);
|
||||
|
||||
sendMessage(message);
|
||||
|
||||
@@ -663,6 +693,34 @@ void LLPluginClassMedia::paste()
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
void LLPluginClassMedia::setUserDataPath(const std::string &user_data_path)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "set_user_data_path");
|
||||
message.setValue("path", user_data_path);
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
void LLPluginClassMedia::setLanguageCode(const std::string &language_code)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "set_language_code");
|
||||
message.setValue("language", 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);
|
||||
}
|
||||
|
||||
/* virtual */
|
||||
void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)
|
||||
{
|
||||
@@ -923,6 +981,13 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)
|
||||
mClickTarget.clear();
|
||||
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;
|
||||
@@ -1006,9 +1071,17 @@ 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");
|
||||
message.setValueBoolean("enable", enable);
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
|
||||
39
indra/llplugin/llpluginclassmedia.h
Normal file → Executable file
39
indra/llplugin/llpluginclassmedia.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginclassmedia.h
|
||||
* @brief LLPluginClassMedia handles interaction with a plugin which knows about the "media" message class.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINCLASSMEDIA_H
|
||||
@@ -38,7 +41,7 @@
|
||||
#include "llrect.h"
|
||||
#include "llpluginclassmediaowner.h"
|
||||
#include <queue>
|
||||
|
||||
#include "v4color.h"
|
||||
|
||||
class LLPluginClassMedia : public LLPluginProcessParentOwner
|
||||
{
|
||||
@@ -48,7 +51,9 @@ public:
|
||||
virtual ~LLPluginClassMedia();
|
||||
|
||||
// local initialization, called by the media manager when creating a source
|
||||
virtual bool init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug, const std::string &user_data_path);
|
||||
virtual bool init(const std::string &launcher_filename,
|
||||
const std::string &plugin_filename,
|
||||
bool debug);
|
||||
|
||||
// undoes everything init() didm called by the media manager when destroying a source
|
||||
virtual void reset();
|
||||
@@ -85,6 +90,8 @@ public:
|
||||
void setSize(int width, int height);
|
||||
void setAutoScale(bool auto_scale);
|
||||
|
||||
void setBackgroundColor(LLColor4 color) { mBackgroundColor = color; };
|
||||
|
||||
// Returns true if all of the texture parameters (depth, format, size, and texture size) are set up and consistent.
|
||||
// This will initially be false, and will also be false for some time after setSize while the resize is processed.
|
||||
// Note that if this returns true, it is safe to use all the get() functions above without checking for invalid return values
|
||||
@@ -111,12 +118,12 @@ public:
|
||||
KEY_EVENT_REPEAT
|
||||
}EKeyEventType;
|
||||
|
||||
bool keyEvent(EKeyEventType type, int key_code, MASK modifiers);
|
||||
bool keyEvent(EKeyEventType type, int key_code, MASK modifiers, LLSD native_key_data);
|
||||
|
||||
void scrollEvent(int x, int y, MASK modifiers);
|
||||
|
||||
// Text may be unicode (utf8 encoded)
|
||||
bool textInput(const std::string &text, MASK modifiers);
|
||||
bool textInput(const std::string &text, MASK modifiers, LLSD native_key_data);
|
||||
|
||||
void loadURI(const std::string &uri);
|
||||
|
||||
@@ -170,6 +177,12 @@ public:
|
||||
|
||||
void paste();
|
||||
bool canPaste() const { return mCanPaste; };
|
||||
|
||||
// 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
|
||||
@@ -178,6 +191,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();
|
||||
@@ -211,6 +225,7 @@ public:
|
||||
// This is valid after MEDIA_EVENT_CLICK_LINK_HREF
|
||||
std::string getClickTarget() const { return mClickTarget; };
|
||||
|
||||
|
||||
std::string getMediaName() const { return mMediaName; };
|
||||
std::string getMediaDescription() const { return mMediaDescription; };
|
||||
|
||||
@@ -327,6 +342,8 @@ protected:
|
||||
std::string mMediaName;
|
||||
std::string mMediaDescription;
|
||||
|
||||
LLColor4 mBackgroundColor;
|
||||
|
||||
/////////////////////////////////////////
|
||||
// media_browser class
|
||||
std::string mNavigateURI;
|
||||
@@ -347,6 +364,14 @@ protected:
|
||||
F64 mCurrentRate;
|
||||
F64 mLoadedDuration;
|
||||
|
||||
//--------------------------------------
|
||||
//debug use only
|
||||
//
|
||||
private:
|
||||
bool mDeleteOK ;
|
||||
public:
|
||||
void setDeleteOK(bool flag) { mDeleteOK = flag ;}
|
||||
//--------------------------------------
|
||||
};
|
||||
|
||||
#endif // LL_LLPLUGINCLASSMEDIA_H
|
||||
|
||||
11
indra/llplugin/llpluginclassmediaowner.h
Normal file → Executable file
11
indra/llplugin/llpluginclassmediaowner.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginclassmediaowner.h
|
||||
* @brief LLPluginClassMedia handles interaction with a plugin which knows about the "media" message class.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINCLASSMEDIAOWNER_H
|
||||
@@ -38,6 +41,7 @@
|
||||
#include <queue>
|
||||
|
||||
class LLPluginClassMedia;
|
||||
class LLPluginCookieStore;
|
||||
|
||||
class LLPluginClassMediaOwner
|
||||
{
|
||||
@@ -77,6 +81,7 @@ public:
|
||||
|
||||
virtual ~LLPluginClassMediaOwner() {};
|
||||
virtual void handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent /*event*/) {};
|
||||
virtual void handleCookieSet(LLPluginClassMedia* /*self*/, const std::string &/*cookie*/) {};
|
||||
};
|
||||
|
||||
#endif // LL_LLPLUGINCLASSMEDIAOWNER_H
|
||||
|
||||
671
indra/llplugin/llplugincookiestore.cpp
Normal file
671
indra/llplugin/llplugincookiestore.cpp
Normal file
@@ -0,0 +1,671 @@
|
||||
/**
|
||||
* @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 <iostream>
|
||||
|
||||
// for curl_getdate() (apparently parsing RFC 1123 dates is hard)
|
||||
#include <curl/curl.h>
|
||||
|
||||
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 - cookie_start),
|
||||
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, const std::string &host)
|
||||
{
|
||||
Cookie *result = new Cookie(s, cookie_start, cookie_end);
|
||||
|
||||
if(!result->parse(host))
|
||||
{
|
||||
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(const std::string &host)
|
||||
{
|
||||
bool first_field = true;
|
||||
|
||||
std::string::size_type cookie_end = mCookie.size();
|
||||
std::string::size_type field_start = 0;
|
||||
|
||||
LL_DEBUGS("CookieStoreParse") << "parsing cookie: " << mCookie << LL_ENDL;
|
||||
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;
|
||||
}
|
||||
|
||||
LL_DEBUGS("CookieStoreParse")
|
||||
<< " field name: \"" << mCookie.substr(name_start, name_end - name_start)
|
||||
<< "\", value: \"" << mCookie.substr(value_start, value_end - value_start) << "\""
|
||||
<< LL_ENDL;
|
||||
|
||||
// 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);
|
||||
LL_DEBUGS("CookieStoreParse") << " expire date parsed to: " << mDate.asRFC1123() << LL_ENDL;
|
||||
#else
|
||||
// This doesn't work (rfc1123-format dates cause it to fail)
|
||||
if(!mDate.fromString(date_string))
|
||||
{
|
||||
// Date failed to parse.
|
||||
LL_WARNS("CookieStoreParse") << "failed to parse cookie's expire date: " << date << LL_ENDL;
|
||||
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 if(matchName(name_start, name_end, "httponly"))
|
||||
{
|
||||
// We don't care about the value of this field (yet)
|
||||
}
|
||||
else
|
||||
{
|
||||
// An unknown field is a parse failure
|
||||
LL_WARNS("CookieStoreParse") << "unexpected field name: " << mCookie.substr(name_start, name_end - name_start) << LL_ENDL;
|
||||
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;
|
||||
|
||||
// If the cookie doesn't have a domain, add the current host as the domain.
|
||||
if(mDomainEnd <= mDomainStart)
|
||||
{
|
||||
if(host.empty())
|
||||
{
|
||||
// no domain and no current host -- this is a parse failure.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Figure out whether this cookie ended with a ";" or not...
|
||||
std::string::size_type last_char = mCookie.find_last_not_of(" ");
|
||||
if((last_char != std::string::npos) && (mCookie[last_char] != ';'))
|
||||
{
|
||||
mCookie += ";";
|
||||
}
|
||||
|
||||
mCookie += " domain=";
|
||||
mDomainStart = mCookie.size();
|
||||
mCookie += host;
|
||||
mDomainEnd = mCookie.size();
|
||||
|
||||
LL_DEBUGS("CookieStoreParse") << "added domain (" << mDomainStart << " to " << mDomainEnd << "), new cookie is: " << mCookie << LL_ENDL;
|
||||
}
|
||||
|
||||
// If the cookie doesn't have a path, add "/".
|
||||
if(mPathEnd <= mPathStart)
|
||||
{
|
||||
// Figure out whether this cookie ended with a ";" or not...
|
||||
std::string::size_type last_char = mCookie.find_last_not_of(" ");
|
||||
if((last_char != std::string::npos) && (mCookie[last_char] != ';'))
|
||||
{
|
||||
mCookie += ";";
|
||||
}
|
||||
|
||||
mCookie += " path=";
|
||||
mPathStart = mCookie.size();
|
||||
mCookie += "/";
|
||||
mPathEnd = mCookie.size();
|
||||
|
||||
LL_DEBUGS("CookieStoreParse") << "added path (" << mPathStart << " to " << mPathEnd << "), new cookie is: " << mCookie << LL_ENDL;
|
||||
}
|
||||
|
||||
|
||||
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_first_of("\r\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("\r\n ", end);
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginCookieStore::setCookiesFromHost(const std::string &cookies, const std::string &host, bool mark_changed)
|
||||
{
|
||||
std::string::size_type start = 0;
|
||||
|
||||
while(start != std::string::npos)
|
||||
{
|
||||
std::string::size_type end = cookies.find_first_of("\r\n", start);
|
||||
if(end > start)
|
||||
{
|
||||
// The line is non-empty. Try to create a cookie from it.
|
||||
setOneCookie(cookies, start, end, mark_changed, host);
|
||||
}
|
||||
start = cookies.find_first_not_of("\r\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, const std::string &host)
|
||||
{
|
||||
Cookie *cookie = Cookie::createFromString(s, cookie_start, cookie_end, host);
|
||||
if(cookie)
|
||||
{
|
||||
LL_DEBUGS("CookieStoreUpdate") << "setting cookie: " << cookie->getCookie() << LL_ENDL;
|
||||
|
||||
// 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);
|
||||
LL_DEBUGS("CookieStoreUpdate") << " marking dead" << LL_ENDL;
|
||||
}
|
||||
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;
|
||||
|
||||
LL_DEBUGS("CookieStoreUpdate") << " removing" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
LL_DEBUGS("CookieStoreUpdate") << " unchanged" << LL_ENDL;
|
||||
}
|
||||
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;
|
||||
|
||||
LL_DEBUGS("CookieStoreUpdate") << " replacing" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
LL_DEBUGS("CookieStoreUpdate") << " adding" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("CookieStoreUpdate") << "failed to parse cookie: " << s.substr(cookie_start, cookie_end - cookie_start) << LL_ENDL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
127
indra/llplugin/llplugincookiestore.h
Normal file
127
indra/llplugin/llplugincookiestore.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* @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 <map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
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);
|
||||
|
||||
// sets one or more cookies (without reinitializing anything), supplying a hostname the cookies came from -- use when setting a cookie manually
|
||||
void setCookiesFromHost(const std::string &cookies, const std::string &host, 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, const std::string &host = LLStringUtil::null);
|
||||
|
||||
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, const std::string &host = LLStringUtil::null);
|
||||
|
||||
// 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(const std::string &host);
|
||||
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<std::string, Cookie*> cookie_map_t;
|
||||
|
||||
cookie_map_t mCookies;
|
||||
bool mHasChangedCookies;
|
||||
|
||||
void clearCookies();
|
||||
void removeCookie(const std::string &key);
|
||||
};
|
||||
|
||||
#endif // LL_LLPLUGINCOOKIESTORE_H
|
||||
9
indra/llplugin/llplugininstance.cpp
Normal file → Executable file
9
indra/llplugin/llplugininstance.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llplugininstance.cpp
|
||||
* @brief LLPluginInstance handles loading the dynamic library of a plugin and setting up its entry points for message passing.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
10
indra/llplugin/llplugininstance.h
Normal file → Executable file
10
indra/llplugin/llplugininstance.h
Normal file → Executable file
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @file llplugininstance.h
|
||||
* @brief LLPluginInstance handles loading the dynamic library of a plugin and setting up its entry points for message passing.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +12,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +28,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGININSTANCE_H
|
||||
|
||||
9
indra/llplugin/llpluginmessage.cpp
Normal file → Executable file
9
indra/llplugin/llpluginmessage.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginmessage.cpp
|
||||
* @brief LLPluginMessage encapsulates the serialization/deserialization of messages passed to and from plugins.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
10
indra/llplugin/llpluginmessage.h
Normal file → Executable file
10
indra/llplugin/llpluginmessage.h
Normal file → Executable file
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @file llpluginmessage.h
|
||||
* @brief LLPluginMessage encapsulates the serialization/deserialization of messages passed to and from plugins.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +12,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +28,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINMESSAGE_H
|
||||
|
||||
9
indra/llplugin/llpluginmessageclasses.h
Normal file → Executable file
9
indra/llplugin/llpluginmessageclasses.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginmessageclasses.h
|
||||
* @brief This file defines the versions of existing message classes for LLPluginMessage.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINMESSAGECLASSES_H
|
||||
|
||||
129
indra/llplugin/llpluginmessagepipe.cpp
Normal file → Executable file
129
indra/llplugin/llpluginmessagepipe.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginmessagepipe.cpp
|
||||
* @brief Classes that implement connections from the plugin system to pipes/pumps.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
@@ -95,11 +98,14 @@ void LLPluginMessagePipeOwner::killMessagePipe(void)
|
||||
}
|
||||
}
|
||||
|
||||
LLPluginMessagePipe::LLPluginMessagePipe(LLPluginMessagePipeOwner *owner, LLSocket::ptr_t socket)
|
||||
LLPluginMessagePipe::LLPluginMessagePipe(LLPluginMessagePipeOwner *owner, LLSocket::ptr_t socket):
|
||||
mInputMutex(gAPRPoolp),
|
||||
mOutputMutex(gAPRPoolp),
|
||||
mOwner(owner),
|
||||
mSocket(socket)
|
||||
{
|
||||
mOwner = owner;
|
||||
|
||||
mOwner->setMessagePipe(this);
|
||||
mSocket = socket;
|
||||
}
|
||||
|
||||
LLPluginMessagePipe::~LLPluginMessagePipe()
|
||||
@@ -113,6 +119,7 @@ LLPluginMessagePipe::~LLPluginMessagePipe()
|
||||
bool LLPluginMessagePipe::addMessage(const std::string &message)
|
||||
{
|
||||
// queue the message for later output
|
||||
LLMutexLock lock(&mOutputMutex);
|
||||
mOutput += message;
|
||||
mOutput += MESSAGE_DELIMITER; // message separator
|
||||
|
||||
@@ -147,6 +154,18 @@ void LLPluginMessagePipe::setSocketTimeout(apr_interval_time_t timeout_usec)
|
||||
}
|
||||
|
||||
bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
{
|
||||
bool result = pumpOutput();
|
||||
|
||||
if(result)
|
||||
{
|
||||
result = pumpInput(timeout);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LLPluginMessagePipe::pumpOutput()
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
@@ -155,6 +174,7 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
apr_status_t status;
|
||||
apr_size_t size;
|
||||
|
||||
LLMutexLock lock(&mOutputMutex);
|
||||
if(!mOutput.empty())
|
||||
{
|
||||
// write any outgoing messages
|
||||
@@ -182,6 +202,17 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
// remove the written part from the buffer and try again later.
|
||||
mOutput = mOutput.substr(size);
|
||||
}
|
||||
else if(APR_STATUS_IS_EOF(status))
|
||||
{
|
||||
// This is what we normally expect when a plugin exits.
|
||||
llinfos << "Got EOF from plugin socket. " << llendl;
|
||||
|
||||
if(mOwner)
|
||||
{
|
||||
mOwner->socketError(status);
|
||||
}
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// some other error
|
||||
@@ -195,6 +226,19 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LLPluginMessagePipe::pumpInput(F64 timeout)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if(mSocket)
|
||||
{
|
||||
apr_status_t status;
|
||||
apr_size_t size;
|
||||
|
||||
// FIXME: For some reason, the apr timeout stuff isn't working properly on windows.
|
||||
// Until such time as we figure out why, don't try to use the socket timeout -- just sleep here instead.
|
||||
@@ -215,8 +259,16 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
char input_buf[1024];
|
||||
apr_size_t request_size;
|
||||
|
||||
// Start out by reading one byte, so that any data received will wake us up.
|
||||
request_size = 1;
|
||||
if(timeout == 0.0f)
|
||||
{
|
||||
// If we have no timeout, start out with a full read.
|
||||
request_size = sizeof(input_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start out by reading one byte, so that any data received will wake us up.
|
||||
request_size = 1;
|
||||
}
|
||||
|
||||
// and use the timeout so we'll sleep if no data is available.
|
||||
setSocketTimeout((apr_interval_time_t)(timeout * 1000000));
|
||||
@@ -235,11 +287,14 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
// LL_INFOS("Plugin") << "after apr_socket_recv, size = " << size << LL_ENDL;
|
||||
|
||||
if(size > 0)
|
||||
{
|
||||
LLMutexLock lock(&mInputMutex);
|
||||
mInput.append(input_buf, size);
|
||||
}
|
||||
|
||||
if(status == APR_SUCCESS)
|
||||
{
|
||||
// llinfos << "success, read " << size << llendl;
|
||||
LL_DEBUGS("PluginSocket") << "success, read " << size << LL_ENDL;
|
||||
|
||||
if(size != request_size)
|
||||
{
|
||||
@@ -249,16 +304,28 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
}
|
||||
else if(APR_STATUS_IS_TIMEUP(status))
|
||||
{
|
||||
// llinfos << "TIMEUP, read " << size << llendl;
|
||||
LL_DEBUGS("PluginSocket") << "TIMEUP, read " << size << LL_ENDL;
|
||||
|
||||
// Timeout was hit. Since the initial read is 1 byte, this should never be a partial read.
|
||||
break;
|
||||
}
|
||||
else if(APR_STATUS_IS_EAGAIN(status))
|
||||
{
|
||||
// llinfos << "EAGAIN, read " << size << llendl;
|
||||
LL_DEBUGS("PluginSocket") << "EAGAIN, read " << size << LL_ENDL;
|
||||
|
||||
// We've been doing partial reads, and we're done now.
|
||||
// Non-blocking read returned immediately.
|
||||
break;
|
||||
}
|
||||
else if(APR_STATUS_IS_EOF(status))
|
||||
{
|
||||
// This is what we normally expect when a plugin exits.
|
||||
LL_INFOS("PluginSocket") << "Got EOF from plugin socket. " << LL_ENDL;
|
||||
|
||||
if(mOwner)
|
||||
{
|
||||
mOwner->socketError(status);
|
||||
}
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -275,22 +342,18 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
break;
|
||||
}
|
||||
|
||||
// Second and subsequent reads should not use the timeout
|
||||
setSocketTimeout(0);
|
||||
// and should try to fill the input buffer
|
||||
request_size = sizeof(input_buf);
|
||||
if(timeout != 0.0f)
|
||||
{
|
||||
// Second and subsequent reads should not use the timeout
|
||||
setSocketTimeout(0);
|
||||
// and should try to fill the input buffer
|
||||
request_size = sizeof(input_buf);
|
||||
}
|
||||
}
|
||||
|
||||
processInput();
|
||||
}
|
||||
}
|
||||
|
||||
if(!result)
|
||||
{
|
||||
// If we got an error, we're done.
|
||||
LL_INFOS("Plugin") << "Error from socket, cleaning up." << LL_ENDL;
|
||||
delete this;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -298,25 +361,27 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
void LLPluginMessagePipe::processInput(void)
|
||||
{
|
||||
// Look for input delimiter(s) in the input buffer.
|
||||
int start = 0;
|
||||
int delim;
|
||||
while((delim = mInput.find(MESSAGE_DELIMITER, start)) != std::string::npos)
|
||||
mInputMutex.lock();
|
||||
while((delim = mInput.find(MESSAGE_DELIMITER)) != std::string::npos)
|
||||
{
|
||||
// Let the owner process this message
|
||||
if (mOwner)
|
||||
{
|
||||
mOwner->receiveMessageRaw(mInput.substr(start, delim - start));
|
||||
// Pull the message out of the input buffer before calling receiveMessageRaw.
|
||||
// It's now possible for this function to get called recursively (in the case where the plugin makes a blocking request)
|
||||
// and this guarantees that the messages will get dequeued correctly.
|
||||
std::string message(mInput, 0, delim);
|
||||
mInput.erase(0, delim + 1);
|
||||
mInputMutex.unlock();
|
||||
mOwner->receiveMessageRaw(message);
|
||||
mInputMutex.lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Plugin") << "!mOwner" << LL_ENDL;
|
||||
}
|
||||
start = delim + 1;
|
||||
}
|
||||
|
||||
// Remove delivered messages from the input buffer.
|
||||
if(start != 0)
|
||||
mInput = mInput.substr(start);
|
||||
|
||||
mInputMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
18
indra/llplugin/llpluginmessagepipe.h
Normal file → Executable file
18
indra/llplugin/llpluginmessagepipe.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginmessagepipe.h
|
||||
* @brief Classes that implement connections from the plugin system to pipes/pumps.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,12 +29,15 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINMESSAGEPIPE_H
|
||||
#define LL_LLPLUGINMESSAGEPIPE_H
|
||||
|
||||
#include "lliosocket.h"
|
||||
#include "llthread.h"
|
||||
|
||||
class LLPluginMessagePipe;
|
||||
|
||||
@@ -50,7 +54,7 @@ public:
|
||||
virtual apr_status_t socketError(apr_status_t error);
|
||||
|
||||
// called from LLPluginMessagePipe to manage the connection with LLPluginMessagePipeOwner -- do not use!
|
||||
virtual void setMessagePipe(LLPluginMessagePipe *message_pipe) ;
|
||||
virtual void setMessagePipe(LLPluginMessagePipe *message_pipe);
|
||||
|
||||
protected:
|
||||
// returns false if writeMessageRaw() would drop the message
|
||||
@@ -75,14 +79,18 @@ public:
|
||||
void clearOwner(void);
|
||||
|
||||
bool pump(F64 timeout = 0.0f);
|
||||
|
||||
bool pumpOutput();
|
||||
bool pumpInput(F64 timeout = 0.0f);
|
||||
|
||||
protected:
|
||||
void processInput(void);
|
||||
|
||||
// used internally by pump()
|
||||
void setSocketTimeout(apr_interval_time_t timeout_usec);
|
||||
|
||||
LLMutex mInputMutex;
|
||||
std::string mInput;
|
||||
LLMutex mOutputMutex;
|
||||
std::string mOutput;
|
||||
|
||||
LLPluginMessagePipeOwner *mOwner;
|
||||
|
||||
107
indra/llplugin/llpluginprocesschild.cpp
Normal file → Executable file
107
indra/llplugin/llpluginprocesschild.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginprocesschild.cpp
|
||||
* @brief LLPluginProcessChild handles the child side of the external-process plugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
@@ -42,10 +45,13 @@ static const F32 PLUGIN_IDLE_SECONDS = 1.0f / 100.0f; // Each call to idle will
|
||||
|
||||
LLPluginProcessChild::LLPluginProcessChild()
|
||||
{
|
||||
mState = STATE_UNINITIALIZED;
|
||||
mInstance = NULL;
|
||||
mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
|
||||
mSleepTime = PLUGIN_IDLE_SECONDS; // default: send idle messages at 100Hz
|
||||
mCPUElapsed = 0.0f;
|
||||
mBlockingRequest = false;
|
||||
mBlockingResponseReceived = false;
|
||||
}
|
||||
|
||||
LLPluginProcessChild::~LLPluginProcessChild()
|
||||
@@ -58,7 +64,7 @@ LLPluginProcessChild::~LLPluginProcessChild()
|
||||
// appears to fail and lock up which means that a given instance of the slplugin process never exits.
|
||||
// This is bad, especially when users try to update their version of SL - it fails because the slplugin
|
||||
// process as well as a bunch of plugin specific files are locked and cannot be overwritten.
|
||||
exit(0);
|
||||
exit( 0 );
|
||||
//delete mInstance;
|
||||
//mInstance = NULL;
|
||||
}
|
||||
@@ -81,9 +87,14 @@ void LLPluginProcessChild::idle(void)
|
||||
bool idle_again;
|
||||
do
|
||||
{
|
||||
if(mSocketError != APR_SUCCESS)
|
||||
if(APR_STATUS_IS_EOF(mSocketError))
|
||||
{
|
||||
LL_INFOS("Plugin") << "message pipe is in error state, moving to STATE_ERROR"<< LL_ENDL;
|
||||
// Plugin socket was closed. This covers both normal plugin termination and host crashes.
|
||||
setState(STATE_ERROR);
|
||||
}
|
||||
else if(mSocketError != APR_SUCCESS)
|
||||
{
|
||||
LL_INFOS("Plugin") << "message pipe is in error state (" << mSocketError << "), moving to STATE_ERROR"<< LL_ENDL;
|
||||
setState(STATE_ERROR);
|
||||
}
|
||||
|
||||
@@ -153,7 +164,6 @@ void LLPluginProcessChild::idle(void)
|
||||
{
|
||||
setState(STATE_PLUGIN_INITIALIZING);
|
||||
LLPluginMessage message("base", "init");
|
||||
message.setValue("user_data_path", mUserDataPath);
|
||||
sendMessageToPlugin(message);
|
||||
}
|
||||
break;
|
||||
@@ -225,6 +235,7 @@ void LLPluginProcessChild::idle(void)
|
||||
|
||||
void LLPluginProcessChild::sleep(F64 seconds)
|
||||
{
|
||||
deliverQueuedMessages();
|
||||
if(mMessagePipe)
|
||||
{
|
||||
mMessagePipe->pump(seconds);
|
||||
@@ -237,6 +248,7 @@ void LLPluginProcessChild::sleep(F64 seconds)
|
||||
|
||||
void LLPluginProcessChild::pump(void)
|
||||
{
|
||||
deliverQueuedMessages();
|
||||
if(mMessagePipe)
|
||||
{
|
||||
mMessagePipe->pump(0.0f);
|
||||
@@ -278,14 +290,14 @@ void LLPluginProcessChild::sendMessageToPlugin(const LLPluginMessage &message)
|
||||
{
|
||||
if (mInstance)
|
||||
{
|
||||
std::string buffer = message.generate();
|
||||
|
||||
LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL;
|
||||
LLTimer elapsed;
|
||||
|
||||
mInstance->sendMessage(buffer);
|
||||
|
||||
mCPUElapsed += elapsed.getElapsedTimeF64();
|
||||
std::string buffer = message.generate();
|
||||
|
||||
LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL;
|
||||
LLTimer elapsed;
|
||||
|
||||
mInstance->sendMessage(buffer);
|
||||
|
||||
mCPUElapsed += elapsed.getElapsedTimeF64();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -308,15 +320,32 @@ void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
|
||||
|
||||
LL_DEBUGS("Plugin") << "Received from parent: " << message << LL_ENDL;
|
||||
|
||||
// Decode this message
|
||||
LLPluginMessage parsed;
|
||||
parsed.parse(message);
|
||||
|
||||
if(mBlockingRequest)
|
||||
{
|
||||
// We're blocking the plugin waiting for a response.
|
||||
|
||||
if(parsed.hasValue("blocking_response"))
|
||||
{
|
||||
// This is the message we've been waiting for -- fall through and send it immediately.
|
||||
mBlockingResponseReceived = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Still waiting. Queue this message and don't process it yet.
|
||||
mMessageQueue.push(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool passMessage = true;
|
||||
|
||||
// FIXME: how should we handle queueing here?
|
||||
|
||||
{
|
||||
// Decode this message
|
||||
LLPluginMessage parsed;
|
||||
parsed.parse(message);
|
||||
|
||||
std::string message_class = parsed.getClass();
|
||||
if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
|
||||
{
|
||||
@@ -326,7 +355,6 @@ void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
|
||||
if(message_name == "load_plugin")
|
||||
{
|
||||
mPluginFile = parsed.getValue("file");
|
||||
mUserDataPath = parsed.getValue("user_data_path");
|
||||
}
|
||||
else if(message_name == "shm_add")
|
||||
{
|
||||
@@ -425,7 +453,13 @@ void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
|
||||
void LLPluginProcessChild::receivePluginMessage(const std::string &message)
|
||||
{
|
||||
LL_DEBUGS("Plugin") << "Received from plugin: " << message << LL_ENDL;
|
||||
|
||||
|
||||
if(mBlockingRequest)
|
||||
{
|
||||
//
|
||||
LL_ERRS("Plugin") << "Can't send a message while already waiting on a blocking request -- aborting!" << LL_ENDL;
|
||||
}
|
||||
|
||||
// Incoming message from the plugin instance
|
||||
bool passMessage = true;
|
||||
|
||||
@@ -436,6 +470,12 @@ void LLPluginProcessChild::receivePluginMessage(const std::string &message)
|
||||
// Decode this message
|
||||
LLPluginMessage parsed;
|
||||
parsed.parse(message);
|
||||
|
||||
if(parsed.hasValue("blocking_request"))
|
||||
{
|
||||
mBlockingRequest = true;
|
||||
}
|
||||
|
||||
std::string message_class = parsed.getClass();
|
||||
if(message_class == "base")
|
||||
{
|
||||
@@ -494,6 +534,19 @@ void LLPluginProcessChild::receivePluginMessage(const std::string &message)
|
||||
LL_DEBUGS("Plugin") << "Passing through to parent: " << message << LL_ENDL;
|
||||
writeMessageRaw(message);
|
||||
}
|
||||
|
||||
while(mBlockingRequest)
|
||||
{
|
||||
// The plugin wants to block and wait for a response to this message.
|
||||
sleep(mSleepTime); // this will pump the message pipe and process messages
|
||||
|
||||
if(mBlockingResponseReceived || mSocketError != APR_SUCCESS || (mMessagePipe == NULL))
|
||||
{
|
||||
// Response has been received, or we've hit an error state. Stop waiting.
|
||||
mBlockingRequest = false;
|
||||
mBlockingResponseReceived = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -502,3 +555,15 @@ void LLPluginProcessChild::setState(EState state)
|
||||
LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL;
|
||||
mState = state;
|
||||
};
|
||||
|
||||
void LLPluginProcessChild::deliverQueuedMessages()
|
||||
{
|
||||
if(!mBlockingRequest)
|
||||
{
|
||||
while(!mMessageQueue.empty())
|
||||
{
|
||||
receiveMessageRaw(mMessageQueue.front());
|
||||
mMessageQueue.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
indra/llplugin/llpluginprocesschild.h
Normal file → Executable file
21
indra/llplugin/llpluginprocesschild.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginprocesschild.h
|
||||
* @brief LLPluginProcessChild handles the child side of the external-process plugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,11 +29,15 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINPROCESSCHILD_H
|
||||
#define LL_LLPLUGINPROCESSCHILD_H
|
||||
|
||||
#include <queue> //imprudence
|
||||
|
||||
#include "llpluginmessage.h"
|
||||
#include "llpluginmessagepipe.h"
|
||||
#include "llplugininstance.h"
|
||||
@@ -88,16 +93,15 @@ private:
|
||||
STATE_ERROR, // generic bailout state
|
||||
STATE_DONE // state machine will sit in this state after either error or normal termination.
|
||||
};
|
||||
EState mState;
|
||||
void setState(EState state);
|
||||
|
||||
EState mState;
|
||||
|
||||
LLHost mLauncherHost;
|
||||
LLSocket::ptr_t mSocket;
|
||||
|
||||
std::string mPluginFile;
|
||||
|
||||
std::string mUserDataPath;
|
||||
|
||||
LLPluginInstance *mInstance;
|
||||
|
||||
typedef std::map<std::string, LLPluginSharedMemory*> sharedMemoryRegionsType;
|
||||
@@ -106,6 +110,11 @@ private:
|
||||
LLTimer mHeartbeat;
|
||||
F64 mSleepTime;
|
||||
F64 mCPUElapsed;
|
||||
bool mBlockingRequest;
|
||||
bool mBlockingResponseReceived;
|
||||
std::queue<std::string> mMessageQueue;
|
||||
|
||||
void deliverQueuedMessages();
|
||||
|
||||
};
|
||||
|
||||
|
||||
536
indra/llplugin/llpluginprocessparent.cpp
Normal file → Executable file
536
indra/llplugin/llpluginprocessparent.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginprocessparent.cpp
|
||||
* @brief LLPluginProcessParent handles the parent side of the external-process plugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
@@ -44,25 +47,90 @@ LLPluginProcessParentOwner::~LLPluginProcessParentOwner()
|
||||
|
||||
}
|
||||
|
||||
LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner)
|
||||
bool LLPluginProcessParent::sUseReadThread = false;
|
||||
apr_pollset_t *LLPluginProcessParent::sPollSet = NULL;
|
||||
bool LLPluginProcessParent::sPollsetNeedsRebuild = false;
|
||||
LLMutex *LLPluginProcessParent::sInstancesMutex;
|
||||
std::list<LLPluginProcessParent*> LLPluginProcessParent::sInstances;
|
||||
LLThread *LLPluginProcessParent::sReadThread = NULL;
|
||||
|
||||
|
||||
class LLPluginProcessParentPollThread: public LLThread
|
||||
{
|
||||
public:
|
||||
LLPluginProcessParentPollThread() :
|
||||
LLThread("LLPluginProcessParentPollThread", gAPRPoolp)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
// Inherited from LLThread
|
||||
/*virtual*/ void run(void)
|
||||
{
|
||||
while(!isQuitting() && LLPluginProcessParent::getUseReadThread())
|
||||
{
|
||||
LLPluginProcessParent::poll(0.1f);
|
||||
checkPause();
|
||||
}
|
||||
|
||||
// Final poll to clean up the pollset, etc.
|
||||
LLPluginProcessParent::poll(0.0f);
|
||||
}
|
||||
|
||||
// Inherited from LLThread
|
||||
/*virtual*/ bool runCondition(void)
|
||||
{
|
||||
return(LLPluginProcessParent::canPollThreadRun());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner):
|
||||
mIncomingQueueMutex(gAPRPoolp)
|
||||
{
|
||||
if(!sInstancesMutex)
|
||||
{
|
||||
sInstancesMutex = new LLMutex(gAPRPoolp);
|
||||
}
|
||||
|
||||
mOwner = owner;
|
||||
mBoundPort = 0;
|
||||
mState = STATE_UNINITIALIZED;
|
||||
mSleepTime = 0.0;
|
||||
mCPUUsage = 0.0;
|
||||
mDisableTimeout = false;
|
||||
mDebug = false;
|
||||
mBlocked = false;
|
||||
mPolledInput = false;
|
||||
mPollFD.client_data = NULL;
|
||||
|
||||
mPluginLaunchTimeout = 60.0f;
|
||||
mPluginLockupTimeout = 15.0f;
|
||||
|
||||
// Don't start the timer here -- start it when we actually launch the plugin process.
|
||||
mHeartbeat.stop();
|
||||
|
||||
|
||||
// Don't add to the global list until fully constructed.
|
||||
{
|
||||
LLMutexLock lock(sInstancesMutex);
|
||||
sInstances.push_back(this);
|
||||
}
|
||||
}
|
||||
|
||||
LLPluginProcessParent::~LLPluginProcessParent()
|
||||
{
|
||||
LL_DEBUGS("Plugin") << "destructor" << LL_ENDL;
|
||||
|
||||
// Remove from the global list before beginning destruction.
|
||||
{
|
||||
// Make sure to get the global mutex _first_ here, to avoid a possible deadlock against LLPluginProcessParent::poll()
|
||||
LLMutexLock lock(sInstancesMutex);
|
||||
{
|
||||
LLMutexLock lock2(&mIncomingQueueMutex);
|
||||
sInstances.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy any remaining shared memory regions
|
||||
sharedMemoryRegionsType::iterator iter;
|
||||
while((iter = mSharedMemoryRegions.begin()) != mSharedMemoryRegions.end())
|
||||
@@ -74,15 +142,17 @@ LLPluginProcessParent::~LLPluginProcessParent()
|
||||
mSharedMemoryRegions.erase(iter);
|
||||
}
|
||||
|
||||
// orphaning the process means it won't be killed when the LLProcessLauncher is destructed.
|
||||
// This is what we want -- it should exit cleanly once it notices the sockets have been closed.
|
||||
mProcess.orphan();
|
||||
mProcess.kill();
|
||||
killSockets();
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::killSockets(void)
|
||||
{
|
||||
killMessagePipe();
|
||||
{
|
||||
LLMutexLock lock(&mIncomingQueueMutex);
|
||||
killMessagePipe();
|
||||
}
|
||||
|
||||
mListenSocket.reset();
|
||||
mSocket.reset();
|
||||
}
|
||||
@@ -95,14 +165,12 @@ void LLPluginProcessParent::errorState(void)
|
||||
setState(STATE_ERROR);
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug, const std::string &user_data_path)
|
||||
void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug)
|
||||
{
|
||||
mProcess.setExecutable(launcher_filename);
|
||||
mPluginFile = plugin_filename;
|
||||
mCPUUsage = 0.0f;
|
||||
mDebug = debug;
|
||||
mUserDataPath = user_data_path;
|
||||
|
||||
mDebug = debug;
|
||||
setState(STATE_INITIALIZED);
|
||||
}
|
||||
|
||||
@@ -158,21 +226,47 @@ void LLPluginProcessParent::idle(void)
|
||||
|
||||
do
|
||||
{
|
||||
// process queued messages
|
||||
mIncomingQueueMutex.lock();
|
||||
while(!mIncomingQueue.empty())
|
||||
{
|
||||
LLPluginMessage message = mIncomingQueue.front();
|
||||
mIncomingQueue.pop();
|
||||
mIncomingQueueMutex.unlock();
|
||||
|
||||
receiveMessage(message);
|
||||
|
||||
mIncomingQueueMutex.lock();
|
||||
}
|
||||
|
||||
mIncomingQueueMutex.unlock();
|
||||
|
||||
// Give time to network processing
|
||||
if(mMessagePipe)
|
||||
{
|
||||
if(!mMessagePipe->pump())
|
||||
// Drain any queued outgoing messages
|
||||
mMessagePipe->pumpOutput();
|
||||
|
||||
// Only do input processing here if this instance isn't in a pollset.
|
||||
if(!mPolledInput)
|
||||
{
|
||||
// LL_WARNS("Plugin") << "Message pipe hit an error state" << LL_ENDL;
|
||||
errorState();
|
||||
mMessagePipe->pumpInput();
|
||||
}
|
||||
}
|
||||
|
||||
if((mSocketError != APR_SUCCESS) && (mState <= STATE_RUNNING))
|
||||
|
||||
if(mState <= STATE_RUNNING)
|
||||
{
|
||||
// The socket is in an error state -- the plugin is gone.
|
||||
LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL;
|
||||
errorState();
|
||||
if(APR_STATUS_IS_EOF(mSocketError))
|
||||
{
|
||||
// Plugin socket was closed. This covers both normal plugin termination and plugin crashes.
|
||||
errorState();
|
||||
}
|
||||
else if(mSocketError != APR_SUCCESS)
|
||||
{
|
||||
// The socket is in an error state -- the plugin is gone.
|
||||
LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL;
|
||||
errorState();
|
||||
}
|
||||
}
|
||||
|
||||
// If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState().
|
||||
@@ -314,6 +408,17 @@ void LLPluginProcessParent::idle(void)
|
||||
mDebugger.addArgument("end tell");
|
||||
mDebugger.launch();
|
||||
|
||||
#elif LL_LINUX
|
||||
|
||||
std::stringstream cmd;
|
||||
|
||||
mDebugger.setExecutable("/usr/bin/gnome-terminal");
|
||||
mDebugger.addArgument("--geometry=165x24-0+0");
|
||||
mDebugger.addArgument("-e");
|
||||
cmd << "/usr/bin/gdb -n /proc/" << mProcess.getProcessID() << "/exe " << mProcess.getProcessID();
|
||||
mDebugger.addArgument(cmd.str());
|
||||
mDebugger.launch();
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -353,13 +458,12 @@ void LLPluginProcessParent::idle(void)
|
||||
break;
|
||||
|
||||
case STATE_HELLO:
|
||||
LL_DEBUGS("Plugin") << "received hello message" << llendl;
|
||||
LL_DEBUGS("Plugin") << "received hello message" << LL_ENDL;
|
||||
|
||||
// Send the message to load the plugin
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin");
|
||||
message.setValue("file", mPluginFile);
|
||||
message.setValue("user_data_path", mUserDataPath);
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
@@ -388,7 +492,7 @@ void LLPluginProcessParent::idle(void)
|
||||
}
|
||||
else if(pluginLockedUp())
|
||||
{
|
||||
LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << llendl;
|
||||
LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << LL_ENDL;
|
||||
errorState();
|
||||
}
|
||||
break;
|
||||
@@ -410,8 +514,7 @@ void LLPluginProcessParent::idle(void)
|
||||
break;
|
||||
|
||||
case STATE_CLEANUP:
|
||||
// Don't do a kill here anymore -- closing the sockets is the new 'kill'.
|
||||
mProcess.orphan();
|
||||
mProcess.kill();
|
||||
killSockets();
|
||||
setState(STATE_DONE);
|
||||
break;
|
||||
@@ -479,23 +582,323 @@ void LLPluginProcessParent::setSleepTime(F64 sleep_time, bool force_send)
|
||||
|
||||
void LLPluginProcessParent::sendMessage(const LLPluginMessage &message)
|
||||
{
|
||||
if(message.hasValue("blocking_response"))
|
||||
{
|
||||
mBlocked = false;
|
||||
|
||||
// reset the heartbeat timer, since there will have been no heartbeats while the plugin was blocked.
|
||||
mHeartbeat.setTimerExpirySec(mPluginLockupTimeout);
|
||||
}
|
||||
|
||||
std::string buffer = message.generate();
|
||||
LL_DEBUGS("Plugin") << "Sending: " << buffer << LL_ENDL;
|
||||
writeMessageRaw(buffer);
|
||||
|
||||
// Try to send message immediately.
|
||||
if(mMessagePipe)
|
||||
{
|
||||
mMessagePipe->pumpOutput();
|
||||
}
|
||||
}
|
||||
|
||||
//virtual
|
||||
void LLPluginProcessParent::setMessagePipe(LLPluginMessagePipe *message_pipe)
|
||||
{
|
||||
bool update_pollset = false;
|
||||
|
||||
if(mMessagePipe)
|
||||
{
|
||||
// Unsetting an existing message pipe -- remove from the pollset
|
||||
mPollFD.client_data = NULL;
|
||||
|
||||
// pollset needs an update
|
||||
update_pollset = true;
|
||||
}
|
||||
if(message_pipe != NULL)
|
||||
{
|
||||
// Set up the apr_pollfd_t
|
||||
mPollFD.p = gAPRPoolp;
|
||||
mPollFD.desc_type = APR_POLL_SOCKET;
|
||||
mPollFD.reqevents = APR_POLLIN|APR_POLLERR|APR_POLLHUP;
|
||||
mPollFD.rtnevents = 0;
|
||||
mPollFD.desc.s = mSocket->getSocket();
|
||||
mPollFD.client_data = (void*)this;
|
||||
|
||||
// pollset needs an update
|
||||
update_pollset = true;
|
||||
}
|
||||
|
||||
mMessagePipe = message_pipe;
|
||||
|
||||
if(update_pollset)
|
||||
{
|
||||
dirtyPollSet();
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLPluginProcessParent::dirtyPollSet()
|
||||
{
|
||||
sPollsetNeedsRebuild = true;
|
||||
|
||||
if(sReadThread)
|
||||
{
|
||||
LL_DEBUGS("PluginPoll") << "unpausing read thread " << LL_ENDL;
|
||||
sReadThread->unpause();
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::updatePollset()
|
||||
{
|
||||
if(!sInstancesMutex)
|
||||
{
|
||||
// No instances have been created yet. There's no work to do.
|
||||
return;
|
||||
}
|
||||
|
||||
LLMutexLock lock(sInstancesMutex);
|
||||
|
||||
if(sPollSet)
|
||||
{
|
||||
LL_DEBUGS("PluginPoll") << "destroying pollset " << sPollSet << LL_ENDL;
|
||||
// delete the existing pollset.
|
||||
apr_pollset_destroy(sPollSet);
|
||||
sPollSet = NULL;
|
||||
}
|
||||
|
||||
std::list<LLPluginProcessParent*>::iterator iter;
|
||||
int count = 0;
|
||||
|
||||
// Count the number of instances that want to be in the pollset
|
||||
for(iter = sInstances.begin(); iter != sInstances.end(); iter++)
|
||||
{
|
||||
(*iter)->mPolledInput = false;
|
||||
if((*iter)->mPollFD.client_data)
|
||||
{
|
||||
// This instance has a socket that needs to be polled.
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
if(sUseReadThread && sReadThread && !sReadThread->isQuitting())
|
||||
{
|
||||
if(!sPollSet && (count > 0))
|
||||
{
|
||||
#ifdef APR_POLLSET_NOCOPY
|
||||
// The pollset doesn't exist yet. Create it now.
|
||||
apr_status_t status = apr_pollset_create(&sPollSet, count, gAPRPoolp, APR_POLLSET_NOCOPY);
|
||||
if(status != APR_SUCCESS)
|
||||
{
|
||||
#endif // APR_POLLSET_NOCOPY
|
||||
LL_WARNS("PluginPoll") << "Couldn't create pollset. Falling back to non-pollset mode." << LL_ENDL;
|
||||
sPollSet = NULL;
|
||||
#ifdef APR_POLLSET_NOCOPY
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS("PluginPoll") << "created pollset " << sPollSet << LL_ENDL;
|
||||
|
||||
// Pollset was created, add all instances to it.
|
||||
for(iter = sInstances.begin(); iter != sInstances.end(); iter++)
|
||||
{
|
||||
if((*iter)->mPollFD.client_data)
|
||||
{
|
||||
status = apr_pollset_add(sPollSet, &((*iter)->mPollFD));
|
||||
if(status == APR_SUCCESS)
|
||||
{
|
||||
(*iter)->mPolledInput = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("PluginPoll") << "apr_pollset_add failed with status " << status << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // APR_POLLSET_NOCOPY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::setUseReadThread(bool use_read_thread)
|
||||
{
|
||||
if(sUseReadThread != use_read_thread)
|
||||
{
|
||||
sUseReadThread = use_read_thread;
|
||||
|
||||
if(sUseReadThread)
|
||||
{
|
||||
if(!sReadThread)
|
||||
{
|
||||
// start up the read thread
|
||||
LL_INFOS("PluginPoll") << "creating read thread " << LL_ENDL;
|
||||
|
||||
// make sure the pollset gets rebuilt.
|
||||
sPollsetNeedsRebuild = true;
|
||||
|
||||
sReadThread = new LLPluginProcessParentPollThread;
|
||||
sReadThread->start();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(sReadThread)
|
||||
{
|
||||
// shut down the read thread
|
||||
LL_INFOS("PluginPoll") << "destroying read thread " << LL_ENDL;
|
||||
delete sReadThread;
|
||||
sReadThread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::poll(F64 timeout)
|
||||
{
|
||||
if(sPollsetNeedsRebuild || !sUseReadThread)
|
||||
{
|
||||
sPollsetNeedsRebuild = false;
|
||||
updatePollset();
|
||||
}
|
||||
|
||||
if(sPollSet)
|
||||
{
|
||||
apr_status_t status;
|
||||
apr_int32_t count;
|
||||
const apr_pollfd_t *descriptors;
|
||||
status = apr_pollset_poll(sPollSet, (apr_interval_time_t)(timeout * 1000000), &count, &descriptors);
|
||||
if(status == APR_SUCCESS)
|
||||
{
|
||||
// One or more of the descriptors signalled. Call them.
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
LLPluginProcessParent *self = (LLPluginProcessParent *)(descriptors[i].client_data);
|
||||
// NOTE: the descriptor returned here is actually a COPY of the original (even though we create the pollset with APR_POLLSET_NOCOPY).
|
||||
// This means that even if the parent has set its mPollFD.client_data to NULL, the old pointer may still there in this descriptor.
|
||||
// It's even possible that the old pointer no longer points to a valid LLPluginProcessParent.
|
||||
// This means that we can't safely dereference the 'self' pointer here without some extra steps...
|
||||
if(self)
|
||||
{
|
||||
// Make sure this pointer is still in the instances list
|
||||
bool valid = false;
|
||||
{
|
||||
LLMutexLock lock(sInstancesMutex);
|
||||
for(std::list<LLPluginProcessParent*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
|
||||
{
|
||||
if(*iter == self)
|
||||
{
|
||||
// Lock the instance's mutex before unlocking the global mutex.
|
||||
// This avoids a possible race condition where the instance gets deleted between this check and the servicePoll() call.
|
||||
self->mIncomingQueueMutex.lock();
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(valid)
|
||||
{
|
||||
// The instance is still valid.
|
||||
// Pull incoming messages off the socket
|
||||
self->servicePoll();
|
||||
self->mIncomingQueueMutex.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS("PluginPoll") << "detected deleted instance " << self << LL_ENDL;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(APR_STATUS_IS_TIMEUP(status))
|
||||
{
|
||||
// timed out with no incoming data. Just return.
|
||||
}
|
||||
else if(status == EBADF)
|
||||
{
|
||||
// This happens when one of the file descriptors in the pollset is destroyed, which happens whenever a plugin's socket is closed.
|
||||
// The pollset has been or will be recreated, so just return.
|
||||
LL_DEBUGS("PluginPoll") << "apr_pollset_poll returned EBADF" << LL_ENDL;
|
||||
}
|
||||
else if(status != APR_SUCCESS)
|
||||
{
|
||||
LL_WARNS("PluginPoll") << "apr_pollset_poll failed with status " << status << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::servicePoll()
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
// poll signalled on this object's socket. Try to process incoming messages.
|
||||
if(mMessagePipe)
|
||||
{
|
||||
result = mMessagePipe->pumpInput(0.0f);
|
||||
}
|
||||
|
||||
if(!result)
|
||||
{
|
||||
// If we got a read error on input, remove this pipe from the pollset
|
||||
apr_pollset_remove(sPollSet, &mPollFD);
|
||||
|
||||
// and tell the code not to re-add it
|
||||
mPollFD.client_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::receiveMessageRaw(const std::string &message)
|
||||
{
|
||||
LL_DEBUGS("Plugin") << "Received: " << message << LL_ENDL;
|
||||
|
||||
// FIXME: should this go into a queue instead?
|
||||
|
||||
LLPluginMessage parsed;
|
||||
if(parsed.parse(message) != -1)
|
||||
{
|
||||
receiveMessage(parsed);
|
||||
if(parsed.hasValue("blocking_request"))
|
||||
{
|
||||
mBlocked = true;
|
||||
}
|
||||
|
||||
if(mPolledInput)
|
||||
{
|
||||
// This is being called on the polling thread -- only do minimal processing/queueing.
|
||||
receiveMessageEarly(parsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is not being called on the polling thread -- do full message processing at this time.
|
||||
receiveMessage(parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::receiveMessageEarly(const LLPluginMessage &message)
|
||||
{
|
||||
// NOTE: this function will be called from the polling thread. It will be called with mIncomingQueueMutex _already locked_.
|
||||
|
||||
bool handled = false;
|
||||
|
||||
std::string message_class = message.getClass();
|
||||
if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
|
||||
{
|
||||
// no internal messages need to be handled early.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Call out to the owner and see if they to reply
|
||||
// TODO: Should this only happen when blocked?
|
||||
if(mOwner != NULL)
|
||||
{
|
||||
handled = mOwner->receivePluginMessageEarly(message);
|
||||
}
|
||||
}
|
||||
|
||||
if(!handled)
|
||||
{
|
||||
// any message that wasn't handled early needs to be queued.
|
||||
mIncomingQueue.push(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,7 +1084,7 @@ std::string LLPluginProcessParent::getPluginVersion(void)
|
||||
|
||||
void LLPluginProcessParent::setState(EState state)
|
||||
{
|
||||
LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL;
|
||||
LL_DEBUGS("Plugin") << "setting state to " << stateToString(state) << LL_ENDL;
|
||||
mState = state;
|
||||
};
|
||||
|
||||
@@ -689,18 +1092,15 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit()
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if(!mDisableTimeout && !mDebug)
|
||||
if(!mProcess.isRunning())
|
||||
{
|
||||
if(!mProcess.isRunning())
|
||||
{
|
||||
LL_WARNS("Plugin") << "child exited" << llendl;
|
||||
result = true;
|
||||
}
|
||||
else if(pluginLockedUp())
|
||||
{
|
||||
LL_WARNS("Plugin") << "timeout" << llendl;
|
||||
result = true;
|
||||
}
|
||||
LL_WARNS("Plugin") << "child exited" << LL_ENDL;
|
||||
result = true;
|
||||
}
|
||||
else if(pluginLockedUp())
|
||||
{
|
||||
LL_WARNS("Plugin") << "timeout" << LL_ENDL;
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -708,7 +1108,63 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit()
|
||||
|
||||
bool LLPluginProcessParent::pluginLockedUp()
|
||||
{
|
||||
if(mDisableTimeout || mDebug || mBlocked)
|
||||
{
|
||||
// Never time out a plugin process in these cases.
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the timer is running and has expired, the plugin has locked up.
|
||||
return (mHeartbeat.getStarted() && mHeartbeat.hasExpired());
|
||||
}
|
||||
|
||||
std::string LLPluginProcessParent::stateToString(EState state)
|
||||
{
|
||||
std::string eng = "unknown plugin state";
|
||||
switch (state)
|
||||
{
|
||||
case STATE_UNINITIALIZED:
|
||||
eng = "STATE_UNINITIALIZED";
|
||||
break;
|
||||
case STATE_INITIALIZED:
|
||||
eng = "STATE_INITIALIZED - init() has been called";
|
||||
break;
|
||||
case STATE_LISTENING:
|
||||
eng = "STATE_LISTENING - listening for incoming connection";
|
||||
break;
|
||||
case STATE_LAUNCHED:
|
||||
eng = "STATE_LAUNCHED - process has been launched";
|
||||
break;
|
||||
case STATE_CONNECTED:
|
||||
eng = "STATE_CONNECTED - process has connected";
|
||||
break;
|
||||
case STATE_HELLO:
|
||||
eng = "STATE_HELLO - first message from the plugin process has been received";
|
||||
break;
|
||||
case STATE_LOADING:
|
||||
eng = "STATE_LOADING - process has been asked to load the plugin";
|
||||
break;
|
||||
case STATE_RUNNING:
|
||||
eng = "STATE_RUNNING - plugin running";
|
||||
break;
|
||||
case STATE_LAUNCH_FAILURE:
|
||||
eng = "STATE_LAUNCH_FAILURE - failure before plugin loaded";
|
||||
break;
|
||||
case STATE_ERROR:
|
||||
eng = "STATE_ERROR - generic bailout state";
|
||||
break;
|
||||
case STATE_CLEANUP:
|
||||
eng = "STATE_CLEANUP - clean everything up";
|
||||
break;
|
||||
case STATE_EXITING:
|
||||
eng = "STATE_EXITING - tried to kill process, waiting for it to exit";
|
||||
break;
|
||||
case STATE_DONE:
|
||||
eng = "STATE_DONE - plugin done";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return llformat("(%d) ", (S32)state) + eng;
|
||||
}
|
||||
|
||||
49
indra/llplugin/llpluginprocessparent.h
Normal file → Executable file
49
indra/llplugin/llpluginprocessparent.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginprocessparent.h
|
||||
* @brief LLPluginProcessParent handles the parent side of the external-process plugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,11 +29,15 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINPROCESSPARENT_H
|
||||
#define LL_LLPLUGINPROCESSPARENT_H
|
||||
|
||||
#include <queue> //imprudence
|
||||
|
||||
#include "llapr.h"
|
||||
#include "llprocesslauncher.h"
|
||||
#include "llpluginmessage.h"
|
||||
@@ -40,12 +45,14 @@
|
||||
#include "llpluginsharedmemory.h"
|
||||
|
||||
#include "lliosocket.h"
|
||||
#include "llthread.h"
|
||||
|
||||
class LLPluginProcessParentOwner
|
||||
{
|
||||
public:
|
||||
virtual ~LLPluginProcessParentOwner();
|
||||
virtual void receivePluginMessage(const LLPluginMessage &message) = 0;
|
||||
virtual bool receivePluginMessageEarly(const LLPluginMessage &message) {return false;};
|
||||
// This will only be called when the plugin has died unexpectedly
|
||||
virtual void pluginLaunchFailed() {};
|
||||
virtual void pluginDied() {};
|
||||
@@ -58,7 +65,10 @@ public:
|
||||
LLPluginProcessParent(LLPluginProcessParentOwner *owner);
|
||||
~LLPluginProcessParent();
|
||||
|
||||
void init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug, const std::string &user_data_path);
|
||||
void init(const std::string &launcher_filename,
|
||||
const std::string &plugin_filename,
|
||||
bool debug);
|
||||
|
||||
void idle(void);
|
||||
|
||||
// returns true if the plugin is on its way to steady state
|
||||
@@ -70,6 +80,9 @@ public:
|
||||
// returns true if the process has exited or we've had a fatal error
|
||||
bool isDone(void);
|
||||
|
||||
// returns true if the process is currently waiting on a blocking request
|
||||
bool isBlocked(void) { return mBlocked; };
|
||||
|
||||
void killSockets(void);
|
||||
|
||||
// Go to the proper error state
|
||||
@@ -83,7 +96,9 @@ public:
|
||||
void receiveMessage(const LLPluginMessage &message);
|
||||
|
||||
// Inherited from LLPluginMessagePipeOwner
|
||||
void receiveMessageRaw(const std::string &message);
|
||||
/*virtual*/ void receiveMessageRaw(const std::string &message);
|
||||
/*virtual*/ void receiveMessageEarly(const LLPluginMessage &message);
|
||||
/*virtual*/ void setMessagePipe(LLPluginMessagePipe *message_pipe) ;
|
||||
|
||||
// This adds a memory segment shared with the client, generating a name for the segment. The name generated is guaranteed to be unique on the host.
|
||||
// The caller must call removeSharedMemory first (and wait until getSharedMemorySize returns 0 for the indicated name) before re-adding a segment with the same name.
|
||||
@@ -106,7 +121,11 @@ public:
|
||||
void setLockupTimeout(F32 timeout) { mPluginLockupTimeout = timeout; };
|
||||
|
||||
F64 getCPUUsage() { return mCPUUsage; };
|
||||
|
||||
|
||||
static void poll(F64 timeout);
|
||||
static bool canPollThreadRun() { return (sPollSet || sPollsetNeedsRebuild || sUseReadThread); };
|
||||
static void setUseReadThread(bool use_read_thread);
|
||||
static bool getUseReadThread() { return sUseReadThread; };
|
||||
private:
|
||||
|
||||
enum EState
|
||||
@@ -128,6 +147,7 @@ private:
|
||||
};
|
||||
EState mState;
|
||||
void setState(EState state);
|
||||
std::string stateToString(EState state);
|
||||
|
||||
bool pluginLockedUp();
|
||||
bool pluginLockedUpOrQuit();
|
||||
@@ -142,8 +162,6 @@ private:
|
||||
|
||||
std::string mPluginFile;
|
||||
|
||||
std::string mUserDataPath;
|
||||
|
||||
LLPluginProcessParentOwner *mOwner;
|
||||
|
||||
typedef std::map<std::string, LLPluginSharedMemory*> sharedMemoryRegionsType;
|
||||
@@ -158,12 +176,27 @@ private:
|
||||
|
||||
bool mDisableTimeout;
|
||||
bool mDebug;
|
||||
bool mBlocked;
|
||||
bool mPolledInput;
|
||||
|
||||
LLProcessLauncher mDebugger;
|
||||
|
||||
F32 mPluginLaunchTimeout; // Somewhat longer timeout for initial launch.
|
||||
F32 mPluginLockupTimeout; // If we don't receive a heartbeat in this many seconds, we declare the plugin locked up.
|
||||
|
||||
static bool sUseReadThread;
|
||||
apr_pollfd_t mPollFD;
|
||||
static apr_pollset_t *sPollSet;
|
||||
static bool sPollsetNeedsRebuild;
|
||||
static LLMutex *sInstancesMutex;
|
||||
static std::list<LLPluginProcessParent*> sInstances;
|
||||
static void dirtyPollSet();
|
||||
static void updatePollset();
|
||||
void servicePoll();
|
||||
static LLThread *sReadThread;
|
||||
|
||||
LLMutex mIncomingQueueMutex;
|
||||
std::queue<LLPluginMessage> mIncomingQueue;
|
||||
};
|
||||
|
||||
#endif // LL_LLPLUGINPROCESSPARENT_H
|
||||
|
||||
11
indra/llplugin/llpluginsharedmemory.cpp
Normal file → Executable file
11
indra/llplugin/llpluginsharedmemory.cpp
Normal file → Executable file
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* @file llpluginsharedmemory.cpp
|
||||
* @brief LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API.
|
||||
* LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +13,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
10
indra/llplugin/llpluginsharedmemory.h
Normal file → Executable file
10
indra/llplugin/llpluginsharedmemory.h
Normal file → Executable file
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @file llpluginsharedmemory.h
|
||||
* @brief LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +12,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +28,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINSHAREDMEMORY_H
|
||||
|
||||
42
indra/llplugin/slplugin/CMakeLists.txt
Normal file → Executable file
42
indra/llplugin/slplugin/CMakeLists.txt
Normal file → Executable file
@@ -16,6 +16,7 @@ include_directories(
|
||||
if (DARWIN)
|
||||
include(CMakeFindFrameworks)
|
||||
find_library(CARBON_LIBRARY Carbon)
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
endif (DARWIN)
|
||||
|
||||
|
||||
@@ -25,11 +26,33 @@ set(SLPlugin_SOURCE_FILES
|
||||
slplugin.cpp
|
||||
)
|
||||
|
||||
if (DARWIN)
|
||||
list(APPEND SLPlugin_SOURCE_FILES
|
||||
slplugin-objc.mm
|
||||
)
|
||||
list(APPEND SLPlugin_HEADER_FILES
|
||||
slplugin-objc.h
|
||||
)
|
||||
endif (DARWIN)
|
||||
|
||||
set_source_files_properties(${SLPlugin_HEADER_FILES}
|
||||
PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
if (SLPlugin_HEADER_FILES)
|
||||
list(APPEND SLPlugin_SOURCE_FILES ${SLPlugin_HEADER_FILES})
|
||||
endif (SLPlugin_HEADER_FILES)
|
||||
|
||||
add_executable(SLPlugin
|
||||
WIN32
|
||||
MACOSX_BUNDLE
|
||||
${SLPlugin_SOURCE_FILES}
|
||||
)
|
||||
|
||||
set_target_properties(SLPlugin
|
||||
PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/slplugin_info.plist
|
||||
)
|
||||
|
||||
target_link_libraries(SLPlugin
|
||||
${LLPLUGIN_LIBRARIES}
|
||||
${LLMESSAGE_LIBRARIES}
|
||||
@@ -44,15 +67,16 @@ add_dependencies(SLPlugin
|
||||
)
|
||||
|
||||
if (DARWIN)
|
||||
# Mac version needs to link against carbon, and also needs an embedded plist (to set LSBackgroundOnly)
|
||||
target_link_libraries(SLPlugin ${CARBON_LIBRARY})
|
||||
set_target_properties(
|
||||
SLPlugin
|
||||
PROPERTIES
|
||||
LINK_FLAGS "-Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_SOURCE_DIR}/slplugin_info.plist"
|
||||
# Mac version needs to link against Carbon
|
||||
target_link_libraries(SLPlugin ${CARBON_LIBRARY} ${COCOA_LIBRARY})
|
||||
# Make sure the app bundle has a Resources directory (it will get populated by viewer-manifest.py later)
|
||||
add_custom_command(
|
||||
TARGET SLPlugin POST_BUILD
|
||||
COMMAND mkdir
|
||||
ARGS
|
||||
-p
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/SLPlugin.app/Contents/Resources
|
||||
)
|
||||
endif (DARWIN)
|
||||
|
||||
if (LINUX)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lrt")
|
||||
endif (LINUX)
|
||||
#ll_deploy_sharedlibs_command(SLPlugin)
|
||||
|
||||
42
indra/llplugin/slplugin/slplugin-objc.h
Normal file
42
indra/llplugin/slplugin/slplugin-objc.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @file slplugin-objc.h
|
||||
* @brief Header file for slplugin-objc.mm.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/* Defined in slplugin-objc.mm: */
|
||||
void setupCocoa();
|
||||
void createAutoReleasePool();
|
||||
void deleteAutoReleasePool();
|
||||
89
indra/llplugin/slplugin/slplugin-objc.mm
Normal file
89
indra/llplugin/slplugin/slplugin-objc.mm
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @file slplugin-objc.mm
|
||||
* @brief Objective-C++ file for use with the loader shell, so we can use a couple of Cocoa APIs.
|
||||
*
|
||||
* @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 <AppKit/AppKit.h>
|
||||
|
||||
#include "slplugin-objc.h"
|
||||
|
||||
|
||||
void setupCocoa()
|
||||
{
|
||||
static bool inited = false;
|
||||
|
||||
if(!inited)
|
||||
{
|
||||
createAutoReleasePool();
|
||||
|
||||
// The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents.
|
||||
// ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr'
|
||||
// when init'ing the Cocoa App window.
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
|
||||
|
||||
// This is a bit of voodoo taken from the Apple sample code "CarbonCocoa_PictureCursor":
|
||||
// http://developer.apple.com/samplecode/CarbonCocoa_PictureCursor/index.html
|
||||
|
||||
// Needed for Carbon based applications which call into Cocoa
|
||||
NSApplicationLoad();
|
||||
|
||||
// Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image
|
||||
[[[NSWindow alloc] init] release];
|
||||
|
||||
deleteAutoReleasePool();
|
||||
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
static NSAutoreleasePool *sPool = NULL;
|
||||
|
||||
void createAutoReleasePool()
|
||||
{
|
||||
if(!sPool)
|
||||
{
|
||||
sPool = [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
}
|
||||
|
||||
void deleteAutoReleasePool()
|
||||
{
|
||||
if(sPool)
|
||||
{
|
||||
[sPool release];
|
||||
sPool = NULL;
|
||||
}
|
||||
}
|
||||
170
indra/llplugin/slplugin/slplugin.cpp
Normal file → Executable file
170
indra/llplugin/slplugin/slplugin.cpp
Normal file → Executable file
@@ -1,10 +1,12 @@
|
||||
/**
|
||||
/**
|
||||
* @file slplugin.cpp
|
||||
* @brief Loader shell for plugins, intended to be launched by the plugin host application, which directly loads a plugin dynamic library.
|
||||
*
|
||||
* @cond
|
||||
*
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +14,13 @@
|
||||
* ("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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
* 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,
|
||||
@@ -28,6 +30,9 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
@@ -41,6 +46,7 @@
|
||||
|
||||
#if LL_DARWIN
|
||||
#include <Carbon/Carbon.h>
|
||||
#include "slplugin-objc.h"
|
||||
#endif
|
||||
|
||||
#if LL_DARWIN || LL_LINUX
|
||||
@@ -48,16 +54,17 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
On Mac OS, since we call WaitNextEvent, this process will show up in the dock unless we set the LSBackgroundOnly flag in the Info.plist.
|
||||
|
||||
On Mac OS, since we call WaitNextEvent, this process will show up in the dock unless we set the LSBackgroundOnly or LSUIElement flag in the Info.plist.
|
||||
|
||||
Normally non-bundled binaries don't have an info.plist file, but it's possible to embed one in the binary by adding this to the linker flags:
|
||||
|
||||
|
||||
-sectcreate __TEXT __info_plist /path/to/slplugin_info.plist
|
||||
|
||||
|
||||
which means adding this to the gcc flags:
|
||||
|
||||
|
||||
-Wl,-sectcreate,__TEXT,__info_plist,/path/to/slplugin_info.plist
|
||||
|
||||
Now that SLPlugin is a bundled app on the Mac, this is no longer necessary (it can just use a regular Info.plist file), but I'm leaving this comment in for posterity.
|
||||
*/
|
||||
|
||||
#if LL_DARWIN || LL_LINUX
|
||||
@@ -68,7 +75,7 @@ static void crash_handler(int sig)
|
||||
// TODO: add our own crash reporting
|
||||
_exit(1);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if LL_WINDOWS
|
||||
#include <windows.h>
|
||||
@@ -81,7 +88,7 @@ LONG WINAPI myWin32ExceptionHandler( struct _EXCEPTION_POINTERS* exception_infop
|
||||
//std::cerr << "intercepted an unhandled exception and will exit immediately." << std::endl;
|
||||
|
||||
// TODO: replace exception handler before we exit?
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
// Taken from : http://blog.kalmbachnet.de/?postid=75
|
||||
@@ -153,7 +160,7 @@ bool checkExceptionHandler()
|
||||
if (prev_filter == NULL)
|
||||
{
|
||||
ok = FALSE;
|
||||
if (myWin32ExceptionHandler == NULL)
|
||||
if (NULL == myWin32ExceptionHandler)
|
||||
{
|
||||
LL_WARNS("AppInit") << "Exception handler uninitialized." << LL_ENDL;
|
||||
}
|
||||
@@ -167,7 +174,7 @@ bool checkExceptionHandler()
|
||||
}
|
||||
#endif
|
||||
|
||||
// If this application on Windows platform is a console application, a console is always
|
||||
// If this application on Windows platform is a console application, a console is always
|
||||
// created which is bad. Making it a Windows "application" via CMake settings but not
|
||||
// adding any code to explicitly create windows does the right thing.
|
||||
#if LL_WINDOWS
|
||||
@@ -178,7 +185,7 @@ int main(int argc, char **argv)
|
||||
{
|
||||
ll_init_apr();
|
||||
|
||||
// Set up llerror logging
|
||||
// Set up llerror logging
|
||||
{
|
||||
LLError::initForApplication(".");
|
||||
LLError::setDefaultLevel(LLError::LEVEL_INFO);
|
||||
@@ -191,25 +198,23 @@ int main(int argc, char **argv)
|
||||
{
|
||||
LL_ERRS("slplugin") << "usage: " << "SLPlugin" << " launcher_port" << LL_ENDL;
|
||||
};
|
||||
|
||||
|
||||
U32 port = 0;
|
||||
if(!LLStringUtil::convertToU32(lpCmdLine, port))
|
||||
{
|
||||
LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL;
|
||||
};
|
||||
|
||||
// Insert our exception handler into the system so this plugin doesn't
|
||||
// Insert our exception handler into the system so this plugin doesn't
|
||||
// display a crash message if something bad happens. The host app will
|
||||
// see the missing heartbeat and log appropriately.
|
||||
initExceptionHandler();
|
||||
#elif LL_DARWIN || LL_LINUX
|
||||
setpriority(PRIO_PROCESS, getpid(), 19);
|
||||
|
||||
if(argc < 2)
|
||||
{
|
||||
LL_ERRS("slplugin") << "usage: " << argv[0] << " launcher_port" << LL_ENDL;
|
||||
}
|
||||
|
||||
|
||||
U32 port = 0;
|
||||
if(!LLStringUtil::convertToU32(argv[1], port))
|
||||
{
|
||||
@@ -227,23 +232,50 @@ int main(int argc, char **argv)
|
||||
signal(SIGSYS, &crash_handler); // non-existent system call invoked
|
||||
#endif
|
||||
|
||||
#if LL_DARWIN
|
||||
setupCocoa();
|
||||
createAutoReleasePool();
|
||||
#endif
|
||||
|
||||
LLPluginProcessChild *plugin = new LLPluginProcessChild();
|
||||
|
||||
plugin->init(port);
|
||||
|
||||
|
||||
#if LL_DARWIN
|
||||
deleteAutoReleasePool();
|
||||
#endif
|
||||
|
||||
LLTimer timer;
|
||||
timer.start();
|
||||
|
||||
#if LL_WINDOWS
|
||||
checkExceptionHandler();
|
||||
#endif
|
||||
|
||||
|
||||
#if LL_DARWIN
|
||||
// If the plugin opens a new window (such as the Flash plugin's fullscreen player), we may need to bring this plugin process to the foreground.
|
||||
// Use this to track the current frontmost window and bring this process to the front if it changes.
|
||||
WindowRef front_window = NULL;
|
||||
WindowGroupRef layer_group = NULL;
|
||||
int window_hack_state = 0;
|
||||
CreateWindowGroup(kWindowGroupAttrFixedLevel, &layer_group);
|
||||
if(layer_group)
|
||||
{
|
||||
// Start out with a window layer that's way out in front (fixes the problem with the menubar not getting hidden on first switch to fullscreen youtube)
|
||||
SetWindowGroupName(layer_group, CFSTR("SLPlugin Layer"));
|
||||
SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LL_DARWIN
|
||||
EventTargetRef event_target = GetEventDispatcherTarget();
|
||||
#endif
|
||||
while(!plugin->isDone())
|
||||
{
|
||||
timer.reset();
|
||||
#if LL_DARWIN
|
||||
createAutoReleasePool();
|
||||
#endif
|
||||
timer.reset();
|
||||
plugin->idle();
|
||||
#if LL_DARWIN
|
||||
{
|
||||
@@ -254,11 +286,85 @@ int main(int argc, char **argv)
|
||||
SendEventToEventTarget (event, event_target);
|
||||
ReleaseEvent(event);
|
||||
}
|
||||
|
||||
// Check for a change in this process's frontmost window.
|
||||
if(FrontWindow() != front_window)
|
||||
{
|
||||
ProcessSerialNumber self = { 0, kCurrentProcess };
|
||||
ProcessSerialNumber parent = { 0, kNoProcess };
|
||||
ProcessSerialNumber front = { 0, kNoProcess };
|
||||
Boolean this_is_front_process = false;
|
||||
Boolean parent_is_front_process = false;
|
||||
{
|
||||
// Get this process's parent
|
||||
ProcessInfoRec info;
|
||||
info.processInfoLength = sizeof(ProcessInfoRec);
|
||||
info.processName = NULL;
|
||||
info.processAppSpec = NULL;
|
||||
if(GetProcessInformation( &self, &info ) == noErr)
|
||||
{
|
||||
parent = info.processLauncher;
|
||||
}
|
||||
|
||||
// and figure out whether this process or its parent are currently frontmost
|
||||
if(GetFrontProcess(&front) == noErr)
|
||||
{
|
||||
(void) SameProcess(&self, &front, &this_is_front_process);
|
||||
(void) SameProcess(&parent, &front, &parent_is_front_process);
|
||||
}
|
||||
}
|
||||
|
||||
if((FrontWindow() != NULL) && (front_window == NULL))
|
||||
{
|
||||
// Opening the first window
|
||||
|
||||
if(window_hack_state == 0)
|
||||
{
|
||||
// Next time through the event loop, lower the window group layer
|
||||
window_hack_state = 1;
|
||||
}
|
||||
|
||||
if(layer_group)
|
||||
{
|
||||
SetWindowGroup(FrontWindow(), layer_group);
|
||||
}
|
||||
|
||||
if(parent_is_front_process)
|
||||
{
|
||||
// Bring this process's windows to the front.
|
||||
(void) SetFrontProcess( &self );
|
||||
}
|
||||
|
||||
ActivateWindow(FrontWindow(), true);
|
||||
}
|
||||
else if((FrontWindow() == NULL) && (front_window != NULL))
|
||||
{
|
||||
// Closing the last window
|
||||
|
||||
if(this_is_front_process)
|
||||
{
|
||||
// Try to bring this process's parent to the front
|
||||
(void) SetFrontProcess(&parent);
|
||||
}
|
||||
}
|
||||
else if(window_hack_state == 1)
|
||||
{
|
||||
if(layer_group)
|
||||
{
|
||||
// Set the window group level back to something less extreme
|
||||
SetWindowGroupLevel(layer_group, kCGNormalWindowLevel);
|
||||
}
|
||||
window_hack_state = 2;
|
||||
}
|
||||
|
||||
front_window = FrontWindow();
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
F64 elapsed = timer.getElapsedTimeF64();
|
||||
F64 remaining = plugin->getSleepTime() - elapsed;
|
||||
|
||||
|
||||
if(remaining <= 0.0f)
|
||||
{
|
||||
// We've already used our full allotment.
|
||||
@@ -271,26 +377,30 @@ int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
// LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, sleeping for " << remaining * 1000.0f << " ms" << LL_ENDL;
|
||||
// timer.reset();
|
||||
|
||||
// timer.reset();
|
||||
|
||||
// This also services the network as needed.
|
||||
plugin->sleep(remaining);
|
||||
|
||||
|
||||
// LL_INFOS("slplugin") << "slept for "<< timer.getElapsedTimeF64() * 1000.0f << " ms" << LL_ENDL;
|
||||
}
|
||||
|
||||
#if LL_WINDOWS
|
||||
// More agressive checking of interfering exception handlers.
|
||||
// Doesn't appear to be required so far - even for plugins
|
||||
// that do crash with a single call to the intercept
|
||||
// Doesn't appear to be required so far - even for plugins
|
||||
// that do crash with a single call to the intercept
|
||||
// exception handler such as QuickTime.
|
||||
//checkExceptionHandler();
|
||||
#endif
|
||||
|
||||
#if LL_DARWIN
|
||||
deleteAutoReleasePool();
|
||||
#endif
|
||||
}
|
||||
|
||||
delete plugin;
|
||||
|
||||
ll_cleanup_apr();
|
||||
|
||||
ll_cleanup_apr();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
4
indra/llplugin/slplugin/slplugin_info.plist
Normal file → Executable file
4
indra/llplugin/slplugin/slplugin_info.plist
Normal file → Executable file
@@ -6,7 +6,7 @@
|
||||
<string>English</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>LSBackgroundOnly</key>
|
||||
<true/>
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
Reference in New Issue
Block a user