Commits

Monroe Linden committed 03bdb3e

svn merge -r 134922:134973 svn+ssh://svn.lindenlab.com/svn/linden/branches/media-on-a-prim/moap-7

Merging branches/media-on-a-prim/moap-7 down to viewer-2.0.

  • Participants
  • Parent commits 01a0022

Comments (0)

Files changed (88)

 		
 			<key>FetchLib</key>
 			<boolean>true</boolean>
-		</map>
+
+            <key>ObjectMedia</key>
+            <boolean>false</boolean>
+                  
+            <key>ObjectMediaNavigate</key>
+            <boolean>false</boolean>
+                  
+      </map>
 
 		<key>messageBans</key>
 		<map>

indra/llcommon/lllslconstants.h

 const U32	CHANGED_REGION = 0x100;
 const U32	CHANGED_TELEPORT = 0x200;
 const U32	CHANGED_REGION_START = 0x400;
+const U32   CHANGED_MEDIA = 0x800;
+
+// Possible error results
+const U32 LSL_STATUS_OK                 = 0;
+const U32 LSL_STATUS_MALFORMED_PARAMS   = 1000;
+const U32 LSL_STATUS_TYPE_MISMATCH      = 1001;
+const U32 LSL_STATUS_BOUNDS_ERROR       = 1002;
+const U32 LSL_STATUS_NOT_FOUND          = 1003;
+const U32 LSL_STATUS_NOT_SUPPORTED      = 1004;
+const U32 LSL_STATUS_INTERNAL_ERROR     = 1999;
+
+// Start per-function errors below, starting at 2000:
+const U32 LSL_STATUS_WHITELIST_FAILED   = 2001;
 
 #endif

indra/llplugin/llpluginclassmedia.cpp

 	mSetMediaHeight = -1;
 	mRequestedMediaWidth = 0;
 	mRequestedMediaHeight = 0;
+	mFullMediaWidth = 0;
+	mFullMediaHeight = 0;
 	mTextureWidth = 0;
 	mTextureHeight = 0;
 	mMediaWidth = 0;
 
 void LLPluginClassMedia::setSize(int width, int height)
 {
-	mSetMediaWidth = width;
-	mSetMediaHeight = height;
+	if((width > 0) && (height > 0))
+	{
+		mSetMediaWidth = width;
+		mSetMediaHeight = height;
+	}
+	else
+	{
+		mSetMediaWidth = -1;
+		mSetMediaHeight = -1;
+	}
 
 	setSizeInternal();
 }
 		mRequestedMediaWidth = mSetMediaWidth;
 		mRequestedMediaHeight = mSetMediaHeight;
 	}
+	else if((mNaturalMediaWidth > 0) && (mNaturalMediaHeight > 0))
+	{
+		mRequestedMediaWidth = mNaturalMediaWidth;
+		mRequestedMediaHeight = mNaturalMediaHeight;
+	}
 	else
 	{
 		mRequestedMediaWidth = mDefaultMediaWidth;
 		mRequestedMediaHeight = mDefaultMediaHeight;
 	}
 	
+	// Save these for size/interest calculations
+	mFullMediaWidth = mRequestedMediaWidth;
+	mFullMediaHeight = mRequestedMediaHeight;
+	
 	if(mAllowDownsample)
 	{
 		switch(mPriority)
 		{
+			case PRIORITY_SLIDESHOW:
 			case PRIORITY_LOW:
 				// Reduce maximum texture dimension to (or below) mLowPrioritySizeLimit
 				while((mRequestedMediaWidth > mLowPrioritySizeLimit) || (mRequestedMediaHeight > mLowPrioritySizeLimit))
 		mRequestedMediaWidth = nextPowerOf2(mRequestedMediaWidth);
 		mRequestedMediaHeight = nextPowerOf2(mRequestedMediaHeight);
 	}
+	
+	if(mRequestedMediaWidth > 2048)
+		mRequestedMediaWidth = 2048;
+
+	if(mRequestedMediaHeight > 2048)
+		mRequestedMediaHeight = 2048;
 }
 
 void LLPluginClassMedia::setAutoScale(bool auto_scale)
 		std::string priority_string;
 		switch(priority)
 		{
+			case PRIORITY_UNLOADED:	
+				priority_string = "unloaded";	
+				mSleepTime = 1.0f;
+			break;
 			case PRIORITY_STOPPED:	
 				priority_string = "stopped";	
 				mSleepTime = 1.0f;
 				priority_string = "hidden";	
 				mSleepTime = 1.0f;
 			break;
+			case PRIORITY_SLIDESHOW:
+				priority_string = "slideshow";		
+				mSleepTime = 1.0f;
+			break;
 			case PRIORITY_LOW:		
 				priority_string = "low";		
 				mSleepTime = 1.0f / 50.0f;
 			mPlugin->setSleepTime(mSleepTime);
 		}
 		
+		LL_DEBUGS("PluginPriority") << this << ": setting priority to " << priority_string << LL_ENDL;
+		
 		// This may affect the calculated size, so recalculate it here.
 		setSizeInternal();
 	}
 
 void LLPluginClassMedia::setLowPrioritySizeLimit(int size)
 {
-	if(mLowPrioritySizeLimit != size)
+	int power = nextPowerOf2(size);
+	if(mLowPrioritySizeLimit != power)
 	{
-		mLowPrioritySizeLimit = size;
+		mLowPrioritySizeLimit = power;
 
 		// This may affect the calculated size, so recalculate it here.
 		setSizeInternal();
 	}
 }
 
+F64 LLPluginClassMedia::getCPUUsage()
+{
+	F64 result = 0.0f;
+	
+	if(mPlugin)
+	{
+		result = mPlugin->getCPUUsage();
+	}
+	
+	return result;
+}
 
 void LLPluginClassMedia::cut()
 {
 			mNaturalMediaWidth = width;
 			mNaturalMediaHeight = height;
 			
-			setSize(width, height);
+			setSizeInternal();
 		}
 		else if(message_name == "size_change_response")
 		{

indra/llplugin/llpluginclassmedia.h

 	int getBitsHeight() const { return (mTextureHeight > 0) ? mTextureHeight : 0; };
 	int getTextureWidth() const;
 	int getTextureHeight() const;
+	int getFullWidth() const { return mFullMediaWidth; };
+	int getFullHeight() const { return mFullMediaHeight; };
 	
 	// This may return NULL.  Callers need to check for and handle this case.
 	unsigned char* getBitsData();
 	
 	typedef enum 
 	{
+		PRIORITY_UNLOADED,	// media plugin isn't even loaded.
 		PRIORITY_STOPPED,	// media is not playing, shouldn't need to update at all.
 		PRIORITY_HIDDEN,	// media is not being displayed or is out of view, don't need to do graphic updates, but may still update audio, playhead, etc.
-		PRIORITY_LOW,		// media is in the far distance, may be rendered at reduced size
+		PRIORITY_SLIDESHOW,	// media is in the far distance, updates very infrequently
+		PRIORITY_LOW,		// media is in the distance, may be rendered at reduced size
 		PRIORITY_NORMAL,	// normal (default) priority
 		PRIORITY_HIGH		// media has user focus and/or is taking up most of the screen
 	}EPriority;
 	void setPriority(EPriority priority);
 	void setLowPrioritySizeLimit(int size);
 	
+	F64 getCPUUsage();
+
 	// Valid after a MEDIA_EVENT_CURSOR_CHANGED event
 	std::string getCursorName() const { return mCursorName; };
 
 	void initializeUrlHistory(const LLSD& url_history);
 
 protected:
+
 	LLPluginClassMediaOwner *mOwner;
 
 	// Notify this object's owner that an event has occurred.
 	int			mSetMediaWidth;
 	int			mSetMediaHeight;
 	
-	// Actual media size being set (may be affected by auto-scale)
+	// Full calculated media size (before auto-scale and downsample calculations)
+	int			mFullMediaWidth;
+	int			mFullMediaHeight;
+
+	// Actual media size being set (after auto-scale)
 	int			mRequestedMediaWidth;
 	int			mRequestedMediaHeight;
 	

indra/llplugin/llpluginmessage.cpp

 
 #include "llpluginmessage.h"
 #include "llsdserialize.h"
+#include "u64.h"
 
 LLPluginMessage::LLPluginMessage()
 {
 	mMessage["params"][key] = value;
 }
 
+void LLPluginMessage::setValuePointer(const std::string &key, void* value)
+{
+	std::stringstream temp;
+	// iostreams should output pointer values in hex with an initial 0x by default.
+	temp << value;
+	setValue(key, temp.str());
+}
+
 std::string LLPluginMessage::getClass(void) const
 {
 	return mMessage["class"];
 	return result;
 }
 
+void* LLPluginMessage::getValuePointer(const std::string &key) const
+{
+	void* result = NULL;
+
+	if(mMessage["params"].has(key))
+	{
+		std::string value = mMessage["params"][key].asString();
+		
+		result = (void*)llstrtou64(value.c_str(), NULL, 16);
+	}
+	
+	return result;
+}
+
 std::string LLPluginMessage::generate(void) const
 {
 	std::ostringstream result;

indra/llplugin/llpluginmessage.h

 	void setValueU32(const std::string &key, U32 value);
 	void setValueBoolean(const std::string &key, bool value);
 	void setValueReal(const std::string &key, F64 value);
+	void setValuePointer(const std::string &key, void *value);
 	
 	std::string getClass(void) const;
 	std::string getName(void) const;
 	// get the value of a key as a float.
 	F64 getValueReal(const std::string &key) const;
 
+	// get the value of a key as a pointer.
+	void* getValuePointer(const std::string &key) const;
+
 	// Flatten the message into a string
 	std::string generate(void) const;
 

indra/llplugin/llpluginprocesschild.cpp

 	mInstance = NULL;
 	mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
 	mSleepTime = 1.0f / 100.0f;	// default: send idle messages at 100Hz
+	mCPUElapsed = 0.0f;
 }
 
 LLPluginProcessChild::~LLPluginProcessChild()
 					{
 						mHeartbeat.start();
 						mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
+						mCPUElapsed = 0.0f;
 						setState(STATE_PLUGIN_LOADED);
 					}
 					else
 					
 					mInstance->idle();
 					
-					if(mHeartbeat.checkExpirationAndReset(HEARTBEAT_SECONDS))
+					if(mHeartbeat.hasExpired())
 					{
+						
 						// This just proves that we're not stuck down inside the plugin code.
-						sendMessageToParent(LLPluginMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "heartbeat"));
+						LLPluginMessage heartbeat(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "heartbeat");
+						
+						// Calculate the approximage CPU usage fraction (floating point value between 0 and 1) used by the plugin this heartbeat cycle.
+						// Note that this will not take into account any threads or additional processes the plugin spawns, but it's a first approximation.
+						// If we could write OS-specific functions to query the actual CPU usage of this process, that would be a better approximation.
+						heartbeat.setValueReal("cpu_usage", mCPUElapsed / mHeartbeat.getElapsedTimeF64());
+						
+						sendMessageToParent(heartbeat);
+
+						mHeartbeat.reset();
+						mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
+						mCPUElapsed = 0.0f;
 					}
 				}
 				// receivePluginMessage will transition to STATE_UNLOADING
 	std::string buffer = message.generate();
 
 	LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL;
