diff --git a/include/eepp/graphics/fonttruetype.hpp b/include/eepp/graphics/fonttruetype.hpp index b122fde47..30fd8bd25 100644 --- a/include/eepp/graphics/fonttruetype.hpp +++ b/include/eepp/graphics/fonttruetype.hpp @@ -274,6 +274,12 @@ class EE_API FontTrueType : public Font { bool setFontFace( void* face ); void updateMonospaceState() const; + + void disconnectBoldFont(); + + void disconnectItalicFont(); + + void disconnectBoldItalicFont(); }; }} // namespace EE::Graphics diff --git a/src/eepp/graphics/fontmanager.cpp b/src/eepp/graphics/fontmanager.cpp index b740e197a..0e131e2da 100644 --- a/src/eepp/graphics/fontmanager.cpp +++ b/src/eepp/graphics/fontmanager.cpp @@ -105,8 +105,6 @@ Font* FontManager::getByInternalId( Uint32 internalId ) const { } FontTrueType* FontManager::getOrLoadSystemFallbackFont( const FontDesc& desc ) { - static constexpr Uint32 MAX_SYSTEM_FALLBACK_FONTS = 32; - if ( desc.path.empty() ) return nullptr; @@ -126,12 +124,6 @@ FontTrueType* FontManager::getOrLoadSystemFallbackFont( const FontDesc& desc ) { mSystemFallbackFonts.push_back( ttf ); - if ( mSystemFallbackFonts.size() > MAX_SYSTEM_FALLBACK_FONTS ) { - Font* oldest = mSystemFallbackFonts.front(); - mSystemFallbackFonts.erase( mSystemFallbackFonts.begin() ); - eeSAFE_DELETE( oldest ); - } - return ttf; } diff --git a/src/eepp/graphics/fonttruetype.cpp b/src/eepp/graphics/fonttruetype.cpp index 20a1395fb..a116a5f53 100644 --- a/src/eepp/graphics/fonttruetype.cpp +++ b/src/eepp/graphics/fonttruetype.cpp @@ -359,10 +359,12 @@ bool FontTrueType::loadFromFile( const std::string& filename, Uint32 faceIndex ) // Load the new font face from the specified file FT_Face face; - if ( FT_New_Face( static_cast( mLibrary ), filename.c_str(), - static_cast( mFaceIndex ), &face ) != 0 ) { - Log::error( "Failed to load font \"%s\" (%s) (failed to create the font face)", - filename.c_str(), mFontName.c_str() ); + FT_Error err = FT_New_Face( static_cast( mLibrary ), filename.c_str(), + static_cast( mFaceIndex ), &face ); + if ( err != 0 ) { + const char* err_str = FT_Error_String( err ); + Log::error( "Failed to load font \"%s\" (%s, face index %zu) - %s (code %d)", filename, + mFontName, mFaceIndex, err_str ? err_str : "Unknown error", err ); return false; } @@ -397,11 +399,14 @@ bool FontTrueType::loadFromMemory( const void* data, std::size_t sizeInBytes, bo // Load the new font face from the specified file FT_Face face; - if ( FT_New_Memory_Face( static_cast( mLibrary ), - reinterpret_cast( ptr ), - static_cast( sizeInBytes ), - static_cast( mFaceIndex ), &face ) != 0 ) { - Log::error( "Failed to load font from memory (failed to create the font face)" ); + FT_Error err = FT_New_Memory_Face( + static_cast( mLibrary ), reinterpret_cast( ptr ), + static_cast( sizeInBytes ), static_cast( mFaceIndex ), &face ); + if ( err != 0 ) { + const char* err_str = FT_Error_String( err ); + Log::error( "Failed to load font from memory (failed to create the font face, face index " + "%zu): %s (code %d)", + mFaceIndex, err_str ? err_str : "Unknown error", err ); return false; } @@ -1071,20 +1076,9 @@ void FontTrueType::cleanup() { if ( FontManager::existsSingleton() && FontManager::instance()->getColorEmojiFont() == this ) FontManager::instance()->setColorEmojiFont( nullptr ); - if ( mFontBoldItalicCb != 0 && mFontBoldItalic != nullptr ) { - mFontBoldItalic->popFontEventCallback( mFontBoldItalicCb ); - mFontBoldItalicCb = 0; - } - - if ( mFontBoldCb != 0 && mFontBold != nullptr ) { - mFontBold->popFontEventCallback( mFontBoldCb ); - mFontBoldCb = 0; - } - - if ( mFontItalicCb != 0 && mFontItalic != nullptr ) { - mFontItalic->popFontEventCallback( mFontItalicCb ); - mFontItalicCb = 0; - } + disconnectBoldItalicFont(); + disconnectBoldFont(); + disconnectItalicFont(); mCallbacks.clear(); mNumCallBacks = 0; @@ -1772,6 +1766,7 @@ void FontTrueType::updateMonospaceState() const { void FontTrueType::setBoldFont( FontTrueType* fontBold ) { if ( fontBold == mFontBold ) return; + disconnectBoldFont(); mFontBold = fontBold; if ( mFontBold != nullptr ) { mFontBoldCb = mFontBold->pushFontEventCallback( [this]( Uint32, Event event, Font* ) { @@ -1788,6 +1783,7 @@ void FontTrueType::setBoldFont( FontTrueType* fontBold ) { void FontTrueType::setItalicFont( FontTrueType* fontItalic ) { if ( fontItalic == mFontItalic ) return; + disconnectItalicFont(); mFontItalic = fontItalic; if ( mFontItalic != nullptr ) { mFontItalicCb = mFontItalic->pushFontEventCallback( [this]( Uint32, Event event, Font* ) { @@ -1804,6 +1800,7 @@ void FontTrueType::setItalicFont( FontTrueType* fontItalic ) { void FontTrueType::setBoldItalicFont( FontTrueType* fontBoldItalic ) { if ( fontBoldItalic == mFontBoldItalic ) return; + disconnectBoldItalicFont(); mFontBoldItalic = fontBoldItalic; if ( mFontBoldItalic != nullptr ) { mFontBoldItalicCb = @@ -1818,6 +1815,27 @@ void FontTrueType::setBoldItalicFont( FontTrueType* fontBoldItalic ) { updateMonospaceState(); } +void FontTrueType::disconnectBoldFont() { + if ( mFontBoldCb != 0 && mFontBold != nullptr ) + mFontBold->popFontEventCallback( mFontBoldCb ); + mFontBold = nullptr; + mFontBoldCb = 0; +} + +void FontTrueType::disconnectItalicFont() { + if ( mFontItalicCb != 0 && mFontItalic != nullptr ) + mFontItalic->popFontEventCallback( mFontItalicCb ); + mFontItalic = nullptr; + mFontItalicCb = 0; +} + +void FontTrueType::disconnectBoldItalicFont() { + if ( mFontBoldItalicCb != 0 && mFontBoldItalic != nullptr ) + mFontBoldItalic->popFontEventCallback( mFontBoldItalicCb ); + mFontBoldItalic = nullptr; + mFontBoldItalicCb = 0; +} + bool FontTrueType::hasSvgGlyphs() const { return mHasSvgGlyphs; } diff --git a/src/eepp/graphics/image.cpp b/src/eepp/graphics/image.cpp index 2539e21fd..28cb5f02e 100644 --- a/src/eepp/graphics/image.cpp +++ b/src/eepp/graphics/image.cpp @@ -1320,6 +1320,11 @@ void Image::copyImage( Graphics::Image* image, const Uint32& x, const Uint32& y } void Image::resize( const Uint32& newWidth, const Uint32& newHeight, ResamplerFilter filter ) { + if ( newWidth == 0 || newHeight == 0 ) { + Log::warning( "Image::resize: Invalid resize %dx%d", newWidth, newHeight ) ; + return; + } + if ( NULL != mPixels && ( mWidth != newWidth || mHeight != newHeight ) ) { unsigned char* resampled = resample_image( mPixels, mWidth, mHeight, mChannels, newWidth, newHeight, filter ); diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index 5a0875650..0f2b37b45 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -661,6 +663,11 @@ Uint64 Http::requestAsync( const Http::AsyncResponseCallback& cb, const URI& uri const Http::Request::FieldTable& headers, const std::string& body, const bool& validateCertificate, const URI& proxy, bool followRedirect ) { + if ( Log::existsSingleton() && Log::instance()->getLogLevelThreshold() == LogLevel::Debug ) { + Log::debug( "Http::requestAsync: %s \"%s\"", Request::methodToString( method ), + uri.toString() ); + } + auto http = sGlobalHttpPool.get( uri, proxy ); Request request( uri.getPathAndQuery(), method, body, validateCertificate, validateCertificate, true, true ); @@ -978,8 +985,6 @@ Http::Response Http::downloadRequest( const Http::Request& request, IOStream& wr std::size_t currentTotalBytes = 0; std::size_t len = 0; std::size_t read = 0; - char* eol = NULL; // end of line - char* bol = NULL; // beginning of line char buffer[PACKET_BUFFER_SIZE + 1]; bool isnheader = false; bool chunked = false; @@ -998,173 +1003,147 @@ Http::Response Http::downloadRequest( const Http::Request& request, IOStream& wr // If we didn't receive the header yet, we will try to find the end of the // header if ( !isnheader ) { - // calculate combined length of unprocessed data and new data - len += read; + headerBuffer.append( readBuffer, read ); - // NULL terminate buffer for string functions - readBuffer[len] = '\0'; + std::size_t headerEnd = headerBuffer.find( "\r\n\r\n" ); + std::size_t headerDelimiterSize = 4; - // process each line in buffer looking for header break - bol = readBuffer; + if ( headerEnd == std::string::npos ) { + headerEnd = headerBuffer.find( "\n\n" ); + headerDelimiterSize = 2; + } - while ( !isnheader && ( eol = strchr( bol, '\n' ) ) != NULL ) { - // update bol based upon the value of eol - bol = eol + 1; + if ( headerEnd != std::string::npos ) { + isnheader = true; + headerEnd += headerDelimiterSize; + len = headerBuffer.size() - headerEnd; - // test if end of headers has been reached - if ( 0 == strncmp( bol, "\r\n", 2 ) || 0 == strncmp( bol, "\n", 1 ) ) { - // note that end of headers has been reached - isnheader = true; + std::string responseHead( headerBuffer, 0, headerEnd ); - // update the value of bol to reflect the beginning of the line - // immediately after the headers - if ( bol[0] != '\n' ) - bol += 1; + if ( !responseHead.empty() ) { + // Build the Response object from the received data + received.parse( responseHead ); - bol += 1; + // Check if the response is chunked + chunked = received.getField( "transfer-encoding" ) == "chunked"; - // calculate the amount of data remaining in the buffer - len = read - ( bol - readBuffer ); + // Check if the content is compressed + std::string encoding( received.getField( "content-encoding" ) ); + compressed = + encoding == "gzip" || encoding == "deflate" || encoding == "br"; - // Fill the header buffer - headerBuffer.append( readBuffer, ( bol - readBuffer ) ); + if ( compressed ) { + Compression::Mode compressionMode = + "gzip" == encoding + ? Compression::MODE_GZIP + : ( "br" == encoding ? Compression::MODE_BROTLI + : Compression::MODE_DEFLATE ); - if ( !headerBuffer.empty() ) { - // Build the Response object from the received data - received.parse( headerBuffer ); + inflateStream = + IOStreamInflate::New( writeTo, compressionMode ); + } - // Check if the response is chunked - chunked = received.getField( "transfer-encoding" ) == "chunked"; + if ( chunked ) { + IOStream& writeToStream = compressed ? *inflateStream : writeTo; + chunkedStream = eeNew( HttpStreamChunked, ( writeToStream ) ); + } - // Check if the content is compressed - std::string encoding( received.getField( "content-encoding" ) ); - compressed = encoding == "gzip" || encoding == "deflate" || - encoding == "br"; - - if ( compressed ) { - Compression::Mode compressionMode = - "gzip" == encoding - ? Compression::MODE_GZIP - : ( "br" == encoding ? Compression::MODE_BROTLI - : Compression::MODE_DEFLATE ); - - inflateStream = - IOStreamInflate::New( writeTo, compressionMode ); - } - - if ( chunked ) { - IOStream& writeToStream = - compressed ? *inflateStream : writeTo; - chunkedStream = - eeNew( HttpStreamChunked, ( writeToStream ) ); - } - - bufferStream = chunked - ? chunkedStream + bufferStream = chunked ? chunkedStream : ( compressed ? inflateStream : &writeTo ); - // Get the content length - if ( !received.getField( "content-length" ).empty() ) { - if ( !String::fromString( - contentLength, - received.getField( "content-length" ) ) ) - contentLength = 0; - } + // Get the content length + if ( !received.getField( "content-length" ).empty() ) { + if ( !String::fromString( + contentLength, + received.getField( "content-length" ) ) ) + contentLength = 0; + } - if ( mConnection && - received.getField( "connection" ) == "closed" ) { - mConnection->setConnected( false ); - mConnection->setTunneled( false ); - } + if ( mConnection && + received.getField( "connection" ) == "closed" ) { + mConnection->setConnected( false ); + mConnection->setTunneled( false ); + } - // If a redirection is requested, and requests follows - // redirections, send a new request to the redirection location. - if ( ( received.getStatus() == Response::MovedPermanently || - received.getStatus() == Response::MovedTemporarily || - received.getStatus() == Response::PermanentRedirect || - received.getStatus() == Response::TemporaryRedirect ) && - request.getFollowRedirect() ) { + // If a redirection is requested, and requests follows + // redirections, send a new request to the redirection location. + if ( ( received.getStatus() == Response::MovedPermanently || + received.getStatus() == Response::MovedTemporarily || + received.getStatus() == Response::PermanentRedirect || + received.getStatus() == Response::TemporaryRedirect ) && + request.getFollowRedirect() ) { - // Only continue redirecting if less than 10 redirections - // were done - if ( request.mRedirectionCount < - request.getMaxRedirects() ) { - std::string location( received.getField( "location" ) ); - URI uri( location ); + // Only continue redirecting if less than 10 redirections + // were done + if ( request.mRedirectionCount < request.getMaxRedirects() ) { + std::string location( received.getField( "location" ) ); + URI uri( location ); - // Close the connection - if ( mConnection && !mConnection->isKeepAlive() ) - mConnection->disconnect(); + // Close the connection + if ( mConnection && !mConnection->isKeepAlive() ) + mConnection->disconnect(); - eeSAFE_DELETE( chunkedStream ); - eeSAFE_DELETE( inflateStream ); + eeSAFE_DELETE( chunkedStream ); + eeSAFE_DELETE( inflateStream ); - if ( !request.isCancelled() && - !sendProgress( *this, request, received, - Request::Redirect, contentLength, - currentTotalBytes ) ) { - request.mCancel = true; + if ( !request.isCancelled() && + !sendProgress( *this, request, received, + Request::Redirect, contentLength, + currentTotalBytes ) ) { + request.mCancel = true; + } else { + Http::Request newRequest( request ); + newRequest.setUri( uri.getPathAndQuery() ); + newRequest.setMethod( + Http::Request::getRedirectMethodFromStatus( + request.getMethod(), received.getStatus() ) ); + + newRequest.setProgressCallback( + request.getProgressCallback() ); + + if ( received.hasField( "set-cookie" ) ) { + newRequest.setField( + "Cookie", received.getField( "set-cookie" ) ); + } + + request.mRedirectionCount++; + newRequest.mRedirectionCount = + request.mRedirectionCount; + + // Same host, expects a path in the same domain + if ( uri.getHost().empty() || + uri.getHost() == getHostName() ) { + return downloadRequest( newRequest, writeTo, + timeout ); } else { - Http::Request newRequest( request ); - newRequest.setUri( uri.getPathAndQuery() ); - newRequest.setMethod( - Http::Request::getRedirectMethodFromStatus( - request.getMethod(), - received.getStatus() ) ); - - newRequest.setProgressCallback( - request.getProgressCallback() ); - - if ( received.hasField( "set-cookie" ) ) { - newRequest.setField( - "Cookie", - received.getField( "set-cookie" ) ); - } - - request.mRedirectionCount++; - newRequest.mRedirectionCount = - request.mRedirectionCount; - - // Same host, expects a path in the same domain - if ( uri.getHost().empty() || - uri.getHost() == getHostName() ) { - return downloadRequest( newRequest, writeTo, - timeout ); - } else { - // New host, we need to solve the host - Http http( uri.getHost(), uri.getPort(), - uri.getScheme() == "https" ? true - : false ); - return http.downloadRequest( newRequest, - writeTo, timeout ); - } + // New host, we need to solve the host + Http http( uri.getHost(), uri.getPort(), + uri.getScheme() == "https" ? true + : false ); + return http.downloadRequest( newRequest, writeTo, + timeout ); } } } - - if ( !request.isCancelled() && - !sendProgress( *this, request, received, - Request::HeaderReceived, contentLength, - 0 ) ) { - request.mCancel = true; - } - - // Move the readBuffer to the starting point - // of the file buffer - if ( len > 0 ) { - readBuffer = bol; - read = len; - } else { - read = 0; - } - - headerBuffer.clear(); } - } - } - if ( !isnheader ) { - headerBuffer.append( readBuffer, ( bol - readBuffer ) ); + if ( !request.isCancelled() && + !sendProgress( *this, request, received, + Request::HeaderReceived, contentLength, 0 ) ) { + request.mCancel = true; + } + + // Move the response body bytes already read into the socket buffer. + if ( len > 0 ) { + std::memmove( buffer, headerBuffer.data() + headerEnd, len ); + readBuffer = buffer; + read = len; + } else { + read = 0; + } + + headerBuffer.clear(); + } } } diff --git a/src/eepp/network/tcpsocket.cpp b/src/eepp/network/tcpsocket.cpp index 5497167b3..21ad391b0 100644 --- a/src/eepp/network/tcpsocket.cpp +++ b/src/eepp/network/tcpsocket.cpp @@ -11,8 +11,8 @@ #endif #ifdef _MSC_VER -#pragma warning( \ - disable : 4127 ) // "conditional expression is constant" generated by the FD_SET macro +#pragma warning( disable \ + : 4127 ) // "conditional expression is constant" generated by the FD_SET macro #endif namespace { @@ -129,6 +129,13 @@ Socket::Status TcpSocket::connect( const IpAddress& remoteAddress, unsigned shor // Otherwise, wait until something happens to our socket (success, timeout or error) if ( status == Socket::NotReady ) { + if ( getHandle() >= FD_SETSIZE ) { + // The socket FD is too large for select(). + // You cannot safely use FD_SET. + setBlocking( true ); + return Error; + } + // Setup the selector fd_set selector; FD_ZERO( &selector ); diff --git a/src/eepp/ui/uirichtext.cpp b/src/eepp/ui/uirichtext.cpp index 0b051542b..2a1694713 100644 --- a/src/eepp/ui/uirichtext.cpp +++ b/src/eepp/ui/uirichtext.cpp @@ -484,6 +484,8 @@ Uint32 UIRichText::getFontSize() const { UIRichText* UIRichText::setFontSize( const Uint32& characterSize ) { if ( mRichText.getFontStyleConfig().CharacterSize != characterSize ) { + if ( characterSize == 0 ) + return this; mRichText.getFontStyleConfig().CharacterSize = characterSize; mRichText.invalidate(); mLineHeightPxDirty = true; @@ -745,6 +747,12 @@ void UIRichText::loadFromXmlNode( const pugi::xml_node& node ) { } else { // Let parent logic load standard child widget UIWidget* uiwidget = UIWidgetCreator::createFromName( child.name() ); + + // For not known elements it should create an HTMLUnknown element, in practice + // an span with a the tag name used is enough + if ( uiwidget == nullptr ) + uiwidget = UITextSpan::NewWithTag( child.name() ); + if ( uiwidget ) { uiwidget->setParent( this ); uiwidget->loadFromXmlNode( child ); diff --git a/src/eepp/ui/uiscenenode.cpp b/src/eepp/ui/uiscenenode.cpp index 2dbc3bcea..930f340b2 100644 --- a/src/eepp/ui/uiscenenode.cpp +++ b/src/eepp/ui/uiscenenode.cpp @@ -1190,9 +1190,11 @@ void UISceneNode::loadFontFaces( const StyleSheetStyleVector& styles, URI baseUR Base64::decode( data, decoded ); FontTrueType* font = FontTrueType::New( familyName ); if ( font->loadFromMemory( &decoded[0], decoded.size() ) ) { - trySetFontFamily( fontFamily, fontStyle, font ); - mFontFaces.push_back( font ); - runOnMainThread( [this] { mRoot->reloadFontFamily(); } ); + runOnMainThread( [this, trySetFontFamily, fontFamily, fontStyle, font] { + trySetFontFamily( fontFamily, fontStyle, font ); + mFontFaces.push_back( font ); + mRoot->reloadFontFamily(); + } ); } else eeSAFE_DELETE( font ); } @@ -1517,18 +1519,38 @@ Font* UISceneNode::getFontFromNamesList( std::string_view names, Uint32 fontStyl name = String::trim( name, ' ' ); name = String::trim( name, '\'' ); name = String::trim( name, '"' ); + std::string fontFamily{ name }; + size_t size = fontFamily.size(); if ( fontStyle ) fontFamily += "#" + Text::styleFlagToString( fontStyle ); + font = FontManager::instance()->getByName( fontFamily ); + + if ( fontStyle ) + fontFamily.resize( size ); + + if ( font == nullptr && + SystemFontResolver::genericFamilyFromName( fontFamily ) != GenericFamily::None ) { + FontQuery query; + query.family = fontFamily; + query.italic = fontStyle & Text::Italic; + query.weight = ( fontStyle & Text::Bold ) ? FontWeight::Bold : FontWeight::Normal; + fontFamily = SystemFontResolver::instance()->resolve( query ).family; + + if ( fontStyle ) + fontFamily += "#" + Text::styleFlagToString( fontStyle ); + + font = FontManager::instance()->getByName( fontFamily ); + } + return font != nullptr; }, ',' ); - if ( !font && Graphics::SystemFontResolver::isEnabled() ) { - Graphics::FontWeight weight = - ( fontStyle & Text::Bold ) ? Graphics::FontWeight::Bold : Graphics::FontWeight::Normal; - Graphics::FontDesc desc = Graphics::SystemFontResolver::instance()->resolveFromNamesList( + if ( font == nullptr && SystemFontResolver::isEnabled() ) { + FontWeight weight = ( fontStyle & Text::Bold ) ? FontWeight::Bold : FontWeight::Normal; + FontDesc desc = SystemFontResolver::instance()->resolveFromNamesList( std::string{ names }, weight, fontStyle & Text::Italic ); if ( !desc.path.empty() ) { std::string family = desc.family; @@ -1559,7 +1581,7 @@ Font* UISceneNode::getFontFromNamesList( std::string_view names, Uint32 fontStyl } Font* UISceneNode::reevaluateFontStyle( Font* currentFont, Uint32 fontStyle ) const { - if ( !currentFont || !Graphics::SystemFontResolver::isEnabled() ) + if ( !currentFont || !SystemFontResolver::isEnabled() ) return nullptr; if ( currentFont->getType() != FontType::TTF ) @@ -1579,7 +1601,7 @@ Font* UISceneNode::reevaluateFontStyle( Font* currentFont, Uint32 fontStyle ) co } void UISceneNode::loadFontStyleVariants( Font* font, const std::string& family ) const { - if ( !font || !Graphics::SystemFontResolver::isEnabled() ) + if ( !font || !SystemFontResolver::isEnabled() ) return; if ( font->getType() != FontType::TTF ) return; diff --git a/src/eepp/ui/uitextspan.cpp b/src/eepp/ui/uitextspan.cpp index c25f3d611..a31a5c138 100644 --- a/src/eepp/ui/uitextspan.cpp +++ b/src/eepp/ui/uitextspan.cpp @@ -231,6 +231,8 @@ Uint32 UITextSpan::getFontSize() const { UITextSpan* UITextSpan::setFontSize( const Uint32& characterSize ) { if ( mRichText.getFontStyleConfig().CharacterSize != characterSize ) { + if ( characterSize == 0 ) + return this; mRichText.getFontStyleConfig().CharacterSize = characterSize; mStyleState |= StyleStateFontSize; mRichText.invalidate(); @@ -414,6 +416,10 @@ void UITextSpan::loadFromXmlNode( const pugi::xml_node& node ) { for ( pugi::xml_node child = node.first_child(); child; child = child.next_sibling() ) { if ( child.type() == pugi::node_element ) { UIWidget* widget = UIWidgetCreator::createFromName( child.name() ); + + if ( widget == nullptr ) + widget = UITextSpan::NewWithTag( child.name() ); + if ( widget ) { widget->setParent( this ); widget->loadFromXmlNode( child ); diff --git a/src/examples/ui_html/ui_html.cpp b/src/examples/ui_html/ui_html.cpp index c0dc3b08b..144dc3be3 100644 --- a/src/examples/ui_html/ui_html.cpp +++ b/src/examples/ui_html/ui_html.cpp @@ -4,7 +4,11 @@ #include EE_MAIN_FUNC int main( int argc, char** argv ) { + std::shared_ptr threadPool( + ThreadPool::createShared( eemax( 4, Sys::getCPUCount() ) ) ); + Http::setThreadPool( threadPool ); SystemFontResolver::setEnabled( true ); + args::ArgumentParser parser( "eepp HTML Example" ); args::HelpFlag help( parser, "help", "Display this help menu", { 'h', "help" } ); diff --git a/src/tests/unit_tests/cookiemanager_tests.cpp b/src/tests/unit_tests/cookiemanager_tests.cpp index 938f9486f..ea48c1de3 100644 --- a/src/tests/unit_tests/cookiemanager_tests.cpp +++ b/src/tests/unit_tests/cookiemanager_tests.cpp @@ -1,6 +1,5 @@ #include "utest.h" -#include #include using namespace EE; diff --git a/src/tests/unit_tests/fontrendering_tests.cpp b/src/tests/unit_tests/fontrendering_tests.cpp index c314a0910..1bf58db26 100644 --- a/src/tests/unit_tests/fontrendering_tests.cpp +++ b/src/tests/unit_tests/fontrendering_tests.cpp @@ -34,6 +34,36 @@ using namespace EE::Graphics; using namespace EE::Window; using namespace EE::UI; +UTEST( FontRendering, relatedFontsDisconnectReplacedCallbacks ) { + FontTrueType* font = FontTrueType::New( "RelatedFontsDisconnect-Regular" ); + FontTrueType* oldBold = FontTrueType::New( "RelatedFontsDisconnect-OldBold" ); + FontTrueType* newBold = FontTrueType::New( "RelatedFontsDisconnect-NewBold" ); + FontTrueType* oldItalic = FontTrueType::New( "RelatedFontsDisconnect-OldItalic" ); + FontTrueType* newItalic = FontTrueType::New( "RelatedFontsDisconnect-NewItalic" ); + FontTrueType* oldBoldItalic = FontTrueType::New( "RelatedFontsDisconnect-OldBoldItalic" ); + FontTrueType* newBoldItalic = FontTrueType::New( "RelatedFontsDisconnect-NewBoldItalic" ); + + font->setBoldFont( oldBold ); + font->setBoldFont( newBold ); + eeDelete( oldBold ); + EXPECT_EQ( newBold, font->getBoldFont() ); + + font->setItalicFont( oldItalic ); + font->setItalicFont( newItalic ); + eeDelete( oldItalic ); + EXPECT_EQ( newItalic, font->getItalicFont() ); + + font->setBoldItalicFont( oldBoldItalic ); + font->setBoldItalicFont( newBoldItalic ); + eeDelete( oldBoldItalic ); + EXPECT_EQ( newBoldItalic, font->getBoldItalicFont() ); + + eeDelete( font ); + eeDelete( newBold ); + eeDelete( newItalic ); + eeDelete( newBoldItalic ); +} + UTEST( FontRendering, fontsTest ) { FileSystem::changeWorkingDirectory( Sys::getProcessPath() ); diff --git a/src/tests/unit_tests/http.cpp b/src/tests/unit_tests/http.cpp new file mode 100644 index 000000000..55492f3c4 --- /dev/null +++ b/src/tests/unit_tests/http.cpp @@ -0,0 +1,38 @@ +#include "utest.h" + +#include +#include + +#include +#include +#include + +using namespace EE; +using namespace EE::Network; + +UTEST( Http, responseHeaderLineLargerThanReceiveBuffer ) { + TcpListener listener; + ASSERT_EQ( listener.listen( Socket::AnyPort, IpAddress::LocalHost ), Socket::Done ); + + std::atomic serverOk{ false }; + std::thread server( [&listener, &serverOk] { + TcpSocket client; + if ( listener.accept( client ) != Socket::Done ) + return; + + const std::string response = "HTTP/1.1 200 OK\r\nX-Long: " + std::string( 17000, 'a' ) + + "\r\nContent-Length: 5\r\nConnection: close\r\n\r\nhello"; + serverOk = client.send( response.data(), response.size() ) == Socket::Done; + client.disconnect(); + } ); + + Http http( "127.0.0.1", listener.getLocalPort() ); + Http::Response response = http.sendRequest( Http::Request( "/" ), Seconds( 5 ) ); + + server.join(); + listener.close(); + + EXPECT_TRUE( serverOk ); + EXPECT_EQ( response.getStatus(), Http::Response::Ok ); + EXPECT_TRUE( response.getBody() == "hello" ); +} diff --git a/src/tests/unit_tests/uihtml_tests.cpp b/src/tests/unit_tests/uihtml_tests.cpp index d70a29d62..309e99319 100644 --- a/src/tests/unit_tests/uihtml_tests.cpp +++ b/src/tests/unit_tests/uihtml_tests.cpp @@ -131,6 +131,11 @@ UTEST( UIHTMLTable, complexLayout2 ) { win->getInput()->update(); SceneManager::instance()->update(); + SceneManager::instance()->update(); + + win->clear(); + SceneManager::instance()->draw(); + win->display(); win->clear(); SceneManager::instance()->draw();