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); + } + + +} |