+	LLTimer elapsed;
+	
+	mInstance->sendMessage(buffer);
 
-	mInstance->sendMessage(buffer);
+	mCPUElapsed += elapsed.getElapsedTimeF64();
 }
 
 void LLPluginProcessChild::sendMessageToParent(const LLPluginMessage &message)
 						LLPluginMessage message("base", "shm_added");
 						message.setValue("name", name);
 						message.setValueS32("size", (S32)size);
-						// shm address is split into 2x32bit values because LLSD doesn't serialize 64bit values and we need to support 64-bit addressing.
-						void * address = region->getMappedAddress();
-						U32 address_lo = (U32)(U64(address) & 0xFFFFFFFF);			// Extract the lower 32 bits
-						U32 address_hi = (U32)((U64(address)>>32) & 0xFFFFFFFF);	// Extract the higher 32 bits 
-						message.setValueU32("address", address_lo);
-						message.setValueU32("address_1", address_hi);
+						message.setValuePointer("address", region->getMappedAddress());
 						sendMessageToPlugin(message);
 						
 						// and send the response to the parent
 	
 	if(passMessage && mInstance != NULL)
 	{
+		LLTimer elapsed;
+
 		mInstance->sendMessage(message);
+
+		mCPUElapsed += elapsed.getElapsedTimeF64();
 	}
 }
 
 	
 	if(passMessage)
 	{
+		LL_DEBUGS("Plugin") << "Passing through to parent: " << message << LL_ENDL;
 		writeMessageRaw(message);
 	}
 }

indra/llplugin/llpluginprocesschild.h

 	
 	LLTimer mHeartbeat;
 	F64		mSleepTime;
+	F64		mCPUElapsed;
 	
 };
 

indra/llplugin/llpluginprocessparent.cpp

 #include "llapr.h"
 
 // If we don't receive a heartbeat in this many seconds, we declare the plugin locked up.
-static const F32 PLUGIN_LOCKED_UP_SECONDS = 10.0f;
+static const F32 PLUGIN_LOCKED_UP_SECONDS = 15.0f;
 
 // Somewhat longer timeout for initial launch.
 static const F32 PLUGIN_LAUNCH_SECONDS = 20.0f;
 {	
 	mProcess.setExecutable(launcher_filename);
 	mPluginFile = plugin_filename;
+	mCPUUsage = 0.0f;
 	
 	setState(STATE_INITIALIZED);
 }
 		{
 			// this resets our timer.
 			mHeartbeat.setTimerExpirySec(PLUGIN_LOCKED_UP_SECONDS);
+
+			mCPUUsage = message.getValueReal("cpu_usage");
+
+			LL_DEBUGS("Plugin") << "cpu usage reported as " << mCPUUsage << LL_ENDL;
+			
 		}
 		else if(message_name == "shm_add_response")
 		{

indra/llplugin/llpluginprocessparent.h

 	bool getDisableTimeout() { return mDisableTimeout; };
 	void setDisableTimeout(bool disable) { mDisableTimeout = disable; };
 
+	F64 getCPUUsage() { return mCPUUsage; };
+
 private:
 
 	enum EState
 	
 	LLTimer mHeartbeat;
 	F64		mSleepTime;
+	F64		mCPUUsage;
 	
 	bool mDisableTimeout;
 };

