diff options
| -rw-r--r-- | indra/llmessage/tests/llcurl_stub.cpp | 1 | ||||
| -rw-r--r-- | indra/llplugin/llpluginprocessparent.cpp | 5 | ||||
| -rw-r--r-- | indra/newview/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 11 | ||||
| -rw-r--r-- | indra/newview/llmediadataclient.cpp | 162 | ||||
| -rw-r--r-- | indra/newview/llmediadataclient.h | 88 | ||||
| -rw-r--r-- | indra/newview/llpanelmediasettingsgeneral.cpp | 3 | ||||
| -rw-r--r-- | indra/newview/llviewermedia.cpp | 34 | ||||
| -rw-r--r-- | indra/newview/llviewermedia.h | 1 | ||||
| -rw-r--r-- | indra/newview/llviewermediafocus.cpp | 6 | ||||
| -rw-r--r-- | indra/newview/llviewerparcelmedia.cpp | 6 | ||||
| -rw-r--r-- | indra/newview/llvovolume.cpp | 154 | ||||
| -rw-r--r-- | indra/newview/llvovolume.h | 14 | ||||
| -rw-r--r-- | indra/newview/tests/llmediadataclient_test.cpp | 483 | 
14 files changed, 842 insertions, 127 deletions
| diff --git a/indra/llmessage/tests/llcurl_stub.cpp b/indra/llmessage/tests/llcurl_stub.cpp index 5dc5932fde..e6a5ad9946 100644 --- a/indra/llmessage/tests/llcurl_stub.cpp +++ b/indra/llmessage/tests/llcurl_stub.cpp @@ -22,6 +22,7 @@  #include "linden_common.h"  LLCurl::Responder::Responder() +	: mReferenceCount(0)  {  } diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index c925d4b760..f3b4c6bdc6 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -493,6 +493,9 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message)  			{  				// Plugin has been loaded.  +				mPluginVersionString = message.getValue("plugin_version"); +				LL_INFOS("Plugin") << "plugin version string: " << mPluginVersionString << LL_ENDL; +  				// Check which message classes/versions the plugin supports.  				// TODO: check against current versions  				// TODO: kill plugin on major mismatches? @@ -503,8 +506,6 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message)  					LL_INFOS("Plugin") << "message class: " << iter->first << " -> version: " << iter->second.asString() << LL_ENDL;  				} -				mPluginVersionString = message.getValue("plugin_version"); -				  				// Send initial sleep time  				setSleepTime(mSleepTime, true);			 diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index eb2c7688f9..0bfb0023c2 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1603,6 +1603,7 @@ include(LLAddBuildTest)  SET(viewer_TEST_SOURCE_FILES    llagentaccess.cpp    lldateutil.cpp +  llmediadataclient.cpp    llviewerhelputil.cpp    lllogininstance.cpp    ) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2f907f97f2..ecad5dfe41 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5294,6 +5294,17 @@        <key>Value</key>  	  <integer>13</integer>      </map> +    <key>PrimMediaMaxRetries</key> +    <map> +      <key>Comment</key> +      <string>Maximum number of retries for media queries.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>U32</string> +      <key>Value</key> +      <integer>4</integer> +    </map>      <key>PrimMediaRequestQueueDelay</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp index 2f2be0561d..3a1b47554c 100644 --- a/indra/newview/llmediadataclient.cpp +++ b/indra/newview/llmediadataclient.cpp @@ -46,7 +46,6 @@  #include "llmediaentry.h"  #include "lltextureentry.h"  #include "llviewerregion.h" -#include "llvovolume.h"  //  // When making a request @@ -59,7 +58,9 @@  // - Any request that gets a 503 still goes through the retry logic  // -// Some helpful logging macros +const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s) +const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 5.0; // secs +const U32 LLMediaDataClient::MAX_RETRIES = 4;  //////////////////////////////////////////////////////////////////////////////////////  // @@ -70,7 +71,7 @@  LLMediaDataClient::Request::Request(const std::string &cap_name,   									const LLSD& sd_payload, -									LLVOVolume *obj,  +									LLMediaDataClientObject *obj,   									LLMediaDataClient *mdc)  	: mCapName(cap_name),   	  mPayload(sd_payload),  @@ -83,6 +84,7 @@ LLMediaDataClient::Request::Request(const std::string &cap_name,  LLMediaDataClient::Request::~Request()  { +	LL_DEBUGS("LLMediaDataClient") << "~Request" << (*this) << LL_ENDL;  	mMDC = NULL;  	mObject = NULL;  } @@ -90,7 +92,7 @@ LLMediaDataClient::Request::~Request()  std::string LLMediaDataClient::Request::getCapability() const  { -	return getObject()->getRegion()->getCapability(getCapName()); +	return getObject()->getCapabilityUrl(getCapName());  }  // Helper function to get the "type" of request, which just pokes around to @@ -142,6 +144,17 @@ void LLMediaDataClient::Request::reEnqueue() const  	mMDC->enqueue(this);  } +F32 LLMediaDataClient::Request::getRetryTimerDelay() const +{ +	return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY : +		mMDC->mRetryTimerDelay;  +} + +U32 LLMediaDataClient::Request::getMaxNumRetries() const +{ +	return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries; +} +  std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r)  {  	s << "<request>"  @@ -168,6 +181,7 @@ LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr)  // virtual   LLMediaDataClient::Responder::RetryTimer::~RetryTimer()   { +	LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL;  	mResponder = NULL;  } @@ -195,28 +209,31 @@ LLMediaDataClient::Responder::Responder(const request_ptr_t &request)  LLMediaDataClient::Responder::~Responder()  { +	LL_DEBUGS("LLMediaDataClient") << "~Responder" << *(getRequest()) << LL_ENDL;  	mRequest = NULL;  }  /*virtual*/  void LLMediaDataClient::Responder::error(U32 status, const std::string& reason)  { -	extern LLControlGroup gSavedSettings; -  	if (status == HTTP_SERVICE_UNAVAILABLE)  	{ -		F32 retry_timeout = gSavedSettings.getF32("PrimMediaRetryTimerDelay"); -		if (retry_timeout <= 0.0) -		{ -			retry_timeout = (F32)UNAVAILABLE_RETRY_TIMER_DELAY; -		} -		LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; +		F32 retry_timeout = mRequest->getRetryTimerDelay();  		mRequest->incRetryCount(); -		// Start timer (instances are automagically tracked by -		// InstanceTracker<> and LLEventTimer) -		new RetryTimer(F32(retry_timeout/*secs*/), this); +		if (mRequest->getRetryCount() < mRequest->getMaxNumRetries())  +		{ +			LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; + +			// Start timer (instances are automagically tracked by +			// InstanceTracker<> and LLEventTimer) +			new RetryTimer(F32(retry_timeout/*secs*/), this); +		} +		else { +			LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retry count " <<  +				mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL; +		}  	}  	else {  		std::string msg = boost::lexical_cast<std::string>(status) + ": " + reason; @@ -257,29 +274,25 @@ bool LLMediaDataClient::Comparator::operator() (const request_ptr_t &o1, const r  	// Calculate the scores for each.    	F64 o1_score = Comparator::getObjectScore(o1->getObject());  	F64 o2_score = Comparator::getObjectScore(o2->getObject()); -		 -	return ( o1_score > o2_score ); + +    // XXX Weird: a higher score should go earlier, but by observation I notice +    // that this causes further-away objects load first.  This is counterintuitive +    // to the priority_queue Comparator, which states that this function should +    // return 'true' if o1 should be *before* o2. +    // In other words, I'd have expected that the following should return +    // ( o1_score > o2_score). +	return ( o1_score < o2_score );  }  // static -F64 LLMediaDataClient::Comparator::getObjectScore(const ll_vo_volume_ptr_t &obj) +F64 LLMediaDataClient::Comparator::getObjectScore(const LLMediaDataClientObject::ptr_t &obj)  {  	// *TODO: make this less expensive? -	F32 dist = obj->getRenderPosition().length() + 0.1;	 // avoids div by 0 +	F64 dist = obj->getDistanceFromAvatar() + 0.1;	 // avoids div by 0  	// square the distance so that they are in the same "unit magnitude" as  	// the interest (which is an area)   	dist *= dist; -	F64 interest = (F64)1; -	int i = 0; -	int end = obj->getNumTEs(); -	for ( ; i < end; ++i) -	{ -		const viewer_media_t &impl = obj->getMediaImpl(i); -		if (!impl.isNull()) -		{ -			interest += impl->getInterest(); -		} -	} +	F64 interest = obj->getTotalMediaInterest() + 1.0;  	return interest/dist;	     } @@ -287,7 +300,7 @@ F64 LLMediaDataClient::Comparator::getObjectScore(const ll_vo_volume_ptr_t &obj)  //////////////////////////////////////////////////////////////////////////////////////  //  // LLMediaDataClient::PriorityQueue -// Queue of LLVOVolume smart pointers to request media for. +// Queue of LLMediaDataClientObject smart pointers to request media for.  //  ////////////////////////////////////////////////////////////////////////////////////// @@ -309,7 +322,7 @@ std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::PriorityQueue  //////////////////////////////////////////////////////////////////////////////////////  //  // LLMediaDataClient::QueueTimer -// Queue of LLVOVolume smart pointers to request media for. +// Queue of LLMediaDataClientObject smart pointers to request media for.  //  ////////////////////////////////////////////////////////////////////////////////////// @@ -321,6 +334,7 @@ LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc)  LLMediaDataClient::QueueTimer::~QueueTimer()  { +	LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL;  	mMDC->setIsRunning(false);  	mMDC = NULL;  } @@ -348,10 +362,10 @@ BOOL LLMediaDataClient::QueueTimer::tick()  	// Peel one off of the items from the queue, and execute request  	request_ptr_t request = queue.top();  	llassert(!request.isNull()); -	const ll_vo_volume_ptr_t &object = request->getObject(); +	const LLMediaDataClientObject *object = request->getObject();  	bool performed_request = false; -	llassert(!object.isNull()); -	if (!object.isNull() && object->hasMedia()) +	llassert(NULL != object); +	if (NULL != object && object->hasMedia())  	{  		std::string url = request->getCapability();  		if (!url.empty()) @@ -373,12 +387,13 @@ BOOL LLMediaDataClient::QueueTimer::tick()  			LL_INFOS("LLMediaDataClient") << "Not Sending request for " << *request << " hasMedia() is false!" << LL_ENDL;  		}  	} -	bool exceeded_retries = request->getRetryCount() > LLMediaDataClient::MAX_RETRIES; +	bool exceeded_retries = request->getRetryCount() > mMDC->mMaxNumRetries;  	if (performed_request || exceeded_retries) // Try N times before giving up   	{  		if (exceeded_retries)  		{ -			LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " << LLMediaDataClient::MAX_RETRIES << " tries...popping object id " << object->getID() << LL_ENDL;  +			LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for "  +										  << mMDC->mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL;   			// XXX Should we bring up a warning dialog??  		}  		queue.pop(); @@ -395,15 +410,9 @@ void LLMediaDataClient::startQueueTimer()  {  	if (! mQueueTimerIsRunning)  	{ -		extern LLControlGroup gSavedSettings; -		F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay"); -		if (queue_timer_delay <= 0.0f) -		{ -			queue_timer_delay = (F32)LLMediaDataClient::QUEUE_TIMER_DELAY; -		} -		LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << queue_timer_delay << " seconds)" << LL_ENDL; +		LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL;  		// LLEventTimer automagically takes care of the lifetime of this object -		new QueueTimer(queue_timer_delay, this); +		new QueueTimer(mQueueTimerDelay, this);  	}  } @@ -412,9 +421,9 @@ void LLMediaDataClient::stopQueueTimer()  	mQueueTimerIsRunning = false;  } -void LLMediaDataClient::request(LLVOVolume *object, const LLSD &payload) +void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload)  { -	if (NULL == object || ! object->hasMedia()) return;  +	if (object.isNull() || ! object->hasMedia()) return;   	// Push the object on the priority queue  	enqueue(new Request(getCapabilityName(), payload, object, this)); @@ -437,7 +446,13 @@ void LLMediaDataClient::enqueue(const Request *request)  //  ////////////////////////////////////////////////////////////////////////////////////// -LLMediaDataClient::LLMediaDataClient() +LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay, +									 F32 retry_timer_delay, +									 U32 max_retries) +	: mQueueTimerDelay(queue_timer_delay), +	  mRetryTimerDelay(retry_timer_delay), +	  mMaxNumRetries(max_retries), +	  mQueueTimerIsRunning(false)  {  	pRequestQueue = new PriorityQueue();  } @@ -447,12 +462,17 @@ LLMediaDataClient::~LLMediaDataClient()  	stopQueueTimer();  	// This should clear the queue, and hopefully call all the destructors. -	LL_DEBUGS("LLMediaDataClient") << "destructor: queue: " <<  +	LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " <<   		(pRequestQueue->empty() ? "<empty> " : "<not empty> ") << (*pRequestQueue) << LL_ENDL;  	delete pRequestQueue;  	pRequestQueue = NULL;  } +bool LLMediaDataClient::isEmpty() const +{ +	return (NULL == pRequestQueue) ? true : pRequestQueue->empty(); +} +  //////////////////////////////////////////////////////////////////////////////////////  //  // LLObjectMediaDataClient @@ -470,7 +490,7 @@ const char *LLObjectMediaDataClient::getCapabilityName() const  	return "ObjectMedia";  } -void LLObjectMediaDataClient::fetchMedia(LLVOVolume *object) +void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject *object)  {  	LLSD sd_payload;  	sd_payload["verb"] = "GET"; @@ -478,18 +498,17 @@ void LLObjectMediaDataClient::fetchMedia(LLVOVolume *object)  	request(object, sd_payload);  } -void LLObjectMediaDataClient::updateMedia(LLVOVolume *object) +void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object)  {  	LLSD sd_payload;  	sd_payload["verb"] = "UPDATE";  	sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID();  	LLSD object_media_data; -	for (int i=0; i < object->getNumTEs(); i++) { -		LLTextureEntry *texture_entry = object->getTE(i); -		llassert((texture_entry->getMediaData() != NULL) == texture_entry->hasMedia()); -		const LLSD &media_data =   -			(texture_entry->getMediaData() == NULL) ? LLSD() : texture_entry->getMediaData()->asLLSD(); -		object_media_data.append(media_data); +	int i = 0; +	int end = object->getMediaDataCount(); +	for ( ; i < end ; ++i)  +	{ +		object_media_data.append(object->getMediaDataLLSD(i));  	}  	sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data; @@ -553,7 +572,7 @@ const char *LLObjectMediaNavigateClient::getCapabilityName() const  	return "ObjectMediaNavigate";  } -void LLObjectMediaNavigateClient::navigate(LLVOVolume *object, U8 texture_index, const std::string &url) +void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url)  {  	LLSD sd_payload;  	sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); @@ -573,8 +592,10 @@ void LLObjectMediaNavigateClient::Responder::error(U32 status, const std::string  	}  	else {  		// bounce the face back -		bounceBack();  		LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: http code=" << status << LL_ENDL; +		const LLSD &payload = getRequest()->getPayload(); +		// bounce the face back +		getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]);  	}  } @@ -591,8 +612,9 @@ void LLObjectMediaNavigateClient::Responder::result(const LLSD& content)  		if (ERROR_PERMISSION_DENIED_CODE == error_code)  		{  			LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL; +			const LLSD &payload = getRequest()->getPayload();  			// bounce the face back -			bounceBack(); +			getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]);  		}  		else {  			LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" <<  @@ -605,23 +627,3 @@ void LLObjectMediaNavigateClient::Responder::result(const LLSD& content)  		LLMediaDataClient::Responder::result(content);  	}  } - - -void LLObjectMediaNavigateClient::Responder::bounceBack() -{ -	const LLSD &payload = getRequest()->getPayload(); -	U8 texture_index = (U8)(LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]; -	viewer_media_t impl = getRequest()->getObject()->getMediaImpl(texture_index); -	// Find the media entry for this navigate -	LLMediaEntry* mep = NULL; -	LLTextureEntry *te = getRequest()->getObject()->getTE(texture_index); -	if(te) -	{ -		mep = te->getMediaData(); -	} -	 -	if (mep && impl) -	{ -//		  impl->navigateTo(mep->getCurrentURL(), "", false, true); -	} -} diff --git a/indra/newview/llmediadataclient.h b/indra/newview/llmediadataclient.h index 59c4334251..9d0aa0981e 100644 --- a/indra/newview/llmediadataclient.h +++ b/indra/newview/llmediadataclient.h @@ -36,12 +36,36 @@  #include "llhttpclient.h"  #include <queue>  #include "llrefcount.h" +#include "llpointer.h"  #include "lltimer.h" -// Forward decls -class LLVOVolume; -typedef LLPointer<LLVOVolume> ll_vo_volume_ptr_t; +// Link seam for LLVOVolume +class LLMediaDataClientObject : public LLRefCount +{ +public: +	// Get the number of media data items +	virtual U8 getMediaDataCount() const = 0; +	// Get the media data at index, as an LLSD +	virtual LLSD getMediaDataLLSD(U8 index) const = 0; +	// Get this object's UUID +	virtual LLUUID getID() const = 0; +	// Navigate back to previous URL +	virtual void mediaNavigateBounceBack(U8 index) = 0; +	// Does this object have media? +	virtual bool hasMedia() const = 0; +	// Update the object's media data to the given array +	virtual void updateObjectMediaData(LLSD const &media_data_array) = 0; +	// Return the distance from the object to the avatar +	virtual F64 getDistanceFromAvatar() const = 0; +	// Return the total "interest" of the media (on-screen area) +	virtual F64 getTotalMediaInterest() const = 0; +	// Return the given cap url +	virtual std::string getCapabilityUrl(const std::string &name) const = 0; + +	// smart pointer +	typedef LLPointer<LLMediaDataClientObject> ptr_t; +};  // This object creates a priority queue for requests.  // Abstracts the Cap URL, the request, and the responder @@ -50,15 +74,23 @@ class LLMediaDataClient : public LLRefCount  public:      LOG_CLASS(LLMediaDataClient); -    const static int QUEUE_TIMER_DELAY = 1; // seconds(s) -	const static int MAX_RETRIES = 4; +    const static F32 QUEUE_TIMER_DELAY;// = 1.0; // seconds(s) +	const static F32 UNAVAILABLE_RETRY_TIMER_DELAY;// = 5.0; // secs +	const static U32 MAX_RETRIES;// = 4;  	// Constructor -	LLMediaDataClient(); +	LLMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, +					  F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, +		              U32 max_retries = MAX_RETRIES);  	// Make the request -	void request(LLVOVolume *object, const LLSD &payload); -     +	void request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload); + +	F32 getRetryTimerDelay() const { return mRetryTimerDelay; } +	 +	// Returns true iff the queue is empty +	bool isEmpty() const;  +	  protected:  	// Destructor  	virtual ~LLMediaDataClient(); // use unref @@ -73,10 +105,10 @@ protected:              NAVIGATE          }; -		Request(const std::string &cap_name, const LLSD& sd_payload, LLVOVolume *obj, LLMediaDataClient *mdc); +		Request(const std::string &cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc);  		const std::string &getCapName() const { return mCapName; }  		const LLSD &getPayload() const { return mPayload; } -		LLVOVolume *getObject() const { return mObject; } +		LLMediaDataClientObject *getObject() const { return mObject; }          U32 getNum() const { return mNum; } @@ -92,6 +124,9 @@ protected:  		// Re-enqueue thyself  		void reEnqueue() const; +		F32 getRetryTimerDelay() const; +		U32 getMaxNumRetries() const; +		  	public:  		friend std::ostream& operator<<(std::ostream &s, const Request &q); @@ -101,7 +136,7 @@ protected:  	private:  		std::string mCapName;  		LLSD mPayload; -		ll_vo_volume_ptr_t mObject; +		LLMediaDataClientObject::ptr_t mObject;  		// Simple tracking  		const U32 mNum;  		static U32 sNum; @@ -115,8 +150,6 @@ protected:  	// Responder  	class Responder : public LLHTTPClient::Responder  	{ -		static const int UNAVAILABLE_RETRY_TIMER_DELAY = 5; // secs -  	public:  		Responder(const request_ptr_t &request);  		//If we get back an error (not found, etc...), handle it here @@ -163,9 +196,10 @@ private:  	public:  		bool operator() (const request_ptr_t &o1, const request_ptr_t &o2) const;  	private: -		static F64 getObjectScore(const ll_vo_volume_ptr_t &obj); +		static F64 getObjectScore(const LLMediaDataClientObject::ptr_t &obj);  	}; +    // PriorityQueue  	class PriorityQueue : public std::priority_queue<  		request_ptr_t,   		std::vector<request_ptr_t>,  @@ -193,6 +227,10 @@ private:  	void startQueueTimer();  	void stopQueueTimer();  	void setIsRunning(bool val) { mQueueTimerIsRunning = val; } + +	const F32 mQueueTimerDelay; +	const F32 mRetryTimerDelay; +	const U32 mMaxNumRetries;  	bool mQueueTimerIsRunning; @@ -204,11 +242,15 @@ private:  class LLObjectMediaDataClient : public LLMediaDataClient  {  public: -    LLObjectMediaDataClient() {} +    LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, +							F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, +							U32 max_retries = MAX_RETRIES) +		: LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) +		{}      ~LLObjectMediaDataClient() {} -	void fetchMedia(LLVOVolume *object);  -    void updateMedia(LLVOVolume *object); +	void fetchMedia(LLMediaDataClientObject *object);  +    void updateMedia(LLMediaDataClientObject *object);  protected:  	// Subclasses must override this factory method to return a new responder @@ -230,14 +272,18 @@ protected:  // MediaDataResponder specific for the ObjectMediaNavigate cap  class LLObjectMediaNavigateClient : public LLMediaDataClient  { +public:  	// NOTE: from llmediaservice.h  	static const int ERROR_PERMISSION_DENIED_CODE = 8002; -public: -    LLObjectMediaNavigateClient() {} +    LLObjectMediaNavigateClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, +								F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, +								U32 max_retries = MAX_RETRIES) +		: LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) +		{}      ~LLObjectMediaNavigateClient() {} -    void navigate(LLVOVolume *object, U8 texture_index, const std::string &url); +    void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url);  protected:  	// Subclasses must override this factory method to return a new responder @@ -254,7 +300,7 @@ protected:  		virtual void error(U32 status, const std::string& reason);          virtual void result(const LLSD &content);      private: -        void bounceBack(); +        void mediaNavigateBounceBack();      };  }; diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp index 295415cb2d..a33d9604fe 100644 --- a/indra/newview/llpanelmediasettingsgeneral.cpp +++ b/indra/newview/llpanelmediasettingsgeneral.cpp @@ -385,8 +385,7 @@ void LLPanelMediaSettingsGeneral::getValues( LLSD &fill_me_in )      fill_me_in[LLMediaEntry::AUTO_SCALE_KEY] = mAutoScale->getValue();      fill_me_in[LLMediaEntry::AUTO_ZOOM_KEY] = mAutoZoom->getValue();      fill_me_in[LLMediaEntry::CONTROLS_KEY] = mControls->getCurrentIndex(); -    // XXX Don't send current URL! -    //fill_me_in[LLMediaEntry::CURRENT_URL_KEY] = mCurrentURL->getValue(); +    fill_me_in[LLMediaEntry::CURRENT_URL_KEY] = mCurrentURL->getValue();      fill_me_in[LLMediaEntry::HEIGHT_PIXELS_KEY] = mHeightPixels->getValue();      fill_me_in[LLMediaEntry::HOME_URL_KEY] = mHomeURL->getValue();      fill_me_in[LLMediaEntry::FIRST_CLICK_INTERACT_KEY] = mFirstClick->getValue(); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index bac87b70be..3a503f22a0 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -588,6 +588,7 @@ LLViewerMediaImpl::LLViewerMediaImpl(	  const LLUUID& texture_id,  	mHasFocus(false),  	mPriority(LLPluginClassMedia::PRIORITY_UNLOADED),  	mDoNavigateOnLoad(false), +	mDoNavigateOnLoadRediscoverType(false),  	mDoNavigateOnLoadServerRequest(false),  	mMediaSourceFailedInit(false),  	mIsUpdated(false) @@ -665,7 +666,7 @@ void LLViewerMediaImpl::createMediaSource()  	{  		if(! mMediaURL.empty())  		{ -			navigateTo(mMediaURL, mMimeType, false, mDoNavigateOnLoadServerRequest); +			navigateTo(mMediaURL, mMimeType, mDoNavigateOnLoadRediscoverType, mDoNavigateOnLoadServerRequest);  		}  		else if(! mMimeType.empty())  		{ @@ -1010,14 +1011,7 @@ BOOL LLViewerMediaImpl::handleMouseUp(S32 x, S32 y, MASK mask)  //////////////////////////////////////////////////////////////////////////////////////////  void LLViewerMediaImpl::navigateHome()  { -	mMediaURL = mHomeURL; -	mDoNavigateOnLoad = !mMediaURL.empty(); -	mDoNavigateOnLoadServerRequest = false; -	 -	if(mMediaSource) -	{ -		mMediaSource->loadURI( mHomeURL ); -	} +	navigateTo(mHomeURL, "", true, false);  }  ////////////////////////////////////////////////////////////////////////////////////////// @@ -1032,12 +1026,16 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi  		setNavState(MEDIANAVSTATE_NONE);  	} -	// Always set the current URL. +	// Always set the current URL and MIME type.  	mMediaURL = url; +	mMimeType = mime_type;  	// If the current URL is not null, make the instance do a navigate on load.  	mDoNavigateOnLoad = !mMediaURL.empty(); +	// if mime type discovery was requested, we'll need to do it when the media loads +	mDoNavigateOnLoadRediscoverType = rediscover_type; +	  	// and if this was a server request, the navigate on load will also need to be one.  	mDoNavigateOnLoadServerRequest = server_request; @@ -1048,6 +1046,21 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi  		return;  	} +	 +	// If the caller has specified a non-empty MIME type, look that up in our MIME types list. +	// If we have a plugin for that MIME type, use that instead of attempting auto-discovery. +	// This helps in supporting legacy media content where the server the media resides on returns a bogus MIME type +	// but the parcel owner has correctly set the MIME type in the parcel media settings. +	 +	if(!mMimeType.empty() && (mMimeType != "none/none")) +	{ +		std::string plugin_basename = LLMIMETypes::implType(mMimeType); +		if(!plugin_basename.empty()) +		{ +			// We have a plugin for this mime type +			rediscover_type = false; +		} +	}  	if(rediscover_type)  	{ @@ -1446,7 +1459,6 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla  		case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_COMPLETE:  		{  			LL_DEBUGS("Media") << "MEDIA_EVENT_NAVIGATE_COMPLETE, uri is: " << plugin->getNavigateURI() << LL_ENDL; -			setNavState(MEDIANAVSTATE_NONE);  		}  		break; diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index d534ef97b9..37aabcf2d6 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -278,6 +278,7 @@ public:  	bool mHasFocus;  	LLPluginClassMedia::EPriority mPriority;  	bool mDoNavigateOnLoad; +	bool mDoNavigateOnLoadRediscoverType;  	bool mDoNavigateOnLoadServerRequest;  	bool mMediaSourceFailedInit; diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index f9377ab37b..1b1b7cedb1 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -304,7 +304,11 @@ BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks)  	BOOL retval = FALSE;  	if(mFocus.notNull() && mMediaImpl.notNull() && mMediaImpl->hasMedia())  	{ -		mMediaImpl->getMediaPlugin()->scrollEvent(x, y, clicks); +        // the scrollEvent() API's x and y are not the same as handleScrollWheel's x and y. +        // The latter is the position of the mouse at the time of the event +        // The former is the 'scroll amount' in x and y, respectively. +        // All we have for 'scroll amount' here is 'clicks', and no mask. +		mMediaImpl->getMediaPlugin()->scrollEvent(0, clicks, /*mask*/0);  		retval = TRUE;  	}  	return retval; diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index 6fba909983..9bcdcbf9ad 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -226,11 +226,13 @@ void LLViewerParcelMedia::play(LLParcel* parcel)  				media_height,   				media_auto_scale,  				media_loop); -			sMediaImpl->navigateTo(media_url); +			sMediaImpl->navigateTo(media_url, mime_type, true);  		}  	}  	else  	{ +		LL_DEBUGS("Media") << "new media impl with mime type " << mime_type << ", url " << media_url << LL_ENDL; +  		// There is no media impl, make a new one  		sMediaImpl = LLViewerMedia::newMediaImpl(  			placeholder_texture_id, @@ -238,7 +240,7 @@ void LLViewerParcelMedia::play(LLParcel* parcel)  			media_height,   			media_auto_scale,  			media_loop); -		sMediaImpl->navigateTo(media_url); +		sMediaImpl->navigateTo(media_url, mime_type, true);  	}  	LLFirstUse::useMedia(); diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 428de078de..1704f63376 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -91,6 +91,57 @@ LLPointer<LLObjectMediaNavigateClient> LLVOVolume::sObjectMediaNavigateClient =  static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles");  static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes"); +// Implementation class of LLMediaDataClientObject.  See llmediadataclient.h +class LLMediaDataClientObjectImpl : public LLMediaDataClientObject +{ +public: +	LLMediaDataClientObjectImpl(LLVOVolume *obj) : mObject(obj) {} +	LLMediaDataClientObjectImpl() { mObject = NULL; } +	 +	virtual U8 getMediaDataCount() const  +		{ return mObject->getNumTEs(); } + +	virtual LLSD getMediaDataLLSD(U8 index) const  +		{ +			LLSD result; +			LLTextureEntry *te = mObject->getTE(index);  +			if (NULL != te) +			{ +				llassert((te->getMediaData() != NULL) == te->hasMedia()); +				if (te->getMediaData() != NULL) +				{ +					result = te->getMediaData()->asLLSD(); +				} +			} +			return result; +		} + +	virtual LLUUID getID() const +		{ return mObject->getID(); } + +	virtual void mediaNavigateBounceBack(U8 index) +		{ mObject->mediaNavigateBounceBack(index); } +	 +	virtual bool hasMedia() const +		{ return mObject->hasMedia(); } +	 +	virtual void updateObjectMediaData(LLSD const &data)  +		{ mObject->updateObjectMediaData(data); } + +	virtual F64 getDistanceFromAvatar() const +		{ return mObject->getRenderPosition().length(); } +	 +	virtual F64 getTotalMediaInterest() const  +		{ return mObject->getTotalMediaInterest(); } + +	virtual std::string getCapabilityUrl(const std::string &name) const +		{ return mObject->getRegion()->getCapability(name); } +	 +private: +	LLPointer<LLVOVolume> mObject; +}; + +  LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)  	: LLViewerObject(id, pcode, regionp),  	  mVolumeImpl(NULL) @@ -134,8 +185,12 @@ LLVOVolume::~LLVOVolume()  // static  void LLVOVolume::initClass()  { -    sObjectMediaClient = new LLObjectMediaDataClient(); -    sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(); +	// gSavedSettings better be around +	const F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay"); +	const F32 retry_timer_delay = gSavedSettings.getF32("PrimMediaRetryTimerDelay"); +	const U32 max_retries = gSavedSettings.getU32("PrimMediaMaxRetries"); +    sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries); +    sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay, max_retries);  }  // static @@ -1634,7 +1689,7 @@ bool LLVOVolume::hasMedia() const  void LLVOVolume::requestMediaDataUpdate()  { -    sObjectMediaClient->fetchMedia(this); +    sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this));  }  void LLVOVolume::cleanUpMediaImpls() @@ -1713,6 +1768,72 @@ void LLVOVolume::syncMediaData(S32 texture_index, const LLSD &media_data, bool m  	//	<< ((NULL == te->getMediaData()) ? "NULL MEDIA DATA" : ll_pretty_print_sd(te->getMediaData()->asLLSD())) << llendl;  } +void LLVOVolume::mediaNavigateBounceBack(U8 texture_index) +{ +	// Find the media entry for this navigate +	const LLMediaEntry* mep = NULL; +	viewer_media_t impl = getMediaImpl(texture_index); +	LLTextureEntry *te = getTE(texture_index); +	if(te) +	{ +		mep = te->getMediaData(); +	} +	 +	if (mep && impl) +	{ +        std::string url = mep->getCurrentURL(); +        if (url.empty()) +        { +            url = mep->getHomeURL(); +        } +        if (! url.empty()) +        { +            LL_INFOS("LLMediaDataClient") << "bouncing back to URL: " << url << LL_ENDL; +            impl->navigateTo(url, "", false, true); +        } +    } +} + +bool LLVOVolume::hasNavigatePermission(const LLMediaEntry* media_entry) +{ +    // NOTE: This logic duplicates the logic in the server (in particular, in llmediaservice.cpp). +    if (NULL == media_entry ) return false; // XXX should we assert here? +     +    // The agent has permissions to navigate if: +    // - agent has edit permissions, or +    // - world permissions are on, or +    // - group permissions are on, and agent_id is in the group, or +    // - agent permissions are on, and agent_id is the owner +     +    if (permModify())  +    { +        return true; +    } +     +    U8 media_perms = media_entry->getPermsInteract(); +     +    // World permissions +    if (0 != (media_perms & LLMediaEntry::PERM_ANYONE))  +    { +        return true; +    } +     +    // Group permissions +    else if (0 != (media_perms & LLMediaEntry::PERM_GROUP) && permGroupOwner()) +    { +        return true; +    } +     +    // Owner permissions +    else if (0 != (media_perms & LLMediaEntry::PERM_OWNER) && permYouOwner())  +    { +        return true; +    } +     +    return false; +     +} +  void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event)  {  	switch(event) @@ -1746,6 +1867,10 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin,  						{  							block_navigation = true;  						} +                        if (!block_navigation && !hasNavigatePermission(mep)) +                        { +                            block_navigation = true; +                        }  					}  					else  					{ @@ -1757,15 +1882,14 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin,  						llinfos << "blocking navigate to URI " << new_location << llendl;  						// "bounce back" to the current URL from the media entry -						// NOTE: the only way block_navigation can be true is if we found the media entry, so we're guaranteed here that mep is not NULL. -						impl->navigateTo(mep->getCurrentURL()); +						mediaNavigateBounceBack(face_index);  					}  					else  					{  						llinfos << "broadcasting navigate with URI " << new_location << llendl; -						sObjectMediaNavigateClient->navigate(this, face_index, new_location); +						sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this), face_index, new_location);  					}  				}  				break; @@ -1791,7 +1915,7 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin,  void LLVOVolume::sendMediaDataUpdate()  { -    sObjectMediaClient->updateMedia(this); +    sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this));  }  void LLVOVolume::removeMediaImpl(S32 texture_index) @@ -1884,6 +2008,22 @@ viewer_media_t LLVOVolume::getMediaImpl(U8 face_id) const  	return NULL;  } +F64 LLVOVolume::getTotalMediaInterest() const +{ +	F64 interest = (F64)0.0; +    int i = 0; +	const int end = getNumTEs(); +	for ( ; i < end; ++i) +	{ +		const viewer_media_t &impl = getMediaImpl(i); +		if (!impl.isNull()) +		{ +			interest += impl->getInterest(); +		} +	} +	return interest; +} +  S32 LLVOVolume::getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id)  {  	S32 end = (S32)mMediaImplList.size() ; diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 250c3ed917..90dfa2204b 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -233,7 +233,18 @@ public:  	BOOL canBeFlexible() const;  	BOOL setIsFlexible(BOOL is_flexible); -	void updateObjectMediaData(const LLSD &media_data_duples); +    // Functions that deal with media, or media navigation +     +    // Update this object's media data with the given media data array +    // (typically this is only called upon a response from a server request) +	void updateObjectMediaData(const LLSD &media_data_array); +     +    // Bounce back media at the given index to its current URL (or home URL, if current URL is empty) +	void mediaNavigateBounceBack(U8 texture_index); +     +    // Returns whether or not this object has permission to navigate the given media entry +    bool hasNavigatePermission(const LLMediaEntry* media_entry); +      	void mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event);  	// Sync the given media data with the impl and the given te @@ -244,6 +255,7 @@ public:  	viewer_media_t getMediaImpl(U8 face_id) const;  	S32 getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id); +	F64 getTotalMediaInterest() const;  	bool hasMedia() const; diff --git a/indra/newview/tests/llmediadataclient_test.cpp b/indra/newview/tests/llmediadataclient_test.cpp new file mode 100644 index 0000000000..a884ed0265 --- /dev/null +++ b/indra/newview/tests/llmediadataclient_test.cpp @@ -0,0 +1,483 @@ +/**  + * @file llmediadataclient_test.cpp + * @brief LLMediaDatClient tests + * + * $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 "../llviewerprecompiledheaders.h" +  +#include <iostream> +#include "../test/lltut.h" + +#include "llsdserialize.h" +#include "llsdutil.h" +#include "llerrorcontrol.h" +#include "llhttpstatuscodes.h" + +#include "../llmediadataclient.h" +#include "../llvovolume.h" + +#include "../../llprimitive/llmediaentry.cpp" +#include "../../llprimitive/lltextureentry.cpp" +#include "../../llmessage/tests/llcurl_stub.cpp" + +#include <boost/lexical_cast.hpp> + +#define VALID_OBJECT_ID   "3607d5c4-644b-4a8a-871a-8b78471af2a2" +#define VALID_OBJECT_ID_1 "11111111-1111-1111-1111-111111111111" +#define VALID_OBJECT_ID_2 "22222222-2222-2222-2222-222222222222" +#define VALID_OBJECT_ID_3 "33333333-3333-3333-3333-333333333333" +#define VALID_OBJECT_ID_4 "44444444-4444-4444-4444-444444444444" + +#define FAKE_OBJECT_MEDIA_CAP_URL "foo_ObjectMedia" +#define FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL "foo_ObjectMediaNavigate" +#define FAKE_OBJECT_MEDIA_CAP_URL_503 "foo_ObjectMedia_503" +#define FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR "foo_ObjectMediaNavigate_ERROR" + +#define MEDIA_DATA "\ +<array>														\ +<string>foo</string>										\ +<string>bar</string>										\ +<string>baz</string>										\ +</array>" + +#define _DATA_URLS(ID,DIST,INT,URL1,URL2) "					\ +<llsd>											\ +  <map>											\ +    <key>uuid</key>								\ +    <string>" ID "</string>						\ +    <key>distance</key>											\ +    <real>" DIST "</real>										\ +    <key>interest</key>											\ +    <real>" INT "</real>											\ +    <key>cap_urls</key>											\ +    <map>														\ +      <key>ObjectMedia</key>									\ +      <string>" URL1 "</string>			\ +      <key>ObjectMediaNavigate</key>							\ +      <string>" URL2 "</string>	\ +    </map>														\ +    <key>media_data</key>                                       \ +	" MEDIA_DATA "												\ +  </map>														\ +</llsd>" + +#define _DATA(ID,DIST,INT) _DATA_URLS(ID,DIST,INT,FAKE_OBJECT_MEDIA_CAP_URL,FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL) + +const char *DATA = _DATA(VALID_OBJECT_ID,"1.0","1.0"); +	 +#define STR(I) boost::lexical_cast<std::string>(I) + +#define LOG_TEST(N) LL_DEBUGS("LLMediaDataClient") << "\n" <<			\ +"================================================================================\n" << \ +"===================================== TEST " #N " ===================================\n" << \ +"================================================================================\n" << LL_ENDL; + +LLSD *gPostRecords = NULL; + +// stubs: +void LLHTTPClient::post( +		const std::string& url, +		const LLSD& body, +		LLHTTPClient::ResponderPtr responder, +		const LLSD& headers, +		const F32 timeout) +{ +	LLSD record; +	record["url"] = url; +	record["body"] = body; +	record["headers"] = headers; +	record["timeout"] = timeout; +	gPostRecords->append(record); +	 +	// Magic URL that triggers a 503: +	if ( url == FAKE_OBJECT_MEDIA_CAP_URL_503 ) +	{ +		responder->error(HTTP_SERVICE_UNAVAILABLE, "fake reason"); +	} +	else if (url == FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR)  +	{ +		LLSD result; +		LLSD error; +		error["code"] = LLObjectMediaNavigateClient::ERROR_PERMISSION_DENIED_CODE; +		result["error"] = error; +		responder->result(result); +	} +	else { +		responder->result(LLSD()); +	} +} + +const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; + +class LLMediaDataClientObjectTest : public LLMediaDataClientObject +{ +public: +	LLMediaDataClientObjectTest(const char *data)  +		{ +			std::istringstream d(data); +			LLSDSerialize::fromXML(mRep, d); +			mNumBounceBacks = 0; +             +           // std::cout << ll_pretty_print_sd(mRep) << std::endl; +           // std::cout << "ID: " << getID() << std::endl; +		} +	LLMediaDataClientObjectTest(const LLSD &rep)  +		: mRep(rep), mNumBounceBacks(0) {} +	~LLMediaDataClientObjectTest() +		{ LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClientObjectTest" << LL_ENDL; } +	 +	virtual U8 getMediaDataCount() const  +		{ return mRep["media_data"].size(); } +	virtual LLSD getMediaDataLLSD(U8 index) const +		{ return mRep["media_data"][(LLSD::Integer)index]; } +	virtual LLUUID getID() const  +		{ return mRep["uuid"]; } +	virtual void mediaNavigateBounceBack(U8 index) +		{ +			mNumBounceBacks++; +		} +	 +	virtual bool hasMedia() const +		{ return mRep.has("media_data"); } +	 +	virtual void updateObjectMediaData(LLSD const &media_data_array) +		{ mRep["media_data"] = media_data_array; } +	 +	virtual F64 getDistanceFromAvatar() const +		{ return (LLSD::Real)mRep["distance"]; } +	 +	virtual F64 getTotalMediaInterest() const +		{ return (LLSD::Real)mRep["interest"]; } + +	virtual std::string getCapabilityUrl(const std::string &name) const  +		{ return mRep["cap_urls"][name]; } + +	int getNumBounceBacks() const +		{ return mNumBounceBacks; } +	 +private: +	LLSD mRep; +	int mNumBounceBacks; +}; + + +namespace tut +{ +    struct mediadataclient +    { +		mediadataclient() { +			gPostRecords = &mLLSD; +			 +// 			LLError::setDefaultLevel(LLError::LEVEL_DEBUG); +// 			LLError::setClassLevel("LLMediaDataClient", LLError::LEVEL_DEBUG); +//			LLError::setTagLevel("MediaOnAPrim", LLError::LEVEL_DEBUG); +		} +		LLSD mLLSD; +    }; +     +	typedef test_group<mediadataclient> mediadataclient_t; +	typedef mediadataclient_t::object mediadataclient_object_t; +	tut::mediadataclient_t tut_mediadataclient("mediadataclient"); +     +    void ensure(const std::string &msg, int value, int expected) +    { +        std::string m = msg; +        m += " value: " + STR(value); +        m += ", expected: " + STR(expected); +        ensure(m, value == expected); +    } +     +    void ensure(const std::string &msg, const std::string & value, const std::string & expected) +    { +        std::string m = msg; +        m += " value: " + value; +        m += ", expected: " + expected; +        ensure(m, value == expected); +    } +     +    void ensure(const std::string &msg, const LLUUID & value, const LLUUID & expected) +    { +        std::string m = msg; +        m += " value: " + value.asString(); +        m += ", expected: " + expected.asString(); +        ensure(m, value == expected); +    } +     +    void ensure_llsd(const std::string &msg, const LLSD & value, const char *expected) +    { +        LLSD expected_llsd; +        std::istringstream e(expected); +        LLSDSerialize::fromXML(expected_llsd, e); +    +        std::string value_str = ll_pretty_print_sd(value); +        std::string expected_str = ll_pretty_print_sd(expected_llsd); +        std::string m = msg; +        m += " value: " + value_str; +        m += ", expected: " + expected_str; +        ensure(m, value_str == expected_str); +    } + +	////////////////////////////////////////////////////////////////////////////////////////// +	 +	template<> template<> +	void mediadataclient_object_t::test<1>() +	{ +		// +		// Test fetchMedia() +		// +		LOG_TEST(1); +		 +		LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); +		int num_refs_start = o->getNumRefs(); +		{ +			// queue time w/ no delay ensures that LLEventTimer::updateClass() will hit the tick() +			LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(0,0,4);   +			mdc->fetchMedia(o); + +			// Make sure no posts happened yet... +			ensure("post records", gPostRecords->size(), 0); + +			LLEventTimer::updateClass(); +		 +			ensure("post records", gPostRecords->size(), 1); +			ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_CAP_URL); +			ensure("post GET", (*gPostRecords)[0]["body"]["verb"], "GET"); +			ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); +			ensure("queue empty", mdc->isEmpty()); +		} +		 +		// Make sure everyone's destroyed properly +		ensure("REF COUNT", o->getNumRefs(), num_refs_start); +    } + +	////////////////////////////////////////////////////////////////////////////////////////// + +	template<> template<> +	void mediadataclient_object_t::test<2>() +	{ +		// +		// Test updateMedia() +		// +		LOG_TEST(2); + +		LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); +		{ +			// queue time w/ no delay ensures that LLEventTimer::updateClass() will hit the tick() +			LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(0,0,4);   +			mdc->updateMedia(o); +			ensure("post records", gPostRecords->size(), 0); +			LLEventTimer::updateClass(); +		 +			ensure("post records", gPostRecords->size(), 1); +			ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_CAP_URL); +			ensure("post UPDATE", (*gPostRecords)[0]["body"]["verb"], "UPDATE"); +			ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); +			ensure_llsd("post data llsd", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_MEDIA_DATA_KEY],  +						"<llsd>" MEDIA_DATA "</llsd>"); +			ensure("queue empty", mdc->isEmpty()); +		} + +		ensure("REF COUNT", o->getNumRefs(), 1); +	} + +	////////////////////////////////////////////////////////////////////////////////////////// + +    template<> template<> +    void mediadataclient_object_t::test<3>() +    { +		// +		// Test navigate() +		// +		LOG_TEST(3); + +		LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); +		{		 +			LLPointer<LLObjectMediaNavigateClient> mdc = new LLObjectMediaNavigateClient(0,0,4); +			const char *TEST_URL = "http://example.com"; +			mdc->navigate(o, 0, TEST_URL); +			ensure("post records", gPostRecords->size(), 0); +			LLEventTimer::updateClass(); + +			// ensure no bounce back +			ensure("bounce back", dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o))->getNumBounceBacks(), 0); +		 +			ensure("post records", gPostRecords->size(), 1); +			ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL); +			ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); +			ensure("post data", (*gPostRecords)[0]["body"][LLTextureEntry::TEXTURE_INDEX_KEY], 0); +			ensure("post data", (*gPostRecords)[0]["body"][LLMediaEntry::CURRENT_URL_KEY], TEST_URL); +			ensure("queue empty", mdc->isEmpty()); +		} +		ensure("REF COUNT", o->getNumRefs(), 1); +    } +	 +	////////////////////////////////////////////////////////////////////////////////////////// + +    template<> template<> +    void mediadataclient_object_t::test<4>() +    { +		// +		// Test queue ordering +		// +		LOG_TEST(4); + +		LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest( +			_DATA(VALID_OBJECT_ID_1,"3.0","1.0")); +		LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest( +			_DATA(VALID_OBJECT_ID_2,"1.0","1.0")); +		LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest( +			_DATA(VALID_OBJECT_ID_3,"2.0","1.0")); +		{ +			LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(0,0,4);   +			const char *ORDERED_OBJECT_IDS[] = { VALID_OBJECT_ID_2, VALID_OBJECT_ID_3, VALID_OBJECT_ID_1 }; +			mdc->fetchMedia(o1); +			mdc->fetchMedia(o2); +			mdc->fetchMedia(o3); + +			// Make sure no posts happened yet... +			ensure("post records", gPostRecords->size(), 0); + +			// tick 3 times... +			LLEventTimer::updateClass(); +			ensure("post records", gPostRecords->size(), 1); +			LLEventTimer::updateClass(); +			ensure("post records", gPostRecords->size(), 2); +			LLEventTimer::updateClass(); +			ensure("post records", gPostRecords->size(), 3); +		 +			for( int i=0; i < 3; i++ ) +			{ +				ensure("[" + STR(i) + "] post url", (*gPostRecords)[i]["url"], FAKE_OBJECT_MEDIA_CAP_URL); +				ensure("[" + STR(i) + "] post GET", (*gPostRecords)[i]["body"]["verb"], "GET"); +				ensure("[" + STR(i) + "] post object id", (*gPostRecords)[i]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(),  +					   LLUUID(ORDERED_OBJECT_IDS[i])); +			} + +			ensure("queue empty", mdc->isEmpty()); +		} +		ensure("refcount of o1", o1->getNumRefs(), 1); +		ensure("refcount of o2", o2->getNumRefs(), 1); +		ensure("refcount of o3", o3->getNumRefs(), 1); +    } + +	////////////////////////////////////////////////////////////////////////////////////////// + +	template<> template<> +	void mediadataclient_object_t::test<5>() +	{ +		// +		// Test fetchMedia() getting a 503 error +		// +		LOG_TEST(5); +		 +		LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest( +			_DATA_URLS(VALID_OBJECT_ID, +					   "1.0", +					   "1.0", +					   FAKE_OBJECT_MEDIA_CAP_URL_503, +					   FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL)); +		int num_refs_start = o->getNumRefs(); +		{ +			const int NUM_RETRIES = 5; +			LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(0,0,NUM_RETRIES); + +			// This should generate a retry +			mdc->fetchMedia(o); + +			// Make sure no posts happened yet... +			ensure("post records before", gPostRecords->size(), 0); + +			// Once, causes retry +			// Second, fires retry timer +			// Third, fires queue timer again +			for (int i=0; i<NUM_RETRIES; ++i) +			{ +				LLEventTimer::updateClass(); +				ensure("post records " + STR(i), gPostRecords->size(), i+1); +				LLEventTimer::updateClass(); +			} + +			// Do some extre pumps to make sure no other timer work occurs. +			LLEventTimer::updateClass(); +			LLEventTimer::updateClass(); +			LLEventTimer::updateClass(); +			 +			// Make sure there were 2 posts +			ensure("post records after", gPostRecords->size(), NUM_RETRIES); +			for (int i=0; i<NUM_RETRIES; ++i) +			{ +				ensure("[" + STR(i) + "] post url", (*gPostRecords)[i]["url"], FAKE_OBJECT_MEDIA_CAP_URL_503); +				ensure("[" + STR(i) + "] post GET", (*gPostRecords)[i]["body"]["verb"], "GET"); +				ensure("[" + STR(i) + "] post object id", (*gPostRecords)[i]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); +			} +			ensure("queue empty", mdc->isEmpty()); +		} +		 +		// Make sure everyone's destroyed properly +		ensure("REF COUNT", o->getNumRefs(), num_refs_start); +    } + +    template<> template<> +    void mediadataclient_object_t::test<6>() +    { +		// +		// Test navigate() with a bounce back +		// +		LOG_TEST(6); + +		LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest( +			_DATA_URLS(VALID_OBJECT_ID, +					   "1.0", +					   "1.0", +					   FAKE_OBJECT_MEDIA_CAP_URL, +					   FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR)); +		{		 +			LLPointer<LLObjectMediaNavigateClient> mdc = new LLObjectMediaNavigateClient(0,0,4); +			const char *TEST_URL = "http://example.com"; +			mdc->navigate(o, 0, TEST_URL); +			ensure("post records", gPostRecords->size(), 0); +			LLEventTimer::updateClass(); + +			// ensure bounce back +			ensure("bounce back",  +				   dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o))->getNumBounceBacks(), +				   1); +			 +			ensure("post records", gPostRecords->size(), 1); +			ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR); +			ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); +			ensure("post data", (*gPostRecords)[0]["body"][LLTextureEntry::TEXTURE_INDEX_KEY], 0); +			ensure("post data", (*gPostRecords)[0]["body"][LLMediaEntry::CURRENT_URL_KEY], TEST_URL); +			ensure("queue empty", mdc->isEmpty()); +		} +		ensure("REF COUNT", o->getNumRefs(), 1); +    } + +	 +} | 
