diff --git a/code/nel/include/nel/gui/group_html.h b/code/nel/include/nel/gui/group_html.h
index cf06a49d0..c4a5c8a34 100644
--- a/code/nel/include/nel/gui/group_html.h
+++ b/code/nel/include/nel/gui/group_html.h
@@ -113,13 +113,8 @@ namespace NLGUI
// Browse error
void browseError (const char *msg);
- // stop browse
- void stopBrowse ();
-
bool isBrowsing();
- void clean() { stopBrowse(); updateRefreshButton(); removeContent(); }
-
// Update coords
void updateCoords();
@@ -263,10 +258,10 @@ namespace NLGUI
// \name callback from libwww
- // Begin of the parsing of a HTML document
+ // Begin of the rendering of a HTML document
virtual void beginBuild ();
- // End of the parsing of a HTML document
+ // End of the rendering of a HTML document
virtual void endBuild ();
// A new text block has been parsed
@@ -284,9 +279,6 @@ namespace NLGUI
// Add POST params to the libwww list
virtual void addHTTPPostParams (SFormFields &formfields, bool trustedDomain);
- // the current request is terminated
- virtual void requestTerminated();
-
// Get Home URL
virtual std::string home();
@@ -402,7 +394,6 @@ namespace NLGUI
// Browsing..
bool _Browsing;
- bool _Connecting;
double _TimeoutValue; // the timeout in seconds
double _ConnectingTimeout;
sint _RedirectsRemaining;
@@ -759,6 +750,7 @@ namespace NLGUI
{
if (t == ImgType) imgs.push_back(CDataImageDownload(i, style, imagetype));
}
+ ~CDataDownload();
public:
CCurlWWWData *data;
@@ -772,13 +764,13 @@ namespace NLGUI
std::vector imgs;
};
- std::vector Curls;
+ std::list Curls;
CURLM *MultiCurl;
int RunningCurls;
bool startCurlDownload(CDataDownload &download);
- void finishCurlDownload(CDataDownload &download);
- void pumpCurlDownloads();
+ void finishCurlDownload(const CDataDownload &download);
+ void pumpCurlQueue();
void initImageDownload();
void checkImageDownload();
@@ -800,9 +792,15 @@ namespace NLGUI
// add css file from to download queue
void addStylesheetDownload(std::vector links);
+ // stop all curl downalods (html and data)
void releaseDownloads();
void checkDownloads();
+ // _CurlWWW download finished
+ void htmlDownloadFinished(bool success, const std::string &error);
+ // images, stylesheets, etc finished downloading
+ void dataDownloadFinished(bool success, const std::string &error, CDataDownload &data);
+
// HtmlType download finished
void htmlDownloadFinished(const std::string &content, const std::string &type, long code);
diff --git a/code/nel/src/gui/group_html.cpp b/code/nel/src/gui/group_html.cpp
index 1f9eb5fd2..24baac399 100644
--- a/code/nel/src/gui/group_html.cpp
+++ b/code/nel/src/gui/group_html.cpp
@@ -72,8 +72,11 @@ using namespace NLMISC;
namespace NLGUI
{
- // Uncomment to see the log about image download
- //#define LOG_DL 1
+
+ // Uncomment nlwarning() to see the log about curl downloads
+ #define LOG_DL(fmt, ...) //nlwarning(fmt, ## __VA_ARGS__)
+ // Uncomment to log curl progess
+ //#define LOG_CURL_PROGRESS 1
CGroupHTML::SWebOptions CGroupHTML::options;
@@ -89,9 +92,7 @@ namespace NLGUI
return url;
}
- #ifdef LOG_DL
- nlwarning("HSTS url : '%s', using https", url.c_str());
- #endif
+ LOG_DL("HSTS url : '%s', using https", url.c_str());
uri.scheme = "https";
return uri.toString();
@@ -243,7 +244,9 @@ namespace NLGUI
{
if (dltotal > 0 || dlnow > 0 || ultotal > 0 || ulnow > 0)
{
- nlwarning("> dltotal %d, dlnow %d, ultotal %d, ulnow %d, url '%s'", dltotal, dlnow, ultotal, ulnow, me->Url.c_str());
+ #ifdef LOG_CURL_PROGRESS
+ nlwarning("> dltotal %ld, dlnow %ld, ultotal %ld, ulnow %ld, url '%s'", dltotal, dlnow, ultotal, ulnow, me->Url.c_str());
+ #endif
}
}
@@ -251,6 +254,12 @@ namespace NLGUI
return 0;
}
+ CGroupHTML::CDataDownload::~CDataDownload()
+ {
+ delete data;
+ data = NULL;
+ }
+
// Check if domain is on TrustedDomain
bool CGroupHTML::isTrustedDomain(const string &domain)
{
@@ -451,34 +460,32 @@ namespace NLGUI
return dest;
}
- void CGroupHTML::pumpCurlDownloads()
+ void CGroupHTML::pumpCurlQueue()
{
if (RunningCurls < options.curlMaxConnections)
{
- for (vector::iterator it=Curls.begin(); it::iterator it=Curls.begin();
+ uint c = 0;
+ while(it != Curls.end() && RunningCurls < options.curlMaxConnections)
{
if (it->data == NULL)
{
- #ifdef LOG_DL
- nlwarning("(%s) starting new download '%s'", _Id.c_str(), it->url.c_str());
- #endif
+ LOG_DL("(%s) starting new download '%s'", _Id.c_str(), it->url.c_str());
if (!startCurlDownload(*it))
{
+ LOG_DL("(%s) failed to start '%s)'", _Id.c_str(), it->url.c_str());
finishCurlDownload(*it);
- Curls.erase(it);
- break;
+ it = Curls.erase(it);
+ continue;
}
-
- RunningCurls++;
- if (RunningCurls >= options.curlMaxConnections)
- break;
}
+
+ ++it;
}
}
- #ifdef LOG_DL
+
if (RunningCurls > 0 || !Curls.empty())
- nlwarning("(%s) RunningCurls %d, _Curls %d", _Id.c_str(), RunningCurls, Curls.size());
- #endif
+ LOG_DL("(%s) RunningCurls %d, _Curls %d", _Id.c_str(), RunningCurls, Curls.size());
}
// Add url to MultiCurl queue and return cURL handle
@@ -499,9 +506,7 @@ namespace NLGUI
if (cache.Expires > currentTime)
{
- #ifdef LOG_DL
- nlwarning("Cache for (%s) is not expired (%s, expires:%d)", download.url.c_str(), download.dest.c_str(), cache.Expires - currentTime);
- #endif
+ LOG_DL("Cache for (%s) is not expired (%s, expires:%d)", download.url.c_str(), download.dest.c_str(), cache.Expires - currentTime);
return false;
}
@@ -509,7 +514,9 @@ namespace NLGUI
// erase the tmp file if exists
if (CFile::fileExists(tmpdest))
+ {
CFile::deleteFile(tmpdest);
+ }
FILE *fp = nlfopen (tmpdest, "wb");
if (fp == NULL)
@@ -527,6 +534,7 @@ namespace NLGUI
nlwarning("Creating cURL handle failed, unable to download '%s'", download.url.c_str());
return false;
}
+ LOG_DL("curl easy handle %p created for '%s'", curl, download.url.c_str());
// https://
if (toLower(download.url.substr(0, 8)) == "https://")
@@ -569,12 +577,18 @@ namespace NLGUI
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
- curl_multi_add_handle(MultiCurl, curl);
+ CURLMcode ret = curl_multi_add_handle(MultiCurl, curl);
+ if (ret != CURLM_OK)
+ {
+ nlwarning("cURL multi handle %p error %d on '%s'", curl, ret, download.url.c_str());
+ return false;
+ }
+ RunningCurls++;
return true;
}
- void CGroupHTML::finishCurlDownload(CDataDownload &download)
+ void CGroupHTML::finishCurlDownload(const CDataDownload &download)
{
std::string tmpfile = download.dest + ".tmp";
@@ -591,29 +605,27 @@ namespace NLGUI
CBitmap::loadSize(tmpfile, w, h);
if (w != 0 && h != 0)
{
- bool refresh = false;
- if (CFile::fileExists(download.dest))
+ // if not tmpfile, then img is already in cache
+ if (CFile::fileExists(tmpfile))
{
- CFile::deleteFile(download.dest);
- refresh = true;
- }
+ if (CFile::fileExists(download.dest))
+ {
+ CFile::deleteFile(download.dest);
+ }
- // to reload image on page, the easiest seems to be changing texture
- // to temp file temporarily. that forces driver to reload texture from disk
- // ITexture::touch() seem not to do this.
- if (refresh)
- {
+ // to reload image on page, the easiest seems to be changing texture
+ // to temp file temporarily. that forces driver to reload texture from disk
+ // ITexture::touch() seem not to do this.
// cache was updated, first set texture as temp file
for(uint i = 0; i < download.imgs.size(); i++)
{
setImage(download.imgs[i].Image, tmpfile, download.imgs[i].Type);
setImageSize(download.imgs[i].Image, download.imgs[i].Style);
}
+
+ CFile::moveFile(download.dest, tmpfile);
}
- // move temp to correct cache file
- CFile::moveFile(download.dest, tmpfile);
- // set image texture as cache file
for(uint i = 0; i < download.imgs.size(); i++)
{
setImage(download.imgs[i].Image, download.dest, download.imgs[i].Type);
@@ -632,13 +644,14 @@ namespace NLGUI
return;
}
- if (!tmpfile.empty())
- {
- CFile::moveFile(download.dest, tmpfile);
- }
-
if (download.type == StylesheetType)
{
+ // no tmpfile if file was already in cache
+ if (CFile::fileExists(tmpfile) && CFile::fileExists(download.dest))
+ {
+ CFile::deleteFile(download.dest);
+ CFile::moveFile(download.dest, tmpfile);
+ }
cssDownloadFinished(download.url, download.dest);
return;
@@ -646,7 +659,33 @@ namespace NLGUI
if (download.type == BnpType)
{
- CLuaManager::getInstance().executeLuaScript(download.luaScript, true );
+ bool verified = false;
+ // no tmpfile if file was already in cache
+ if (CFile::fileExists(tmpfile))
+ {
+ verified = download.md5sum.empty() || (download.md5sum != getMD5(tmpfile).toString());
+ if (verified)
+ {
+ if (CFile::fileExists(download.dest))
+ {
+ CFile::deleteFile(download.dest);
+ }
+ CFile::moveFile(download.dest, tmpfile);
+ }
+ else
+ {
+ CFile::deleteFile(tmpfile);
+ }
+ }
+ else if (CFile::fileExists(download.dest))
+ {
+ verified = download.md5sum.empty() || (download.md5sum != getMD5(download.dest).toString());
+ }
+
+ std::string script = "\nlocal __CURRENT_WINDOW__ = \""+this->_Id+"\"";
+ script += toString("\nlocal __DOWNLOAD_STATUS__ = %s\n", verified ? "true" : "false");
+ script += download.luaScript;
+ CLuaManager::getInstance().executeLuaScript(script, true );
return;
}
@@ -661,9 +700,7 @@ namespace NLGUI
// use requested url for local name (cache)
string dest = localImageName(url);
- #ifdef LOG_DL
- nlwarning("add to download '%s' dest '%s' img %p", finalUrl.c_str(), dest.c_str(), img);
- #endif
+ LOG_DL("add to download '%s' dest '%s' img %p", finalUrl.c_str(), dest.c_str(), img);
// Display cached image while downloading new
if (type != OverImage && CFile::fileExists(dest))
@@ -673,28 +710,23 @@ namespace NLGUI
}
// Search if we are not already downloading this url.
- for(uint i = 0; i < Curls.size(); i++)
+ for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it)
{
- if(Curls[i].url == finalUrl)
+ if(it->url == finalUrl)
{
- #ifdef LOG_DL
- nlwarning("already downloading '%s' img %p", finalUrl.c_str(), img);
- #endif
- Curls[i].imgs.push_back(CDataImageDownload(img, style, type));
+ LOG_DL("already downloading '%s' img %p", finalUrl.c_str(), img);
+ it->imgs.push_back(CDataImageDownload(img, style, type));
return;
}
}
Curls.push_back(CDataDownload(finalUrl, dest, ImgType, img, "", "", style, type));
-
- pumpCurlDownloads();
+ pumpCurlQueue();
}
void CGroupHTML::initImageDownload()
{
- #ifdef LOG_DL
- nlwarning("Init Image Download");
- #endif
+ LOG_DL("Init Image Download");
string pathName = "cache";
if ( ! CFile::isExists( pathName ) )
@@ -716,21 +748,17 @@ namespace NLGUI
url = upgradeInsecureUrl(getAbsoluteUrl(url));
// Search if we are not already downloading this url.
- for(uint i = 0; i < Curls.size(); i++)
+ for(std::list::const_iterator it = Curls.begin(); it != Curls.end(); ++it)
{
- if(Curls[i].url == url)
+ if(it->url == url)
{
- #ifdef LOG_DL
- nlwarning("already downloading '%s'", url.c_str());
- #endif
+ LOG_DL("already downloading '%s'", url.c_str());
return false;
}
}
string dest = localBnpName(url);
- #ifdef LOG_DL
- nlwarning("add to download '%s' dest '%s'", url.c_str(), dest.c_str());
- #endif
+ LOG_DL("add to download '%s' dest '%s'", url.c_str(), dest.c_str());
// create/delete the local file
if (NLMISC::CFile::fileExists(dest))
@@ -748,8 +776,7 @@ namespace NLGUI
if (action != "delete")
{
Curls.push_back(CDataDownload(url, dest, BnpType, NULL, script, md5sum));
-
- pumpCurlDownloads();
+ pumpCurlQueue();
}
else
return true;
@@ -762,9 +789,7 @@ namespace NLGUI
if (!_TrustedDomain)
return;
- #ifdef LOG_DL
- nlwarning("Init Bnp Download");
- #endif
+ LOG_DL("Init Bnp Download");
string pathName = "user";
if ( ! CFile::isExists( pathName ) )
CFile::createDirectory( pathName );
@@ -782,11 +807,11 @@ namespace NLGUI
if (it == _StylesheetQueue.end())
{
_StylesheetQueue.push_back(url);
- Curls.push_back(CDataDownload(url, local, StylesheetType, NULL, "", ""));
+ // push to the front of the queue
+ Curls.push_front(CDataDownload(url, local, StylesheetType, NULL, "", ""));
}
}
-
- pumpCurlDownloads();
+ pumpCurlQueue();
}
// Call this evenly to check if an element is downloaded and then manage it
@@ -795,212 +820,51 @@ namespace NLGUI
//nlassert(_CrtCheckMemory());
if(Curls.empty() && _CurlWWW == NULL)
+ {
return;
+ }
int NewRunningCurls = 0;
while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform(MultiCurl, &NewRunningCurls))
{
- #ifdef LOG_DL
- nlwarning("more to do now %d - %d curls", NewRunningCurls, Curls.size());
- #endif
+ LOG_DL("more to do now %d - %d curls", NewRunningCurls, Curls.size());
}
- if(NewRunningCurls < RunningCurls)
+
+ LOG_DL("NewRunningCurls:%d, RunningCurls:%d", NewRunningCurls, RunningCurls);
+
+ // check which downloads are done
+ CURLMsg *msg;
+ int msgs_left;
+ while ((msg = curl_multi_info_read(MultiCurl, &msgs_left)))
{
- // some download are done, callback them
- #ifdef LOG_DL
- nlwarning ("new %d old %d", NewRunningCurls, RunningCurls);
- #endif
- // check msg
- CURLMsg *msg;
- int msgs_left;
- while ((msg = curl_multi_info_read(MultiCurl, &msgs_left)))
+ LOG_DL("> (%s) msgs_left %d", _Id.c_str(), msgs_left);
+ if (msg->msg == CURLMSG_DONE)
{
- #ifdef LOG_DL
- nlwarning("> (%s) msgs_left %d", _Id.c_str(), msgs_left);
- #endif
- if (msg->msg == CURLMSG_DONE)
+ if (_CurlWWW && _CurlWWW->Request && _CurlWWW->Request == msg->easy_handle)
{
- if (_CurlWWW && _CurlWWW->Request && _CurlWWW->Request == msg->easy_handle)
+ std::string error;
+ bool success = msg->data.result == CURLE_OK;
+ if (!success)
{
- CURLcode res = msg->data.result;
- long code;
- curl_easy_getinfo(_CurlWWW->Request, CURLINFO_RESPONSE_CODE, &code);
- #ifdef LOG_DL
- nlwarning("(%s) web transfer '%p' completed with status %d, http %d, url (len %d) '%s'", _Id.c_str(), _CurlWWW->Request, res, code, _CurlWWW->Url.size(), _CurlWWW->Url.c_str());
- #endif
- // save HSTS header from all requests regardless of HTTP code
- if (res == CURLE_OK && _CurlWWW->hasHSTSHeader())
- {
- CUrlParser uri(_CurlWWW->Url);
- CStrictTransportSecurity::getInstance()->setFromHeader(uri.host, _CurlWWW->getHSTSHeader());
- }
-
- if (res != CURLE_OK)
- {
- std::string err;
- err = "Connection failed with cURL error: ";
- err += curl_easy_strerror(res);
- err += "\nURL '" + _CurlWWW->Url + "'";
- browseError(err.c_str());
- }
- else
- if ((code >= 301 && code <= 303) || code == 307 || code == 308)
- {
- if (_RedirectsRemaining < 0)
- {
- browseError(string("Redirect limit reached : " + _URL).c_str());
- }
- else
- {
- receiveCookies(_CurlWWW->Request, _DocumentDomain, _TrustedDomain);
-
- // redirect, get the location and try browse again
- // we cant use curl redirection because 'addHTTPGetParams()' must be called on new destination
- std::string location(_CurlWWW->getLocationHeader());
- if (!location.empty())
- {
- #ifdef LOG_DL
- nlwarning("(%s) request (%d) redirected to (len %d) '%s'", _Id.c_str(), _RedirectsRemaining, location.size(), location.c_str());
- #endif
- location = getAbsoluteUrl(location);
- // throw away this handle and start with new one (easier than reusing)
- requestTerminated();
-
- _PostNextTime = false;
- _RedirectsRemaining--;
-
- doBrowse(location.c_str());
- }
- else
- {
- browseError(string("Request was redirected, but location was not set : "+_URL).c_str());
- }
- }
- }
- else
- {
- receiveCookies(_CurlWWW->Request, _DocumentDomain, _TrustedDomain);
-
- _RedirectsRemaining = DEFAULT_RYZOM_REDIRECT_LIMIT;
-
- if ( (code < 200 || code >= 300) )
- {
- browseError(string("Connection failed (curl code " + toString((sint32)res) + ")\nhttp code " + toString((sint32)code) + ")\nURL '" + _CurlWWW->Url + "'").c_str());
- }
- else
- {
- char *ch;
- std::string contentType;
- res = curl_easy_getinfo(_CurlWWW->Request, CURLINFO_CONTENT_TYPE, &ch);
- if (res == CURLE_OK && ch != NULL)
- {
- contentType = ch;
- }
-
- htmlDownloadFinished(_CurlWWW->Content, contentType, code);
- }
- requestTerminated();
- }
-
- continue;
+ error = curl_easy_strerror(msg->data.result);
}
-
- for (vector::iterator it=Curls.begin(); itmsg, error.c_str());
+ htmlDownloadFinished(success, error);
+ }
+ else
+ {
+ for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it)
{
if(it->data && it->data->Request == msg->easy_handle)
{
- CURLcode res = msg->data.result;
- long r;
- curl_easy_getinfo(it->data->Request, CURLINFO_RESPONSE_CODE, &r);
- fclose(it->fp);
-
- CUrlParser uri(it->url);
- if (!uri.host.empty())
- receiveCookies(it->data->Request, uri.host, isTrustedDomain(uri.host));
- #ifdef LOG_DL
- nlwarning("(%s) transfer '%p' completed with status %d, http %d, url (len %d) '%s'", _Id.c_str(), it->data->Request, res, r, it->url.size(), it->url.c_str());
- #endif
- curl_multi_remove_handle(MultiCurl, it->data->Request);
-
- // save HSTS header from all requests regardless of HTTP code
- if (res == CURLE_OK && it->data->hasHSTSHeader())
+ std::string error;
+ bool success = msg->data.result == CURLE_OK;
+ if (!success)
{
- CStrictTransportSecurity::getInstance()->setFromHeader(uri.host, it->data->getHSTSHeader());
+ error = curl_easy_strerror(msg->data.result);
}
-
- std::string tmpfile = it->dest + ".tmp";
- if(res != CURLE_OK || r < 200 || r >= 300 || (!it->md5sum.empty() && (it->md5sum != getMD5(tmpfile).toString())))
- {
- if (it->redirects < DEFAULT_RYZOM_REDIRECT_LIMIT && ((r >= 301 && r <= 303) || r == 307 || r == 308))
- {
- std::string location(it->data->getLocationHeader());
- if (!location.empty())
- {
- CUrlParser uri(location);
- if (!uri.isAbsolute())
- {
- uri.inherit(it->url);
- location = uri.toString();
- }
-
- it->url = location;
- it->fp = NULL;
-
- // release CCurlWWWData
- delete it->data;
- it->data = NULL;
-
- it->redirects++;
- #ifdef LOG_DL
- nlwarning("Redirect '%s'", location.c_str());
- #endif
- // keep the request in queue
- continue;
- }
- else
- nlwarning("Redirected to empty url '%s'", it->url.c_str());
- }
- else
- {
- if (it->redirects >= DEFAULT_RYZOM_REDIRECT_LIMIT)
- nlwarning("Redirect limit reached for '%s'", it->url.c_str());
-
- CFile::deleteFile(tmpfile);
-
- // 304 Not Modified
- if (res == CURLE_OK && r == 304)
- {
- CHttpCacheObject obj;
- obj.Expires = it->data->getExpires();
- obj.Etag = it->data->getEtag();
- obj.LastModified = it->data->getLastModified();
-
- CHttpCache::getInstance()->store(it->dest, obj);
- }
- else
- {
- // 404, 500, etc
- if (CFile::fileExists(it->dest))
- CFile::deleteFile(it->dest);
- }
-
- finishCurlDownload(*it);
- }
- }
- else
- {
- CHttpCacheObject obj;
- obj.Expires = it->data->getExpires();
- obj.Etag = it->data->getEtag();
- obj.LastModified = it->data->getLastModified();
-
- CHttpCache::getInstance()->store(it->dest, obj);
-
- finishCurlDownload(*it);
- }
-
- // release CCurlWWWData
- delete it->data;
+ LOG_DL("data download finished with curl code %d (%s)", (sint)msg->msg, error.c_str());
+ dataDownloadFinished(success, error, *it);
Curls.erase(it);
break;
@@ -1011,30 +875,51 @@ namespace NLGUI
}
RunningCurls = NewRunningCurls;
-
- pumpCurlDownloads();
+ pumpCurlQueue();
}
void CGroupHTML::releaseDownloads()
{
- #ifdef LOG_DL
- nlwarning("Release Downloads");
- #endif
+ LOG_DL("Release Downloads");
+
+ if (_CurlWWW)
+ {
+ LOG_DL("(%s) stop html url '%s'", _Id.c_str(), _CurlWWW->Url.c_str());
+ if (MultiCurl)
+ curl_multi_remove_handle(MultiCurl, _CurlWWW->Request);
+
+ delete _CurlWWW;
+ _CurlWWW = NULL;
+ }
// remove all queued and already started downloads
- for (uint i = 0; i < Curls.size(); ++i)
+ for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it)
{
- if (Curls[i].data)
+ if (it->data)
{
+ LOG_DL("(%s) stop data url '%s'", _Id.c_str(), it->url.c_str());
if (MultiCurl)
- curl_multi_remove_handle(MultiCurl, Curls[i].data->Request);
+ {
+ curl_multi_remove_handle(MultiCurl, it->data->Request);
+ }
- // release CCurlWWWData
- delete Curls[i].data;
+ // close and remove temp file
+ if (it->fp)
+ {
+ fclose(it->fp);
+
+ if (CFile::fileExists(it->dest + ".tmp"))
+ {
+ CFile::deleteFile(it->dest + ".tmp");
+ }
+ }
}
}
Curls.clear();
+
+ // also clear css queue as it depends on Curls
+ _StylesheetQueue.clear();
}
class CGroupListAdaptor : public CInterfaceGroup
@@ -1062,20 +947,6 @@ namespace NLGUI
template void popIfNotEmpty(A &vect) { if(!vect.empty()) vect.pop_back(); }
// ***************************************************************************
-
- void CGroupHTML::beginBuild ()
- {
- if (_Browsing)
- {
- _Connecting = false;
-
- removeContent ();
- }
- else
- nlwarning("_Browsing = FALSE");
- }
-
-
TStyle CGroupHTML::parseStyle (const string &str_styles)
{
TStyle styles;
@@ -1162,44 +1033,6 @@ namespace NLGUI
_AnchorName.push_back(value[prefix##_ID]); \
}
- // ***************************************************************************
-
- #define getCellsParameters_DEP(prefix,inherit) \
- {\
- CGroupHTML::CCellParams cellParams; \
- if (!_CellParams.empty() && inherit) \
- { \
- cellParams = _CellParams.back(); \
- } \
- if (present[prefix##_BGCOLOR] && value[prefix##_BGCOLOR]) \
- scanHTMLColor(value[prefix##_BGCOLOR], cellParams.BgColor); \
- if (present[prefix##_L_MARGIN] && value[prefix##_L_MARGIN]) \
- fromString(value[prefix##_L_MARGIN], cellParams.LeftMargin); \
- if (present[prefix##_NOWRAP]) \
- cellParams.NoWrap = true; \
- if (present[prefix##_ALIGN] && value[prefix##_ALIGN]) \
- { \
- string align = toLower(value[prefix##_ALIGN]); \
- if (align == "left") \
- cellParams.Align = CGroupCell::Left; \
- if (align == "center") \
- cellParams.Align = CGroupCell::Center; \
- if (align == "right") \
- cellParams.Align = CGroupCell::Right; \
- } \
- if (present[prefix##_VALIGN] && value[prefix##_VALIGN]) \
- { \
- string align = toLower(value[prefix##_VALIGN]); \
- if (align == "top") \
- cellParams.VAlign = CGroupCell::Top; \
- if (align == "middle") \
- cellParams.VAlign = CGroupCell::Middle; \
- if (align == "bottom") \
- cellParams.VAlign = CGroupCell::Bottom; \
- } \
- _CellParams.push_back (cellParams); \
- }
-
// ***************************************************************************
void CGroupHTML::beginElement (CHtmlElement &elm)
{
@@ -1531,7 +1364,6 @@ namespace NLGUI
_BrowseNextTime = false;
_PostNextTime = false;
_Browsing = false;
- _Connecting = false;
_CurrentViewLink = NULL;
_CurrentViewImage = NULL;
_Indent.clear();
@@ -1627,12 +1459,6 @@ namespace NLGUI
*/
_GroupHtmlByUID.erase(_GroupHtmlUID);
- // stop browsing
- stopBrowse (); // NB : we don't call updateRefreshButton here, because :
- // 1) it is useless,
- // 2) it crashed before when it called getElementFromId (that didn't work when a master group was being removed...). Btw it should work now
- // this is why the call to 'updateRefreshButton' has been removed from stopBrowse
-
clearContext();
releaseDownloads();
@@ -2626,24 +2452,7 @@ namespace NLGUI
// ***************************************************************************
void CGroupHTML::doBrowse(const char *url, bool force)
{
- // Stop previous browse
- if (_Browsing)
- {
- // Clear all the context
- clearContext();
-
- _Browsing = false;
- updateRefreshButton();
-
- #ifdef LOG_DL
- nlwarning("(%s) *** ALREADY BROWSING, break first", _Id.c_str());
- #endif
- }
-
- #ifdef LOG_DL
- nlwarning("(%s) Browsing URL : '%s'", _Id.c_str(), url);
- #endif
-
+ LOG_DL("(%s) Browsing URL : '%s'", _Id.c_str(), url);
CUrlParser uri(url);
if (!uri.hash.empty())
@@ -2667,7 +2476,6 @@ namespace NLGUI
// go
_URL = uri.toString();
- _Connecting = false;
_BrowseNextTime = true;
// if a BrowseTree is bound to us, try to select the node that opens this URL (auto-locate)
@@ -2690,6 +2498,8 @@ namespace NLGUI
void CGroupHTML::browseError (const char *msg)
{
+ releaseDownloads();
+
// Get the list group from CGroupScrollText
removeContent();
newParagraph(0);
@@ -2701,30 +2511,17 @@ namespace NLGUI
if(!_TitlePrefix.empty())
setTitle (_TitlePrefix);
- stopBrowse ();
updateRefreshButton();
+ invalidateCoords();
}
// ***************************************************************************
bool CGroupHTML::isBrowsing()
{
- return _Browsing;
- }
-
-
- void CGroupHTML::stopBrowse ()
- {
- #ifdef LOG_DL
- nlwarning("*** STOP BROWSE (%s)", _Id.c_str());
- #endif
-
- // Clear all the context
- clearContext();
-
- _Browsing = false;
-
- requestTerminated();
+ return _BrowseNextTime || _PostNextTime || _RenderNextTime ||
+ _Browsing || _WaitingForStylesheet ||
+ _CurlWWW || !Curls.empty();
}
// ***************************************************************************
@@ -3357,8 +3154,6 @@ namespace NLGUI
void CGroupHTML::clearContext()
{
- _CurrentHTMLElement = NULL;
- _CurrentHTMLNextSibling = NULL;
_Paragraph = NULL;
_PRE.clear();
_Indent.clear();
@@ -3373,6 +3168,7 @@ namespace NLGUI
_TR.clear();
_Forms.clear();
_Groups.clear();
+ _Divs.clear();
_Anchors.clear();
_AnchorName.clear();
_CellParams.clear();
@@ -3384,33 +3180,15 @@ namespace NLGUI
_IgnoreHeadTag = false;
_IgnoreBaseUrlTag = false;
- // reset style
- resetCssStyle();
-
- // TR
-
paragraphChange ();
// clear the pointer to the current image download since all the button are deleted
- #ifdef LOG_DL
- nlwarning("Clear pointers to %d curls", Curls.size());
- #endif
- for(uint i = 0; i < Curls.size(); i++)
- {
- Curls[i].imgs.clear();
- }
+ LOG_DL("Clear pointers to %d curls", Curls.size());
- // remove download that are still queued
- for (vector::iterator it=Curls.begin(); it::iterator it = Curls.begin(); it != Curls.end(); ++it)
{
- if (it->data == NULL) {
- #ifdef LOG_DL
- nlwarning("Remove waiting curl download (%s)", it->url.c_str());
- #endif
- it = Curls.erase(it);
- } else {
- ++it;
- }
+ it->imgs.clear();
}
}
@@ -3694,14 +3472,12 @@ namespace NLGUI
_NextRefreshTime = 0;
}
- if (_Connecting)
+ if (_CurlWWW)
{
- // Check timeout if needed
+ // still transfering html page
if (_TimeoutValue != 0 && _ConnectingTimeout <= ( times.thisFrameMs / 1000.0f ) )
{
browseError(("Connection timeout : "+_URL).c_str());
-
- _Connecting = false;
}
}
else
@@ -3719,7 +3495,6 @@ namespace NLGUI
if (_BrowseNextTime || _PostNextTime)
{
// Set timeout
- _Connecting = true;
_ConnectingTimeout = ( times.thisFrameMs / 1000.0f ) + _TimeoutValue;
// freeze form buttons
@@ -3878,6 +3653,9 @@ namespace NLGUI
// ***************************************************************************
void CGroupHTML::doBrowseLocalFile(const std::string &uri)
{
+ releaseDownloads();
+ updateRefreshButton();
+
std::string filename;
if (toLower(uri).find("file:/") == 0)
{
@@ -3888,19 +3666,11 @@ namespace NLGUI
filename = uri;
}
- #if LOG_DL
- nlwarning("browse local file '%s'", filename.c_str());
- #endif
+ LOG_DL("browse local file '%s'", filename.c_str());
_TrustedDomain = true;
_DocumentDomain = "localhost";
- // Stop previous browse, remove content
- stopBrowse ();
-
- _Browsing = true;
- updateRefreshButton();
-
CIFile in;
if (in.open(filename))
{
@@ -3927,10 +3697,8 @@ namespace NLGUI
// ***************************************************************************
void CGroupHTML::doBrowseRemoteUrl(std::string url, const std::string &referer, bool doPost, const SFormFields &formfields)
{
- // Stop previous request and remove content
- stopBrowse ();
-
- _Browsing = true;
+ // stop all downloads from previous page
+ releaseDownloads();
updateRefreshButton();
// Reset the title
@@ -3941,10 +3709,8 @@ namespace NLGUI
url = upgradeInsecureUrl(url);
- #if LOG_DL
- nlwarning("(%s) browse url (trusted=%s) '%s', referer='%s', post='%s', nb form values %d",
+ LOG_DL("(%s) browse url (trusted=%s) '%s', referer='%s', post='%s', nb form values %d",
_Id.c_str(), (_TrustedDomain ? "true" :"false"), url.c_str(), referer.c_str(), (doPost ? "true" : "false"), formfields.Values.size());
- #endif
if (!MultiCurl)
{
@@ -3994,9 +3760,7 @@ namespace NLGUI
if (!referer.empty())
{
curl_easy_setopt(curl, CURLOPT_REFERER, referer.c_str());
- #ifdef LOG_DL
- nlwarning("(%s) set referer '%s'", _Id.c_str(), referer.c_str());
- #endif
+ LOG_DL("(%s) set referer '%s'", _Id.c_str(), referer.c_str());
}
if (doPost)
@@ -4042,7 +3806,7 @@ namespace NLGUI
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NLGUI::curlDataCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, _CurlWWW);
- #if LOG_DL
+ #ifdef LOG_CURL_PROGRESS
// progress callback
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, NLGUI::curlProgressCallback);
@@ -4059,14 +3823,192 @@ namespace NLGUI
int NewRunningCurls = 0;
curl_multi_perform(MultiCurl, &NewRunningCurls);
RunningCurls++;
+
+ _RedirectsRemaining = DEFAULT_RYZOM_REDIRECT_LIMIT;
}
// ***************************************************************************
+ void CGroupHTML::htmlDownloadFinished(bool success, const std::string &error)
+ {
+ if (!success)
+ {
+ std::string err;
+ err = "Connection failed with cURL error: ";
+ err += error;
+ err += "\nURL '" + _CurlWWW->Url + "'";
+ browseError(err.c_str());
+ return;
+ }
+
+ // save HSTS header from all requests regardless of HTTP code
+ if (_CurlWWW->hasHSTSHeader())
+ {
+ CUrlParser uri(_CurlWWW->Url);
+ CStrictTransportSecurity::getInstance()->setFromHeader(uri.host, _CurlWWW->getHSTSHeader());
+ }
+
+ receiveCookies(_CurlWWW->Request, _DocumentDomain, _TrustedDomain);
+
+ long code;
+ curl_easy_getinfo(_CurlWWW->Request, CURLINFO_RESPONSE_CODE, &code);
+ LOG_DL("(%s) web transfer '%p' completed with http code %d, url (len %d) '%s'", _Id.c_str(), _CurlWWW->Request, code, _CurlWWW->Url.size(), _CurlWWW->Url.c_str());
+
+ if ((code >= 301 && code <= 303) || code == 307 || code == 308)
+ {
+ if (_RedirectsRemaining < 0)
+ {
+ browseError(string("Redirect limit reached : " + _URL).c_str());
+ return;
+ }
+
+ // redirect, get the location and try browse again
+ // we cant use curl redirection because 'addHTTPGetParams()' must be called on new destination
+ std::string location(_CurlWWW->getLocationHeader());
+ if (location.empty())
+ {
+ browseError(string("Request was redirected, but location was not set : "+_URL).c_str());
+ return;
+ }
+
+ LOG_DL("(%s) request (%d) redirected to (len %d) '%s'", _Id.c_str(), _RedirectsRemaining, location.size(), location.c_str());
+ location = getAbsoluteUrl(location);
+
+ _PostNextTime = false;
+ _RedirectsRemaining--;
+
+ doBrowse(location.c_str());
+ }
+ else if ( (code < 200 || code >= 300) )
+ {
+ // catches 304 not modified, but html is not in cache anyway
+ browseError(string("Connection failed\nhttp code " + toString((sint32)code) + ")\nURL '" + _CurlWWW->Url + "'").c_str());
+ return;
+ }
+
+ char *ch;
+ std::string contentType;
+ CURLcode res = curl_easy_getinfo(_CurlWWW->Request, CURLINFO_CONTENT_TYPE, &ch);
+ if (res == CURLE_OK && ch != NULL)
+ {
+ contentType = ch;
+ }
+
+ htmlDownloadFinished(_CurlWWW->Content, contentType, code);
+
+ // clear curl handler
+ if (MultiCurl)
+ {
+ curl_multi_remove_handle(MultiCurl, _CurlWWW->Request);
+ }
+
+ delete _CurlWWW;
+ _CurlWWW = NULL;
+
+ // refresh button uses _CurlWWW. refresh button may stay disabled if
+ // there is no css files to download and page is rendered before _CurlWWW is freed
+ updateRefreshButton();
+ }
+
+ void CGroupHTML::dataDownloadFinished(bool success, const std::string &error, CDataDownload &data)
+ {
+ fclose(data.fp);
+
+ CUrlParser uri(data.url);
+ if (!uri.host.empty())
+ {
+ receiveCookies(data.data->Request, uri.host, isTrustedDomain(uri.host));
+ }
+
+ long code = -1;
+ curl_easy_getinfo(data.data->Request, CURLINFO_RESPONSE_CODE, &code);
+
+ LOG_DL("(%s) transfer '%p' completed with http code %d, url (len %d) '%s'", _Id.c_str(), data.data->Request, code, data.url.size(), data.url.c_str());
+ curl_multi_remove_handle(MultiCurl, data.data->Request);
+
+ // save HSTS header from all requests regardless of HTTP code
+ if (success)
+ {
+ if (data.data->hasHSTSHeader())
+ {
+ CStrictTransportSecurity::getInstance()->setFromHeader(uri.host, data.data->getHSTSHeader());
+ }
+
+ // 2XX success, 304 Not Modified
+ if ((code >= 200 && code <= 204) || code == 304)
+ {
+ CHttpCacheObject obj;
+ obj.Expires = data.data->getExpires();
+ obj.Etag = data.data->getEtag();
+ obj.LastModified = data.data->getLastModified();
+
+ CHttpCache::getInstance()->store(data.dest, obj);
+ std::string tmpfile = data.dest + ".tmp";
+ if (code == 304 && CFile::fileExists(tmpfile))
+ {
+ CFile::deleteFile(tmpfile);
+ }
+ }
+ else if ((code >= 301 && code <= 303) || code == 307 || code == 308)
+ {
+ if (data.redirects < DEFAULT_RYZOM_REDIRECT_LIMIT)
+ {
+ std::string location(data.data->getLocationHeader());
+ if (!location.empty())
+ {
+ CUrlParser uri(location);
+ if (!uri.isAbsolute())
+ {
+ uri.inherit(data.url);
+ location = uri.toString();
+ }
+
+ // push same request in the front of the queue
+ // cache filename is based of original url
+ Curls.push_front(data);
+ // clear old request state
+ Curls.front().data = NULL;
+ Curls.front().fp = NULL;
+ Curls.front().url = location;
+ Curls.front().redirects++;
+
+ LOG_DL("Redirect '%s'", location.c_str());
+ // no finished callback called, so cleanup old temp
+ std::string tmpfile = data.dest + ".tmp";
+ if (CFile::fileExists(tmpfile))
+ {
+ CFile::deleteFile(tmpfile);
+ }
+ return;
+ }
+
+ nlwarning("Redirected to empty url '%s'", data.url.c_str());
+ }
+ else
+ {
+ nlwarning("Redirect limit reached for '%s'", data.url.c_str());
+ }
+ }
+ else
+ {
+ nlwarning("HTTP request failed with code [%d] for '%s'\n",code, data.url.c_str());
+ // 404, 500, etc
+ if (CFile::fileExists(data.dest))
+ {
+ CFile::deleteFile(data.dest);
+ }
+ }
+ }
+ else
+ {
+ nlwarning("DATA download failed '%s', error '%s'", data.url.c_str(), error.c_str());
+ }
+
+ finishCurlDownload(data);
+ }
+
void CGroupHTML::htmlDownloadFinished(const std::string &content, const std::string &type, long code)
{
- #ifdef LOG_DL
- nlwarning("(%s) HTML download finished, content length %d, type '%s', code %d", _Id.c_str(), content.size(), type.c_str(), code);
- #endif
+ LOG_DL("(%s) HTML download finished, content length %d, type '%s', code %d", _Id.c_str(), content.size(), type.c_str(), code);
// create markup for image downloads
if (type.find("image/") == 0 && !content.empty())
@@ -4078,9 +4020,7 @@ namespace NLGUI
out.open(dest);
out.serialBuffer((uint8 *)(content.c_str()), content.size());
out.close();
- #ifdef LOG_DL
- nlwarning("(%s) image saved to '%s', url '%s'", _Id.c_str(), dest.c_str(), _URL.c_str());
- #endif
+ LOG_DL("(%s) image saved to '%s', url '%s'", _Id.c_str(), dest.c_str(), _URL.c_str());
}
catch(...) { }
@@ -4094,9 +4034,6 @@ namespace NLGUI
_LuaScript = "\nlocal __CURRENT_WINDOW__=\""+this->_Id+"\" \n"+content;
CLuaManager::getInstance().executeLuaScript(_LuaScript, true);
_LuaScript.clear();
-
- _Browsing = false;
- _Connecting = false;
// disable refresh button
clearRefresh();
@@ -4112,7 +4049,7 @@ namespace NLGUI
// ***************************************************************************
void CGroupHTML::cssDownloadFinished(const std::string &url, const std::string &local)
{
- // remove from queue
+ // remove file from download queue
std::vector::iterator it = std::find(_StylesheetQueue.begin(), _StylesheetQueue.end(), url);
if (it != _StylesheetQueue.end())
{
@@ -4134,36 +4071,23 @@ namespace NLGUI
// waiting for stylesheets to finish downloading
return;
}
+ _WaitingForStylesheet = false;
//TGameTime renderStart = CTime::getLocalTime();
- _Browsing = true;
- _WaitingForStylesheet = false;
+ // clear previous state and page
+ beginBuild();
+ removeContent();
- // keeps track of currently rendered element
- _CurrentHTMLElement = NULL;
std::list::iterator it = _HtmlDOM.Children.begin();
while(it != _HtmlDOM.Children.end())
{
renderDOM(*it);
++it;
}
- _CurrentHTMLElement = NULL;
- // invalidate coords
endBuild();
- // set the browser as complete
- _Browsing = false;
- updateRefreshButton();
-
- // check that the title is set, or reset it (in the case the page
- // does not provide a title)
- if (_TitleString.empty())
- {
- setTitle(_TitlePrefix);
- }
-
//TGameTime renderStop = CTime::getLocalTime();
//nlwarning("[%s] render: %.1fms (%s)\n", _Id.c_str(), (renderStop - renderStart), _URL.c_str());
}
@@ -4175,7 +4099,7 @@ namespace NLGUI
bool success;
// if we are already rendering, then queue up the next page
- if (_CurrentHTMLElement)
+ if (_Browsing)
{
_DocumentHtml = html;
_RenderNextTime = true;
@@ -4184,21 +4108,29 @@ namespace NLGUI
}
//
- _Browsing = true;
_DocumentUrl = _URL;
_DocumentHtml = html;
_NextRefreshTime = 0;
_RefreshUrl.clear();
- // clear content
- beginBuild();
- resetCssStyle();
-
- // start new rendering
- _HtmlDOM = CHtmlElement(CHtmlElement::NONE, "");
-
- if (!trim(html).empty())
+ if (trim(html).empty())
{
+ // clear the page
+ beginBuild();
+
+ // clear previous page and state
+ removeContent();
+
+ endBuild();
+ }
+ else
+ {
+ // browser.css
+ resetCssStyle();
+
+ // start new rendering
+ _HtmlDOM = CHtmlElement(CHtmlElement::NONE, "");
+ _CurrentHTMLElement = NULL;
success = parseHtml(html);
if (success)
{
@@ -4214,10 +4146,6 @@ namespace NLGUI
}
}
- endBuild();
- updateRefreshButton();
- _Browsing = false;
-
return success;
}
@@ -4250,8 +4178,24 @@ namespace NLGUI
// ***************************************************************************
+ void CGroupHTML::beginBuild ()
+ {
+ _Browsing = true;
+ }
+
void CGroupHTML::endBuild ()
{
+ // set the browser as complete
+ _Browsing = false;
+ updateRefreshButton();
+
+ // check that the title is set, or reset it (in the case the page
+ // does not provide a title)
+ if (_TitleString.empty())
+ {
+ setTitle(_TitlePrefix);
+ }
+
invalidateCoords();
}
@@ -4267,24 +4211,6 @@ namespace NLGUI
{
}
- // ***************************************************************************
- void CGroupHTML::requestTerminated()
- {
- if (_CurlWWW)
- {
- #if LOG_DL
- nlwarning("(%s) stop curl, url '%s'", _Id.c_str(), _CurlWWW->Url.c_str());
- #endif
- if (MultiCurl)
- curl_multi_remove_handle(MultiCurl, _CurlWWW->Request);
-
- delete _CurlWWW;
-
- _CurlWWW = NULL;
- _Connecting = false;
- }
- }
-
// ***************************************************************************
string CGroupHTML::home ()
@@ -4472,10 +4398,12 @@ namespace NLGUI
void CGroupHTML::updateRefreshButton()
{
CCtrlBaseButton *butRefresh = dynamic_cast(CWidgetManager::getInstance()->getElementFromId(_BrowseRefreshButton));
-
- bool enabled = !_Browsing && !_Connecting && !_URL.empty();
if(butRefresh)
- butRefresh->setFrozen(!enabled);
+ {
+ // connecting, rendering, or is missing url
+ bool frozen = _CurlWWW || _Browsing || _URL.empty();
+ butRefresh->setFrozen(frozen);
+ }
}
// ***************************************************************************
@@ -4949,6 +4877,7 @@ namespace NLGUI
{
_WaitingForStylesheet = false;
_StylesheetQueue.clear();
+ _Style.reset();
std::string css;
@@ -4977,7 +4906,6 @@ namespace NLGUI
// table { border-spacing: 2px;} - overwrites cellspacing attribute
css += "table { border-collapse: separate;}";
- _Style.reset();
_Style.parseStylesheet(css);
}
@@ -5158,11 +5086,6 @@ namespace NLGUI
if (_TrustedDomain)
_Link.back() = suri;
}
- else if (_TrustedDomain && suri[0] == '#' && _LuaHrefHack)
- {
- // Direct url (hack for lua beginElement)
- _Link.back() = suri.substr(1);
- }
else
{
// convert href from "?key=val" into "http://domain.com/?key=val"
diff --git a/code/ryzom/client/src/interface_v3/lua_ihm_ryzom.cpp b/code/ryzom/client/src/interface_v3/lua_ihm_ryzom.cpp
index a599cd18b..f749d9227 100644
--- a/code/ryzom/client/src/interface_v3/lua_ihm_ryzom.cpp
+++ b/code/ryzom/client/src/interface_v3/lua_ihm_ryzom.cpp
@@ -3207,7 +3207,6 @@ void CLuaIHMRyzom::browseNpcWebPage(const std::string &htmlId, const std::string
groupHtml->setTimeout((float)std::max(0.0, timeout));
// Browse the url
- groupHtml->clean();
groupHtml->browse(url.c_str());
// Set top of the page
CCtrlScroll *pScroll = groupHtml->getScrollBar();