indra/llprimitive/CMakeLists.txt

 
 set(llprimitive_SOURCE_FILES
     llmaterialtable.cpp
+    llmediaentry.cpp
     llprimitive.cpp
     llprimtexturelist.cpp
     lltextureanim.cpp
 
     legacy_object_types.h
     llmaterialtable.h
+    llmediaentry.h
     llprimitive.h
     llprimtexturelist.h
     lltextureanim.h
 list(APPEND llprimitive_SOURCE_FILES ${llprimitive_HEADER_FILES})
 
 add_library (llprimitive ${llprimitive_SOURCE_FILES})
+
+#add unit tests
+INCLUDE(LLAddBuildTest)
+SET(llprimitive_TEST_SOURCE_FILES
+  llmediaentry.cpp
+  )
+LL_ADD_PROJECT_UNIT_TESTS(llprimitive "${llprimitive_TEST_SOURCE_FILES}")

indra/llprimitive/llmediaentry.cpp

+/** 
+ * @file llmediaentry.cpp
+ * @brief This is a single instance of media data related to the face of a prim
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ * 
+ * Copyright (c) 2001-2009, 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://secondlifegrid.net/programs/open_source/licensing/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
+ * 
+ * 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$
+ */
+
+#include "linden_common.h"
+#include "llmediaentry.h"
+#include "lllslconstants.h"
+
+#include <boost/regex.hpp>
+
+// LLSD key defines
+// DO NOT REORDER OR REMOVE THESE!
+
+// Some LLSD keys.  Do not change!
+#define MEDIA_ALT_IMAGE_ENABLE_KEY_STR   "alt_image_enable"
+#define MEDIA_CONTROLS_KEY_STR           "controls"
+#define MEDIA_CURRENT_URL_KEY_STR        "current_url"
+#define MEDIA_HOME_URL_KEY_STR           "home_url"
+#define MEDIA_AUTO_LOOP_KEY_STR          "auto_loop"
+#define MEDIA_AUTO_PLAY_KEY_STR          "auto_play"
+#define MEDIA_AUTO_SCALE_KEY_STR         "auto_scale"
+#define MEDIA_AUTO_ZOOM_KEY_STR          "auto_zoom"
+#define MEDIA_FIRST_CLICK_INTERACT_KEY_STR  "first_click_interact"
+#define MEDIA_WIDTH_PIXELS_KEY_STR       "width_pixels"
+#define MEDIA_HEIGHT_PIXELS_KEY_STR      "height_pixels"
+
+// "security" fields
+#define MEDIA_WHITELIST_ENABLE_KEY_STR   "whitelist_enable"
+#define MEDIA_WHITELIST_KEY_STR          "whitelist"
+
+// "permissions" fields
+#define MEDIA_PERMS_INTERACT_KEY_STR     "perms_interact"
+#define MEDIA_PERMS_CONTROL_KEY_STR      "perms_control"
+
+// "general" fields
+const char* LLMediaEntry::ALT_IMAGE_ENABLE_KEY  = MEDIA_ALT_IMAGE_ENABLE_KEY_STR;
+const char* LLMediaEntry::CONTROLS_KEY          = MEDIA_CONTROLS_KEY_STR;
+const char* LLMediaEntry::CURRENT_URL_KEY       = MEDIA_CURRENT_URL_KEY_STR;
+const char* LLMediaEntry::HOME_URL_KEY          = MEDIA_HOME_URL_KEY_STR;
+const char* LLMediaEntry::AUTO_LOOP_KEY         = MEDIA_AUTO_LOOP_KEY_STR;
+const char* LLMediaEntry::AUTO_PLAY_KEY         = MEDIA_AUTO_PLAY_KEY_STR;
+const char* LLMediaEntry::AUTO_SCALE_KEY        = MEDIA_AUTO_SCALE_KEY_STR;
+const char* LLMediaEntry::AUTO_ZOOM_KEY         = MEDIA_AUTO_ZOOM_KEY_STR;
+const char* LLMediaEntry::FIRST_CLICK_INTERACT_KEY = MEDIA_FIRST_CLICK_INTERACT_KEY_STR;
+const char* LLMediaEntry::WIDTH_PIXELS_KEY      = MEDIA_WIDTH_PIXELS_KEY_STR;
+const char* LLMediaEntry::HEIGHT_PIXELS_KEY     = MEDIA_HEIGHT_PIXELS_KEY_STR;
+
+// "security" fields
+const char* LLMediaEntry::WHITELIST_ENABLE_KEY  = MEDIA_WHITELIST_ENABLE_KEY_STR;
+const char* LLMediaEntry::WHITELIST_KEY         = MEDIA_WHITELIST_KEY_STR;
+
+// "permissions" fields
+const char* LLMediaEntry::PERMS_INTERACT_KEY    = MEDIA_PERMS_INTERACT_KEY_STR;
+const char* LLMediaEntry::PERMS_CONTROL_KEY     = MEDIA_PERMS_CONTROL_KEY_STR;
+
+#define DEFAULT_URL_PREFIX  "http://"
+
+// Constructor(s)
+LLMediaEntry::LLMediaEntry() :
+    mAltImageEnable(false),
+    mControls(STANDARD),
+    mCurrentURL(""),
+    mHomeURL(""),
+    mAutoLoop(false),
+    mAutoPlay(false),
+    mAutoScale(false),
+    mAutoZoom(false),
+    mFirstClickInteract(false),
+    mWidthPixels(0),
+    mHeightPixels(0),
+    mWhiteListEnable(false),
+    // mWhiteList
+    mPermsInteract(PERM_ALL),
+    mPermsControl(PERM_ALL),
+    mMediaIDp(NULL)
+{
+}
+
+LLMediaEntry::LLMediaEntry(const LLMediaEntry &rhs) :
+    mMediaIDp(NULL)
+{
+    // "general" fields
+    mAltImageEnable = rhs.mAltImageEnable;
+    mControls = rhs.mControls;
+    mCurrentURL = rhs.mCurrentURL;
+    mHomeURL = rhs.mHomeURL;
+    mAutoLoop = rhs.mAutoLoop;
+    mAutoPlay = rhs.mAutoPlay;
+    mAutoScale = rhs.mAutoScale;
+    mAutoZoom = rhs.mAutoZoom;
+    mFirstClickInteract = rhs.mFirstClickInteract;
+    mWidthPixels = rhs.mWidthPixels;
+    mHeightPixels = rhs.mHeightPixels;
+
+    // "security" fields
+    mWhiteListEnable = rhs.mWhiteListEnable;
+    mWhiteList = rhs.mWhiteList;
+
+    // "permissions" fields
+    mPermsInteract = rhs.mPermsInteract;
+    mPermsControl = rhs.mPermsControl;
+}
+
+LLMediaEntry::~LLMediaEntry()
+{
+    if (NULL != mMediaIDp)
+    {
+        delete mMediaIDp;
+    }
+}
+
+LLSD LLMediaEntry::asLLSD() const
+{
+    LLSD sd;
+    asLLSD(sd);
+    return sd;
+}
+
+//
+// LLSD functions
+//
+void LLMediaEntry::asLLSD(LLSD& sd) const
+{
+    // "general" fields
+    sd[ALT_IMAGE_ENABLE_KEY] = mAltImageEnable;
+    sd[CONTROLS_KEY] = (LLSD::Integer)mControls;
+    sd[CURRENT_URL_KEY] = mCurrentURL;
+    sd[HOME_URL_KEY] = mHomeURL;
+    sd[AUTO_LOOP_KEY] = mAutoLoop;
+    sd[AUTO_PLAY_KEY] = mAutoPlay;
+    sd[AUTO_SCALE_KEY] = mAutoScale;
+    sd[AUTO_ZOOM_KEY] = mAutoZoom;
+    sd[FIRST_CLICK_INTERACT_KEY] = mFirstClickInteract;
+    sd[WIDTH_PIXELS_KEY] = mWidthPixels;
+    sd[HEIGHT_PIXELS_KEY] = mHeightPixels;
+
+    // "security" fields
+    sd[WHITELIST_ENABLE_KEY] = mWhiteListEnable;
+    for (U32 i=0; i<mWhiteList.size(); i++) 
+	{
+        sd[WHITELIST_KEY].append(mWhiteList[i]);
+    }
+
+    // "permissions" fields
+    sd[PERMS_INTERACT_KEY] = mPermsInteract;
+    sd[PERMS_CONTROL_KEY] = mPermsControl;
+}
+
+// static
+bool LLMediaEntry::checkLLSD(const LLSD& sd)
+{
+    if (sd.isUndefined()) return true;
+    LLMediaEntry temp;
+    return temp.fromLLSDInternal(sd, true);
+}
+
+void LLMediaEntry::fromLLSD(const LLSD& sd)
+{
+    (void)fromLLSDInternal(sd, true);
+}
+
+void LLMediaEntry::mergeFromLLSD(const LLSD& sd)
+{
+    (void)fromLLSDInternal(sd, false);
+}
+
+// *NOTE: returns true if NO failures to set occurred, false otherwise.
+//        However, be aware that if a failure to set does occur, it does
+//        not stop setting fields from the LLSD!
+bool LLMediaEntry::fromLLSDInternal(const LLSD& sd, bool overwrite)
+{
+    // *HACK: we sort of cheat here and assume that status is a
+    // bit field.  We "or" into status and instead of returning
+    // it, we return whether it finishes off as LSL_STATUS_OK or not.
+    U32 status = LSL_STATUS_OK;
+    
+    // "general" fields
+    if ( overwrite || sd.has(ALT_IMAGE_ENABLE_KEY) )
+    {
+        status |= setAltImageEnable( sd[ALT_IMAGE_ENABLE_KEY] );
+    }
+    if ( overwrite || sd.has(CONTROLS_KEY) )
+    {
+        status |= setControls( (MediaControls)(LLSD::Integer)sd[CONTROLS_KEY] );
+    }
+    if ( overwrite || sd.has(CURRENT_URL_KEY) )
+    {
+        // Don't check whitelist
+        status |= setCurrentURLInternal( sd[CURRENT_URL_KEY], false );
+    }
+    if ( overwrite || sd.has(HOME_URL_KEY) )
+    {
+        status |= setHomeURL( sd[HOME_URL_KEY] );
+    }
+    if ( overwrite || sd.has(AUTO_LOOP_KEY) )
+    {
+        status |= setAutoLoop( sd[AUTO_LOOP_KEY] );
+    }
+    if ( overwrite || sd.has(AUTO_PLAY_KEY) )
+    {
+        status |= setAutoPlay( sd[AUTO_PLAY_KEY] );
+    }
+    if ( overwrite || sd.has(AUTO_SCALE_KEY) )
+    {
+        status |= setAutoScale( sd[AUTO_SCALE_KEY] );
+    }
+    if ( overwrite || sd.has(AUTO_ZOOM_KEY) )
+    {
+        status |= setAutoZoom( sd[AUTO_ZOOM_KEY] );
+    }
+    if ( overwrite || sd.has(FIRST_CLICK_INTERACT_KEY) )
+    {
+        status |= setFirstClickInteract( sd[FIRST_CLICK_INTERACT_KEY] );
+    }
+    if ( overwrite || sd.has(WIDTH_PIXELS_KEY) )
+    {
+        status |= setWidthPixels( (LLSD::Integer)sd[WIDTH_PIXELS_KEY] );
+    }
+    if ( overwrite || sd.has(HEIGHT_PIXELS_KEY) )
+    {
+        status |= setHeightPixels( (LLSD::Integer)sd[HEIGHT_PIXELS_KEY] );
+    }
+
+    // "security" fields
+    if ( overwrite || sd.has(WHITELIST_ENABLE_KEY) )
+    {
+        status |= setWhiteListEnable( sd[WHITELIST_ENABLE_KEY] );
+    }
+    if ( overwrite || sd.has(WHITELIST_KEY) )
+    {
+        status |= setWhiteList( sd[WHITELIST_KEY] );
+    }
+
+    // "permissions" fields
+    if ( overwrite || sd.has(PERMS_INTERACT_KEY) )
+    {
+        status |= setPermsInteract( 0xff & (LLSD::Integer)sd[PERMS_INTERACT_KEY] );
+    }
+    if ( overwrite || sd.has(PERMS_CONTROL_KEY) )
+    {
+        status |= setPermsControl( 0xff & (LLSD::Integer)sd[PERMS_CONTROL_KEY] );
+    }
+    
+    return LSL_STATUS_OK == status;
+}
+
+LLMediaEntry& LLMediaEntry::operator=(const LLMediaEntry &rhs)
+{
+    if (this != &rhs)
+    {
+        // "general" fields
+        mAltImageEnable = rhs.mAltImageEnable;
+        mControls = rhs.mControls;
+        mCurrentURL = rhs.mCurrentURL;
+        mHomeURL = rhs.mHomeURL;
+        mAutoLoop = rhs.mAutoLoop;
+        mAutoPlay = rhs.mAutoPlay;
+        mAutoScale = rhs.mAutoScale;
+        mAutoZoom = rhs.mAutoZoom;
+        mFirstClickInteract = rhs.mFirstClickInteract;
+        mWidthPixels = rhs.mWidthPixels;
+        mHeightPixels = rhs.mHeightPixels;
+
+        // "security" fields
+        mWhiteListEnable = rhs.mWhiteListEnable;
+        mWhiteList = rhs.mWhiteList;
+
+        // "permissions" fields
+        mPermsInteract = rhs.mPermsInteract;
+        mPermsControl = rhs.mPermsControl;
+    }
+
+    return *this;
+}
+
+bool LLMediaEntry::operator==(const LLMediaEntry &rhs) const
+{
+    return (
+        // "general" fields
+        mAltImageEnable == rhs.mAltImageEnable &&
+        mControls == rhs.mControls &&
+        mCurrentURL == rhs.mCurrentURL &&
+        mHomeURL == rhs.mHomeURL &&
+        mAutoLoop == rhs.mAutoLoop &&
+        mAutoPlay == rhs.mAutoPlay &&
+        mAutoScale == rhs.mAutoScale &&
+        mAutoZoom == rhs.mAutoZoom &&
+        mFirstClickInteract == rhs.mFirstClickInteract &&
+        mWidthPixels == rhs.mWidthPixels &&
+        mHeightPixels == rhs.mHeightPixels &&
+
+        // "security" fields
+        mWhiteListEnable == rhs.mWhiteListEnable &&
+        mWhiteList == rhs.mWhiteList &&
+
+        // "permissions" fields
+        mPermsInteract == rhs.mPermsInteract &&
+        mPermsControl == rhs.mPermsControl
+
+        );
+}
+ 
+bool LLMediaEntry::operator!=(const LLMediaEntry &rhs) const
+{
+    return (
+        // "general" fields
+        mAltImageEnable != rhs.mAltImageEnable ||
+        mControls != rhs.mControls ||
+        mCurrentURL != rhs.mCurrentURL ||
+        mHomeURL != rhs.mHomeURL ||
+        mAutoLoop != rhs.mAutoLoop ||
+        mAutoPlay != rhs.mAutoPlay ||
+        mAutoScale != rhs.mAutoScale ||
+        mAutoZoom != rhs.mAutoZoom ||
+        mFirstClickInteract != rhs.mFirstClickInteract ||
+        mWidthPixels != rhs.mWidthPixels ||
+        mHeightPixels != rhs.mHeightPixels ||
+
+        // "security" fields
+        mWhiteListEnable != rhs.mWhiteListEnable ||
+        mWhiteList != rhs.mWhiteList ||
+
+        // "permissions" fields
+        mPermsInteract != rhs.mPermsInteract ||
+        mPermsControl != rhs.mPermsControl 
+        
+        );
+}
+
+U32 LLMediaEntry::setWhiteList( const std::vector<std::string> &whitelist )
+{
+    // *NOTE: This code is VERY similar to the setWhitelist below.
+    // IF YOU CHANGE THIS IMPLEMENTATION, BE SURE TO CHANGE THE OTHER!
+    U32 size = 0;
+    U32 count = 0;
+    // First count to make sure the size constraint is not violated
+    std::vector<std::string>::const_iterator iter = whitelist.begin();
+    std::vector<std::string>::const_iterator end = whitelist.end();
+    for ( ; iter < end; ++iter) 
+    {
+        const std::string &entry = (*iter);
+        size += entry.length() + 1; // Include one for \0
+        count ++;
+        if (size > MAX_WHITELIST_SIZE || count > MAX_WHITELIST_COUNT) 
+		{
+            return LSL_STATUS_BOUNDS_ERROR;
+        }
+    }
+    // Next clear the vector
+    mWhiteList.clear();
+    // Then re-iterate and copy entries
+    iter = whitelist.begin();
+    for ( ; iter < end; ++iter)
+    {
+        const std::string &entry = (*iter);
+        mWhiteList.push_back(entry);
+    }
+    return LSL_STATUS_OK;
+}
+
+U32 LLMediaEntry::setWhiteList( const LLSD &whitelist )
+{
+    // If whitelist is undef, this is a no-op.
+    if (whitelist.isUndefined()) return LSL_STATUS_OK;
+
+    // However, if the whitelist is an empty array, erase it.
+    if (whitelist.isArray()) 
+    {
+        // *NOTE: This code is VERY similar to the setWhitelist above.
+        // IF YOU CHANGE THIS IMPLEMENTATION, BE SURE TO CHANGE THE OTHER!
+        U32 size = 0;
+        U32 count = 0;
+        // First check to make sure the size and count constraints are not violated
+        LLSD::array_const_iterator iter = whitelist.beginArray();
+        LLSD::array_const_iterator end = whitelist.endArray();
+        for ( ; iter < end; ++iter) 
+        {
+            const std::string &entry = (*iter).asString();
+            size += entry.length() + 1; // Include one for \0
+            count ++;
+            if (size > MAX_WHITELIST_SIZE || count > MAX_WHITELIST_COUNT) 
+			{
+                return LSL_STATUS_BOUNDS_ERROR;
+            }
+        }
+        // Next clear the vector
+        mWhiteList.clear();
+        // Then re-iterate and copy entries
+        iter = whitelist.beginArray();
+        for ( ; iter < end; ++iter)
+        {
+            const std::string &entry = (*iter).asString();
+            mWhiteList.push_back(entry);
+        }
+        return LSL_STATUS_OK;
+    }
+    else 
+	{
+        return LSL_STATUS_MALFORMED_PARAMS;
+    }
+}
+
+
+static void prefix_with(std::string &str, const char *chars, const char *prefix)
+{
+    // Given string 'str', prefix all instances of any character in 'chars'
+    // with 'prefix'
+    size_t found = str.find_first_of(chars);
+    size_t prefix_len = strlen(prefix);
+    while (found != std::string::npos)
+    {
+        str.insert(found, prefix, prefix_len);
+        found = str.find_first_of(chars, found+prefix_len+1);
+    }
+}
+
+static bool pattern_match(const std::string &candidate_str, const std::string &pattern)
+{
+    // If the pattern is empty, it matches
+    if (pattern.empty()) return true;
+    
+    // 'pattern' is a glob pattern, we only accept '*' chars
+    // copy it
+    std::string expression = pattern;
+    
+    // Escape perl's regexp chars with a backslash, except all "*" chars
+    prefix_with(expression, ".[{()\\+?|^$", "\\");
+    prefix_with(expression, "*", ".");
+                    
+    // case-insensitive matching:
+    boost::regex regexp(expression, boost::regex::perl|boost::regex::icase);
+    return boost::regex_match(candidate_str, regexp);
+}
+
+bool LLMediaEntry::checkCandidateUrl(const std::string& url) const
+{
+    if (getWhiteListEnable()) 
+    {
+        return checkUrlAgainstWhitelist(url, getWhiteList());
+    }
+    else 
+	{
+        return true;
+    }
+}
+
+// static
+bool LLMediaEntry::checkUrlAgainstWhitelist(const std::string& url, 
+                                            const std::vector<std::string> &whitelist)
+{
+    bool passes = true;
+    // *NOTE: no entries?  Don't check
+    if (whitelist.size() > 0) 
+    {
+        passes = false;
+            
+        // Case insensitive: the reason why we toUpper both this and the
+        // filter
+        std::string candidate_url = url;
+        // Use lluri to see if there is a path part in the candidate URL.  No path?  Assume "/"
+        LLURI candidate_uri(candidate_url);
+        std::vector<std::string>::const_iterator iter = whitelist.begin();
+        std::vector<std::string>::const_iterator end = whitelist.end();
+        for ( ; iter < end; ++iter )
+        {
+            std::string filter = *iter;
+                
+            LLURI filter_uri(filter);
+            bool scheme_passes = pattern_match( candidate_uri.scheme(), filter_uri.scheme() );
+            if (filter_uri.scheme().empty()) 
+            {
+                filter_uri = LLURI(DEFAULT_URL_PREFIX + filter);
+            }
+            bool authority_passes = pattern_match( candidate_uri.authority(), filter_uri.authority() );
+            bool path_passes = pattern_match( candidate_uri.escapedPath(), filter_uri.escapedPath() );
+
+            if (scheme_passes && authority_passes && path_passes)
+            {
+                passes = true;
+                break;
+            }
+        }
+    }
+    return passes;
+}
+
+U32 LLMediaEntry::setStringFieldWithLimit( std::string &field, const std::string &value, U32 limit )
+{
+    if ( value.length() > limit ) 
+	{
+        return LSL_STATUS_BOUNDS_ERROR;
+    }
+    else 
+	{
+        field = value;
+        return LSL_STATUS_OK;
+    }
+}
+
+U32 LLMediaEntry::setControls(LLMediaEntry::MediaControls controls)
+{
+    if (controls == STANDARD ||
+        controls == MINI)
+    {
+        mControls = controls;
+        return LSL_STATUS_OK;
+    }
+    return LSL_STATUS_BOUNDS_ERROR;
+}
+
+U32 LLMediaEntry::setPermsInteract( U8 val )
+{
+    mPermsInteract = val & PERM_MASK;
+    return LSL_STATUS_OK;
+}
+
+U32 LLMediaEntry::setPermsControl( U8 val )
+{
+    mPermsControl = val & PERM_MASK;
+    return LSL_STATUS_OK;
+}
+
+U32 LLMediaEntry::setCurrentURL(const std::string& current_url)
+{
+    return setCurrentURLInternal( current_url, true );
+}
+
+U32 LLMediaEntry::setCurrentURLInternal(const std::string& current_url, bool check_whitelist)
+{
+    if ( ! check_whitelist || checkCandidateUrl(current_url)) 
+    {
+        return setStringFieldWithLimit( mCurrentURL, current_url, MAX_URL_LENGTH );
+    }
+    else 
+	{
+        return LSL_STATUS_WHITELIST_FAILED;
+    }
+}
+
+U32 LLMediaEntry::setHomeURL(const std::string& home_url)
+{
+    return setStringFieldWithLimit( mHomeURL, home_url, MAX_URL_LENGTH );
+}
+
+U32 LLMediaEntry::setWidthPixels(U16 width)
+{
+    if (width > MAX_WIDTH_PIXELS) return LSL_STATUS_BOUNDS_ERROR;
+    mWidthPixels = width;
+    return LSL_STATUS_OK; 
+}
+
+U32 LLMediaEntry::setHeightPixels(U16 height)
+{
+    if (height > MAX_HEIGHT_PIXELS) return LSL_STATUS_BOUNDS_ERROR;
+    mHeightPixels = height;
+    return LSL_STATUS_OK; 
+}
+
+const LLUUID &LLMediaEntry::getMediaID() const
+{
+    // Lazily generate media ID
+    if (NULL == mMediaIDp)
+    {
+        mMediaIDp = new LLUUID();
+        mMediaIDp->generate();
+    }
+    return *mMediaIDp;
+}
+

indra/llprimitive/llmediaentry.h

+/** 
+ * @file llmediaentry.h
+ * @brief This is a single instance of media data related to the face of a prim
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ * 
+ * Copyright (c) 2001-2009, 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://secondlifegrid.net/programs/open_source/licensing/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
+ * 
+ * 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$
+ */
+
+#ifndef LL_LLMEDIAENTRY_H
+#define LL_LLMEDIAENTRY_H
+
+#include "llsd.h"
+#include "llstring.h"
+
+// For return values of set*
+#include "lllslconstants.h"
+
+class LLMediaEntry
+{
+public: 
+    enum MediaControls {
+        STANDARD = 0,
+        MINI
+    };
+    
+    // Constructors
+    LLMediaEntry();
+    LLMediaEntry(const LLMediaEntry &rhs);
+
+    LLMediaEntry &operator=(const LLMediaEntry &rhs);
+    virtual ~LLMediaEntry();
+
+    bool operator==(const LLMediaEntry &rhs) const;
+    bool operator!=(const LLMediaEntry &rhs) const;
+    
+    // Render as LLSD
+    LLSD asLLSD() const;
+    void asLLSD(LLSD& sd) const;
+    operator LLSD() const { return asLLSD(); }
+    // Returns false iff the given LLSD contains fields that violate any bounds
+    // limits.
+    static bool checkLLSD(const LLSD& sd);
+    // This doesn't merge, it overwrites the data, so will use
+    // LLSD defaults if need be.  Note: does not check limits!
+    // Use checkLLSD() above first to ensure the LLSD is valid.
+    void fromLLSD(const LLSD& sd);  
+    // This merges data from the incoming LLSD into our fields.
+    // Note that it also does NOT check limits!  Use checkLLSD() above first.
+    void mergeFromLLSD(const LLSD& sd);
+
+    // "general" fields
+    bool getAltImageEnable() const { return mAltImageEnable; }
+    MediaControls getControls() const { return mControls; }
+    std::string getCurrentURL() const { return mCurrentURL; }
+    std::string getHomeURL() const { return mHomeURL; }
+    bool getAutoLoop() const { return mAutoLoop; }
+    bool getAutoPlay() const { return mAutoPlay; }
+    bool getAutoScale() const { return mAutoScale; }
+    bool getAutoZoom() const { return mAutoZoom; }
+    bool getFirstClickInteract() const { return mFirstClickInteract; }
+    U16 getWidthPixels() const { return mWidthPixels; }
+    U16 getHeightPixels() const { return mHeightPixels; }
+
+    // "security" fields
+    bool getWhiteListEnable() const { return mWhiteListEnable; }
+    const std::vector<std::string> &getWhiteList() const { return mWhiteList; }
+
+    // "permissions" fields
+    U8 getPermsInteract() const { return mPermsInteract; }
+    U8 getPermsControl() const { return mPermsControl; }
+
+    // Setters.  Those that return a U32 return a status error code
+    // See lllslconstants.h
+    
+    // "general" fields
+    U32 setAltImageEnable(bool alt_image_enable) { mAltImageEnable = alt_image_enable; return LSL_STATUS_OK; }
+    U32 setControls(MediaControls controls);
+    U32 setCurrentURL(const std::string& current_url);
+    U32 setHomeURL(const std::string& home_url);
+    U32 setAutoLoop(bool auto_loop) { mAutoLoop = auto_loop; return LSL_STATUS_OK; }
+    U32 setAutoPlay(bool auto_play) { mAutoPlay = auto_play; return LSL_STATUS_OK; }
+    U32 setAutoScale(bool auto_scale) { mAutoScale = auto_scale; return LSL_STATUS_OK; }
+    U32 setAutoZoom(bool auto_zoom) { mAutoZoom = auto_zoom; return LSL_STATUS_OK; }
+    U32 setFirstClickInteract(bool first_click) { mFirstClickInteract = first_click; return LSL_STATUS_OK; }
+    U32 setWidthPixels(U16 width);
+    U32 setHeightPixels(U16 height);
+
+    // "security" fields
+    U32 setWhiteListEnable( bool whitelist_enable ) { mWhiteListEnable = whitelist_enable; return LSL_STATUS_OK; }
+    U32 setWhiteList( const std::vector<std::string> &whitelist );
+    U32 setWhiteList( const LLSD &whitelist );  // takes an LLSD array
+
+    // "permissions" fields
+    U32 setPermsInteract( U8 val );
+    U32 setPermsControl( U8 val );
+
+    const LLUUID& getMediaID() const;
+
+    // Helper function to check a candidate URL against the whitelist
+    // Returns true iff candidate URL passes (or if there is no whitelist), false otherwise
+    bool checkCandidateUrl(const std::string& url) const;
+
+public:
+    // Static function to check a URL against a whitelist
+    // Returns true iff url passes the given whitelist
+    static bool checkUrlAgainstWhitelist(const std::string &url, 
+                                         const std::vector<std::string> &whitelist);
+    
+public:
+        // LLSD key defines
+    // "general" fields
+    static const char*  ALT_IMAGE_ENABLE_KEY;
+    static const char*  CONTROLS_KEY;
+    static const char*  CURRENT_URL_KEY;
+    static const char*  HOME_URL_KEY;
+    static const char*  AUTO_LOOP_KEY;
+    static const char*  AUTO_PLAY_KEY;
+    static const char*  AUTO_SCALE_KEY;
+    static const char*  AUTO_ZOOM_KEY;
+    static const char*  FIRST_CLICK_INTERACT_KEY;
+    static const char*  WIDTH_PIXELS_KEY;
+    static const char*  HEIGHT_PIXELS_KEY;
+
+    // "security" fields
+    static const char*  WHITELIST_ENABLE_KEY;
+    static const char*  WHITELIST_KEY;
+
+    // "permissions" fields
+    static const char*  PERMS_INTERACT_KEY;
+    static const char*  PERMS_CONTROL_KEY;
+
+    // Field enumerations & constants
+
+    // *NOTE: DO NOT change the order of these, and do not insert values
+    // in the middle!
+    // Add values to the end, and make sure to change PARAM_MAX_ID!
+    enum Fields {
+         ALT_IMAGE_ENABLE_ID = 0,
+         CONTROLS_ID = 1,
+         CURRENT_URL_ID = 2,
+         HOME_URL_ID = 3,
+         AUTO_LOOP_ID = 4,
+         AUTO_PLAY_ID = 5,
+         AUTO_SCALE_ID = 6,
+         AUTO_ZOOM_ID = 7,
+         FIRST_CLICK_INTERACT_ID = 8,
+         WIDTH_PIXELS_ID = 9,
+         HEIGHT_PIXELS_ID = 10,
+         WHITELIST_ENABLE_ID = 11,
+         WHITELIST_ID = 12,
+         PERMS_INTERACT_ID = 13,
+         PERMS_CONTROL_ID = 14,
+         PARAM_MAX_ID = PERMS_CONTROL_ID
+    };
+
+    // "permissions" values
+    // (e.g. (PERM_OWNER | PERM_GROUP) sets permissions on for OWNER and GROUP
+    static const U8    PERM_NONE             = 0x0;
+    static const U8    PERM_OWNER            = 0x1;
+    static const U8    PERM_GROUP            = 0x2;
+    static const U8    PERM_ANYONE           = 0x4;
+    static const U8    PERM_ALL              = PERM_OWNER|PERM_GROUP|PERM_ANYONE;
+    static const U8    PERM_MASK             = PERM_OWNER|PERM_GROUP|PERM_ANYONE;
+
+    // Limits (in bytes)
+    static const U32   MAX_URL_LENGTH        = 1024;
+    static const U32   MAX_WHITELIST_SIZE    = 1024;
+    static const U32   MAX_WHITELIST_COUNT   = 64;
+    static const U16   MAX_WIDTH_PIXELS      = 2048;
+    static const U16   MAX_HEIGHT_PIXELS     = 2048;
+
+private:
+
+    U32 setStringFieldWithLimit( std::string &field, const std::string &value, U32 limit );
+    U32 setCurrentURLInternal( const std::string &url, bool check_whitelist);
+    bool fromLLSDInternal(const LLSD &sd, bool overwrite);
+
+private:
+     // "general" fields
+    bool mAltImageEnable;
+    MediaControls mControls;
+    std::string mCurrentURL;
+    std::string mHomeURL;
+    bool mAutoLoop;
+    bool mAutoPlay;
+    bool mAutoScale;
+    bool mAutoZoom;
+    bool mFirstClickInteract;
+    U16 mWidthPixels;
+    U16 mHeightPixels;
+
+    // "security" fields
+    bool mWhiteListEnable;
+    std::vector<std::string> mWhiteList;
+
+    // "permissions" fields
+    U8 mPermsInteract;
+    U8 mPermsControl;
+    
+    mutable LLUUID *mMediaIDp;            // temporary id assigned to media on the viewer
+};
+
+#endif
+

indra/llprimitive/llprimitive.cpp

 		color.mV[VALPHA]	= F32(255 - coloru.mV[VALPHA]) / 255.f;
 
 		retval |= setTEColor(i, color);
+
 	}
 
 	return retval;

indra/llprimitive/lltextureentry.cpp

 
 #include "linden_common.h"
 
+#include "lluuid.h"
+#include "llmediaentry.h"
 #include "lltextureentry.h"
 #include "llsdutil.h"
+#include "v4color.h"
 
 const U8 DEFAULT_BUMP_CODE = 0;  // no bump or shininess
 
 const LLTextureEntry LLTextureEntry::null;
 
+// Some LLSD keys.  Do not change these!
+#define OBJECT_ID_KEY_STR "object_id"
+#define TEXTURE_INDEX_KEY_STR "texture_index"
+#define OBJECT_MEDIA_VERSION_KEY_STR "object_media_version"
+#define OBJECT_MEDIA_DATA_KEY_STR "object_media_data"
+#define TEXTURE_MEDIA_DATA_KEY_STR "media_data"
+
+/*static*/ const char* LLTextureEntry::OBJECT_ID_KEY = OBJECT_ID_KEY_STR;
+/*static*/ const char* LLTextureEntry::OBJECT_MEDIA_DATA_KEY = OBJECT_MEDIA_DATA_KEY_STR;
+/*static*/ const char* LLTextureEntry::MEDIA_VERSION_KEY = OBJECT_MEDIA_VERSION_KEY_STR;
+/*static*/ const char* LLTextureEntry::TEXTURE_INDEX_KEY = TEXTURE_INDEX_KEY_STR;
+/*static*/ const char* LLTextureEntry::TEXTURE_MEDIA_DATA_KEY = TEXTURE_MEDIA_DATA_KEY_STR;
+
+static const std::string MEDIA_VERSION_STRING_PREFIX = "x-mv:";
+
 // static 
 LLTextureEntry* LLTextureEntry::newTextureEntry()
 {
 
 //===============================================================
 LLTextureEntry::LLTextureEntry()
+  : mMediaEntry(NULL)
 {
 	init(LLUUID::null,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE);
 }
 
 LLTextureEntry::LLTextureEntry(const LLUUID& tex_id)
+  : mMediaEntry(NULL)
 {
 	init(tex_id,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE);
 }
 
 LLTextureEntry::LLTextureEntry(const LLTextureEntry &rhs)
+  : mMediaEntry(NULL)
 {
 	mID = rhs.mID;
 	mScaleS = rhs.mScaleS;
 	mBump = rhs.mBump;
 	mMediaFlags = rhs.mMediaFlags;
 	mGlow = rhs.mGlow;
+	if (rhs.mMediaEntry != NULL) {
+		// Make a copy
+		mMediaEntry = new LLMediaEntry(*rhs.mMediaEntry);
+	}
 }
 
 LLTextureEntry &LLTextureEntry::operator=(const LLTextureEntry &rhs)
 		mBump = rhs.mBump;
 		mMediaFlags = rhs.mMediaFlags;
 		mGlow = rhs.mGlow;
+		if (mMediaEntry != NULL) {
+			delete mMediaEntry;
+		}
+		if (rhs.mMediaEntry != NULL) {
+			// Make a copy
+			mMediaEntry = new LLMediaEntry(*rhs.mMediaEntry);
+		}
+		else {
+			mMediaEntry = NULL;
+		}
 	}
 
 	return *this;
     mGlow = 0;
 	
 	setColor(LLColor4(1.f, 1.f, 1.f, 1.f));
+	if (mMediaEntry != NULL) {
+	    delete mMediaEntry;
+	}
+	mMediaEntry = NULL;
 }
 
 LLTextureEntry::~LLTextureEntry()
 {
+	if(mMediaEntry)
+	{
+		delete mMediaEntry;
+		mMediaEntry = NULL;
+	}
 }
 
 bool LLTextureEntry::operator!=(const LLTextureEntry &rhs) const
 	sd["bump"] = getBumpShiny();
 	sd["fullbright"] = getFullbright();
 	sd["media_flags"] = mMediaFlags;
+	if (hasMedia()) {
+		LLSD mediaData;
+        if (NULL != getMediaData()) {
+            getMediaData()->asLLSD(mediaData);
+        }
+		sd[TEXTURE_MEDIA_DATA_KEY] = mediaData;
+	}
 	sd["glow"] = mGlow;
 }
 
-bool LLTextureEntry::fromLLSD(LLSD& sd)
+bool LLTextureEntry::fromLLSD(const LLSD& sd)
 {
 	const char *w, *x;
 	w = "imageid";
 	{
 		setMediaTexGen( sd[w].asInteger() );
 	} else goto fail;
+	// If the "has media" flag doesn't match the fact that 
+	// media data exists, updateMediaData will "fix" it
+	// by either clearing or setting the flag
+	w = TEXTURE_MEDIA_DATA_KEY;
+	if (hasMedia() != sd.has(w))
+	{
+		llwarns << "LLTextureEntry::fromLLSD: media_flags (" << hasMedia() <<
+			") does not match presence of media_data (" << sd.has(w) << ").  Fixing." << llendl;
+	}
+	updateMediaData(sd[w]);
+
 	w = "glow";
 	if (sd.has(w))
 	{
 	if (mMediaFlags != media)
 	{
 		mMediaFlags = media;
-		return TEM_CHANGE_TEXTURE;
+
+		// Special code for media handling
+		if( hasMedia() && mMediaEntry == NULL)
+		{
+			mMediaEntry = new LLMediaEntry;
+		}
+        else if ( ! hasMedia() && mMediaEntry != NULL)
+        {
+            delete mMediaEntry;
+            mMediaEntry = NULL;
+        }
+
+		return TEM_CHANGE_MEDIA;
 	}
 	return TEM_CHANGE_NONE;
 }
 	{
 		mMediaFlags &= ~TEM_MEDIA_MASK;
 		mMediaFlags |= media_flags;
-		return TEM_CHANGE_TEXTURE;
+        
+		// Special code for media handling
+		if( hasMedia() && mMediaEntry == NULL)
+		{
+			mMediaEntry = new LLMediaEntry;
+		}
+        else if ( ! hasMedia() && mMediaEntry != NULL)
+        {
+            delete mMediaEntry;
+            mMediaEntry = NULL;
+        }
+        
+		return TEM_CHANGE_MEDIA;
 	}
 	return TEM_CHANGE_NONE;
 }
 	}
 	return TEM_CHANGE_NONE;
 }
+
+void LLTextureEntry::setMediaData(const LLMediaEntry &media_entry)
+{
+    mMediaFlags |= MF_HAS_MEDIA;
+    if (NULL != mMediaEntry)
+    {
+        delete mMediaEntry;
+    }
+    mMediaEntry = new LLMediaEntry(media_entry);
+}
+
+bool LLTextureEntry::updateMediaData(const LLSD& media_data)
+{
+	if (media_data.isUndefined())
+	{
+		// clear the media data
+        clearMediaData();
+		return false;
+	}
+	else {
+		mMediaFlags |= MF_HAS_MEDIA;
+		if (mMediaEntry == NULL)
+		{
+			mMediaEntry = new LLMediaEntry;
+		}
+        // *NOTE: this will *clobber* all of the fields in mMediaEntry 
+        // with whatever fields are present (or not present) in media_data!
+ 		mMediaEntry->fromLLSD(media_data);
+		return true;
+	}
+}
+
+void LLTextureEntry::clearMediaData()
+{
+    mMediaFlags &= ~MF_HAS_MEDIA;
+    if (mMediaEntry != NULL) {
+        delete mMediaEntry;
+    }
+    mMediaEntry = NULL;
+}    
+
+void LLTextureEntry::mergeIntoMediaData(const LLSD& media_fields)
+{
+    mMediaFlags |= MF_HAS_MEDIA;
+    if (mMediaEntry == NULL)
+    {
+        mMediaEntry = new LLMediaEntry;
+    }
+    // *NOTE: this will *merge* the data in media_fields
+    // with the data in our media entry
+    mMediaEntry->mergeFromLLSD(media_fields);
+}
+
+//static
+std::string LLTextureEntry::touchMediaVersionString(const std::string &in_version, const LLUUID &agent_id)
+{
+    // XXX TODO: make media version string binary (base64-encoded?)
+    // Media "URL" is a representation of a version and the last-touched agent
+    // x-mv:nnnnn/agent-id
+    // where "nnnnn" is version number
+    // *NOTE: not the most efficient code in the world...
+    U32 current_version = getVersionFromMediaVersionString(in_version) + 1;
+    const size_t MAX_VERSION_LEN = 10; // 2^32 fits in 10 decimal digits
+    char buf[MAX_VERSION_LEN+1];
+    snprintf(buf, (int)MAX_VERSION_LEN+1, "%0*u", (int)MAX_VERSION_LEN, current_version);  // added int cast to fix warning/breakage on mac.
+    return MEDIA_VERSION_STRING_PREFIX + buf + "/" + agent_id.asString();
+}
+
+//static
+U32 LLTextureEntry::getVersionFromMediaVersionString(const std::string &version_string)
+{
+    U32 version = 0;
+    if (!version_string.empty()) 
+    {
+        size_t found = version_string.find(MEDIA_VERSION_STRING_PREFIX);
+        if (found != std::string::npos) 
+        {
+            found = version_string.find_first_of("/", found);
+            std::string v = version_string.substr(MEDIA_VERSION_STRING_PREFIX.length(), found);
+            version = strtoul(v.c_str(),NULL,10);
+        }
+    }
+    return version;
+}
+
+//static
+LLUUID LLTextureEntry::getAgentIDFromMediaVersionString(const std::string &version_string)
+{
+    LLUUID id;
+    if (!version_string.empty()) 
+    {
+        size_t found = version_string.find(MEDIA_VERSION_STRING_PREFIX);
+        if (found != std::string::npos) 
+        {
+            found = version_string.find_first_of("/", found);
+            if (found != std::string::npos) 
+            {
+                std::string v = version_string.substr(found + 1);
+                id.set(v);
+            }
+        }
+    }
+    return id;
+}

indra/llprimitive/lltextureentry.h

 #include "v4color.h"
 #include "llsd.h"
 
+// These bits are used while unpacking TEM messages to tell which aspects of
+// the texture entry changed.
 const S32 TEM_CHANGE_NONE = 0x0;
 const S32 TEM_CHANGE_COLOR = 0x1;
 const S32 TEM_CHANGE_TEXTURE = 0x2;
-const S32 TEM_INVALID = 0x4;
+const S32 TEM_CHANGE_MEDIA = 0x4;
+const S32 TEM_INVALID = 0x8;
 
 const S32 TEM_BUMPMAP_COUNT = 32;
 
 const S32 TEM_TEX_GEN_MASK		= 0x06;
 const S32 TEM_TEX_GEN_SHIFT		= 1;
 
+// forward declarations
+class LLMediaEntry;
 
 class LLTextureEntry
 {
 	LLSD asLLSD() const;
 	void asLLSD(LLSD& sd) const;
 	operator LLSD() const { return asLLSD(); }
-	bool fromLLSD(LLSD& sd);
+	bool fromLLSD(const LLSD& sd);
 
 	virtual LLTextureEntry* newBlank() const;
 	virtual LLTextureEntry* newCopy() const;
 	U8	 getTexGen() const	{ return mMediaFlags & TEM_TEX_GEN_MASK; }
 	U8	 getMediaTexGen() const { return mMediaFlags; }
     F32  getGlow() const { return mGlow; }
-	
+
+    // *NOTE: it is possible for hasMedia() to return true, but getMediaData() to return NULL.
+    // CONVERSELY, it is also possible for hasMedia() to return false, but getMediaData()
+    // to NOT return NULL.  
+	bool hasMedia() const { return (bool)(mMediaFlags & MF_HAS_MEDIA); } 
+	LLMediaEntry* getMediaData() const { return mMediaEntry; }
+
+    // Completely change the media data on this texture entry.
+    void setMediaData(const LLMediaEntry &media_entry);
+	// Returns true if media data was updated, false if it was cleared
+	bool updateMediaData(const LLSD& media_data);
+    // Clears media data, and sets the media flags bit to 0
+    void clearMediaData();
+    // Merges the given LLSD of media fields with this media entry.
+    // Only those fields that are set that match the keys in
+    // LLMediaEntry will be affected.  If no fields are set or if
+    // the LLSD is undefined, this is a no-op.
+    void mergeIntoMediaData(const LLSD& media_fields);
+
+    // Takes a media version string (an empty string or a previously-returned string)
+    // and returns a "touched" string, touched by agent_id
+    static std::string touchMediaVersionString(const std::string &in_version, const LLUUID &agent_id);
+    // Given a media version string, return the version
+    static U32 getVersionFromMediaVersionString(const std::string &version_string);
+    // Given a media version string, return the UUID of the agent
+    static LLUUID getAgentIDFromMediaVersionString(const std::string &version_string);
+
 	// Media flags
-	enum { MF_NONE = 0x0, MF_WEB_PAGE = 0x1 };
+	enum { MF_NONE = 0x0, MF_HAS_MEDIA = 0x1 };
 
 public:
 	F32                 mScaleS;                // S, T offset
 	F32                 mRotation;              // anti-clockwise rotation in rad about the bottom left corner
 
 	static const LLTextureEntry null;
+
+	// LLSD key defines
+	static const char* OBJECT_ID_KEY;
+	static const char* OBJECT_MEDIA_DATA_KEY;
+    static const char* MEDIA_VERSION_KEY;
+	static const char* TEXTURE_INDEX_KEY;
+	static const char* TEXTURE_MEDIA_DATA_KEY;
+
 protected:
 	LLUUID				mID;					// Texture GUID
 	LLColor4			mColor;
 	U8					mMediaFlags;			// replace with web page, movie, etc.
 	F32                 mGlow;
 
+	// Note the media data is not sent via the same message structure as the rest of the TE
+	LLMediaEntry*		mMediaEntry;			// The media data for the face
+
 	// NOTE: when adding new data to this class, in addition to adding it to the serializers asLLSD/fromLLSD and the
 	// message packers (e.g. LLPrimitive::packTEMessage) you must also implement its copy in LLPrimitive::copyTEs()
 

indra/llprimitive/tests/llmediaentry_test.cpp

+/** 
+ * @file llmediaentry_test.cpp
+ * @brief llmediaentry unit tests
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$                               
+ * Copyright (c) 2001-2009, Linden Research, Inc.                               
+ * $/LicenseInfo$                                                               
+ */
+
+#include "linden_common.h"
+#include "lltut.h"
+#include "boost/lexical_cast.hpp"
+#include "llstring.h"
+#include "llsdutil.h"
+#include "llsdserialize.h"
+
+#include "../llmediaentry.h"
+#include "lllslconstants.h"
+
+#define DEFAULT_MEDIA_ENTRY "<llsd>\n\
+ <map>\n\
+ <key>alt_image_enable</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_loop</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_play</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_scale</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_zoom</key>\n\
+ <boolean>0</boolean>\n\
+ <key>controls</key>\n\
+ <integer>0</integer>\n\
+ <key>current_url</key>\n\
+ <string />\n\
+ <key>first_click_interact</key>\n\
+ <boolean>0</boolean>\n\
+ <key>height_pixels</key>\n\
+ <integer>0</integer>\n\
+ <key>home_url</key>\n\
+ <string />\n\
+ <key>perms_control</key>\n\
+ <integer>7</integer>\n\
+ <key>perms_interact</key>\n\
+ <integer>7</integer>\n\
+ <key>whitelist_enable</key>\n\
+ <boolean>0</boolean>\n\
+ <key>width_pixels</key>\n\
+ <integer>0</integer>\n\
+ </map>\n\
+ </llsd>"
+
+#define EMPTY_MEDIA_ENTRY "<llsd>\n\
+ <map>\n\
+ <key>alt_image_enable</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_loop</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_play</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_scale</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_zoom</key>\n\
+ <boolean>0</boolean>\n\
+ <key>controls</key>\n\
+ <integer>0</integer>\n\
+ <key>current_url</key>\n\
+ <string />\n\
+ <key>first_click_interact</key>\n\
+ <boolean>0</boolean>\n\
+ <key>height_pixels</key>\n\
+ <integer>0</integer>\n\
+ <key>home_url</key>\n\
+ <string />\n\
+ <key>perms_control</key>\n\
+ <integer>0</integer>\n\
+ <key>perms_interact</key>\n\
+ <integer>0</integer>\n\
+ <key>whitelist_enable</key>\n\
+ <boolean>0</boolean>\n\
+ <key>width_pixels</key>\n\
+ <integer>0</integer>\n\
+ </map>\n\
+ </llsd>"
+
+#define PARTIAL_MEDIA_ENTRY(CURRENT_URL) "<llsd>\n\
+ <map>\n\
+ <key>alt_image_enable</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_loop</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_play</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_scale</key>\n\
+ <boolean>0</boolean>\n\
+ <key>auto_zoom</key>\n\
+ <boolean>0</boolean>\n\
+ <key>controls</key>\n\
+ <integer>0</integer>\n\
+ <key>current_url</key>\n\
+ <string>" CURRENT_URL "</string>\n\
+ <key>first_click_interact</key>\n\
+ <boolean>0</boolean>\n\
+ <key>height_pixels</key>\n\
+ <integer>0</integer>\n\
+ <key>home_url</key>\n\
+ <string />\n\
+ <key>perms_control</key>\n\
+ <integer>0</integer>\n\
+ <key>perms_interact</key>\n\
+ <integer>0</integer>\n\
+ <key>whitelist_enable</key>\n\
+ <boolean>0</boolean>\n\
+ <key>width_pixels</key>\n\
+ <integer>0</integer>\n\
+ </map>\n\
+ </llsd>"
+
+namespace tut
+{
+    // this is fixture data that gets created before each test and destroyed
+    // after each test.  this is where we put all of the setup/takedown code
+    // and data needed for each test.
+    struct MediaEntry_test
+    {
+        MediaEntry_test() {
+            emptyMediaEntryStr = EMPTY_MEDIA_ENTRY;
+            std::istringstream e(EMPTY_MEDIA_ENTRY);
+            LLSDSerialize::fromXML(emptyMediaEntryLLSD, e);
+            defaultMediaEntryStr = DEFAULT_MEDIA_ENTRY;
+            std::istringstream d(DEFAULT_MEDIA_ENTRY);
+            LLSDSerialize::fromXML(defaultMediaEntryLLSD, d);
+        }     
+        std::string emptyMediaEntryStr;
+        LLSD emptyMediaEntryLLSD;
+        std::string defaultMediaEntryStr;
+        LLSD defaultMediaEntryLLSD;
+    };
+
+    typedef test_group<MediaEntry_test, 55> factory;
+    typedef factory::object object;
+}
+        
+
+namespace
+{
+    // this is for naming our tests to make pretty output
+    tut::factory tf("MediaEntry Test");
+}
+
+namespace tut
+{
+    bool llsd_equals(const LLSD& a, const LLSD& b) {
+        // cheesy, brute force, but it works
+        return std::string(ll_pretty_print_sd(a)) == std::string(ll_pretty_print_sd(b));
+    }
+
+    void ensure_llsd_equals(const std::string& msg, const LLSD& expected, const LLSD& actual)
+    {
+        if (! llsd_equals(expected, actual))
+        {
+            std::string message = msg;
+            message += ": actual: ";
+            message += ll_pretty_print_sd(actual);
+            message += "\n  expected: ";
+            message += ll_pretty_print_sd(expected);
+            message += "\n";
+            ensure(message, false);
+        }
+    }
+
+    void ensure_string_equals(const std::string& msg, const std::string& expected, const std::string& actual)
+    {
+        if ( expected != actual )
+        {
+            std::string message = msg;
+            message += ": actual: ";
+            message += actual;
+            message += "\n  expected: ";
+            message += expected;
+            message += "\n";
+            ensure(message, false);
+        }
+    }
+
+    void set_whitelist(LLMediaEntry &entry, const char *str)
+    {
+        std::vector<std::string> tokens;
+        LLStringUtil::getTokens(std::string(str), tokens, ",");
+        entry.setWhiteList(tokens);
+    }
+
+    void whitelist_test(bool enable, const char *whitelist, const char *candidate_url, bool expected_pass)
+    {
+        std::string message = "Whitelist test";
+        LLMediaEntry entry;
+        entry.setWhiteListEnable(enable);
+        set_whitelist(entry, whitelist);
+        bool passed_whitelist = entry.checkCandidateUrl(