Skip to content

Commit

Permalink
Browser: Added a new URL scheme
Browse files Browse the repository at this point in the history
There are several issues with custom URL schemes in CEF. To get rid of these problems I decided to replace the mtalocal:// scheme by http://mta/local/bla.html (for the current resource) and http://mta/resourceName/bla.html as a generic handler.
This commit also enables post requests for ajax handlers.

mtalocal works still though, but throws a warning when used by loadBrowserURL
  • Loading branch information
jushar committed Aug 16, 2015
1 parent fdfe530 commit b8e2994
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 67 deletions.
171 changes: 121 additions & 50 deletions MTA10/core/CWebCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ bool CWebCore::Initialise ()
{
CefMainArgs mainArgs;
void* sandboxInfo = nullptr;
CefRefPtr<CCefApp> app = new CCefApp;
CefRefPtr<CCefApp> app { new CCefApp };

#if CEF_ENABLE_SANDBOX
CefScopedSandboxInfo scopedSandbox;
Expand Down Expand Up @@ -88,6 +88,7 @@ bool CWebCore::Initialise ()

// Register custom scheme handler factory
CefRegisterSchemeHandlerFactory ( "mtalocal", "", app );
CefRegisterSchemeHandlerFactory ( "http", "mta", app );

return state;
}
Expand Down Expand Up @@ -784,10 +785,7 @@ void CCefApp::OnRegisterCustomSchemes ( CefRefPtr < CefSchemeRegistrar > registr

CefRefPtr<CefResourceHandler> CCefApp::Create ( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& scheme_name, CefRefPtr<CefRequest> request )
{
if (scheme_name != "mtalocal")
return nullptr;

CWebCore* pWebCore = static_cast<CWebCore*>(g_pCore->GetWebCore ());
CWebCore* pWebCore = static_cast<CWebCore*> ( g_pCore->GetWebCore () );
auto pWebView = pWebCore->FindWebView ( browser );
if ( !pWebView || !pWebView->IsLocal () )
return nullptr;
Expand All @@ -796,64 +794,137 @@ CefRefPtr<CefResourceHandler> CCefApp::Create ( CefRefPtr<CefBrowser> browser, C
if ( !CefParseURL ( request->GetURL (), urlParts ) )
return nullptr;

// Get full path
SString path = UTF16ToMbUTF8 ( urlParts.path.str ).substr ( 2 );
if ( scheme_name == "mtalocal" ) // Backward compatibility
{
// Get full path
SString path = UTF16ToMbUTF8 ( urlParts.path.str ).substr ( 2 );

// Get mime type from extension
CefString mimeType;
size_t pos = path.find_last_of ( '.' );
if ( pos != std::string::npos )
mimeType = CefGetMimeType ( path.substr ( pos + 1 ) );
// Check if we're dealing with an external resource
if ( path[0] == ':' )
{
size_t end = path.find_first_of ( '/' );
if ( end != std::string::npos )
{
SString resourceName = path.substr ( 1, end-1 );
SString resourcePath = path.substr ( end );

// Make sure we provide a mime type, even
// when we cannot deduct it from the file extension
if ( mimeType.empty () )
mimeType = "application/octet-stream";
// Call this function recursively and use the mta scheme instead
request->SetURL ( "http://mta/local/" + resourceName + resourcePath );
return Create ( browser, frame, "http", request );
}
return nullptr;
}

// Redirect mtalocal://* to http://mta/local/*, call recursively
request->SetURL ( "http://mta/local/" + path );
return Create ( browser, frame, "http", request );
}

SString host = UTF16ToMbUTF8 ( urlParts.host.str );
if ( scheme_name == "http" && host == "mta" )
{
// Scheme format: http://mta/resourceName/file.html or http://mta/local/file.html for the current resource

// Get resource name and path
SString path = UTF16ToMbUTF8 ( urlParts.path.str ).substr ( 1 ); // Remove slash at the front
size_t slashPos = path.find ( '/' );
if ( slashPos == std::string::npos )
return nullptr;

if( pWebView->HasAjaxHandler ( path ) )
{
std::vector<SString> vecGet;
std::vector<SString> vecPost;
SString resourceName = path.substr ( 0, slashPos );
SString resourcePath = path.substr ( slashPos + 1 );

SString strGet = UTF16ToMbUTF8(urlParts.query.str);
std::vector<SString> vecTmp;
strGet.Split ( "&", vecTmp, 0, 0 );
if ( resourcePath.empty () )
return nullptr;

SString key; SString value;
for ( auto&& param : vecTmp )
{
param.Split ( "=", &key, &value );
vecGet.push_back ( key );
vecGet.push_back ( value );
}
// Get mime type from extension
CefString mimeType;
size_t pos = resourcePath.find_last_of ( '.' );
if ( pos != std::string::npos )
mimeType = CefGetMimeType ( resourcePath.substr ( pos + 1 ) );

// ToDo: POST Information is Empty for non-standard URI schemes
// Getting POST parameters into Lua will require changing
// mtalocal:// to a standard scheme first
/*
CefPostData::ElementVector vecPostElements;
request->GetPostData ().get ()->GetElements ( vecPostElements );
// Make sure we provide a mime type, even
// when we cannot deduct it from the file extension
if ( mimeType.empty () )
mimeType = "application/octet-stream";

for ( auto&& post : vecPostElements )
if ( pWebView->HasAjaxHandler ( resourcePath ) )
{
// ToDo: Parse POST data into our vector
std::vector<SString> vecGet;
std::vector<SString> vecPost;

if ( urlParts.query.str != nullptr )
{
SString strGet = UTF16ToMbUTF8 ( urlParts.query.str );
std::vector<SString> vecTmp;
strGet.Split ( "&", vecTmp );

SString key; SString value;
for ( auto&& param : vecTmp )
{
param.Split ( "=", &key, &value );
vecGet.push_back ( key );
vecGet.push_back ( value );
}
}

CefPostData::ElementVector vecPostElements;
auto postData = request->GetPostData ();
if ( postData.get () )
{
request->GetPostData ()->GetElements ( vecPostElements );

SString key; SString value;
for ( auto&& post : vecPostElements )
{
// Limit to 5MiB and allow byte data only
size_t bytesCount = post->GetBytesCount ();
if ( bytesCount > 5*1024*1024 || post->GetType () != CefPostDataElement::Type::PDE_TYPE_BYTES )
continue;

// Make string from buffer
std::unique_ptr<char[]> buffer { new char[bytesCount] };
post->GetBytes ( bytesCount, buffer.get () );
SStringX param ( buffer.get (), bytesCount );

// Parse POST data into vector
std::vector<SString> vecTmp;
param.Split ( "&", vecTmp );

for ( auto&& param : vecTmp )
{
param.Split ( "=", &key, &value );
vecPost.push_back ( key );
vecPost.push_back ( value );
}
}
}

auto handler = new CAjaxResourceHandler ( vecGet, vecPost, mimeType );
pWebView->HandleAjaxRequest ( resourcePath, handler );
return handler;
}
*/
else
{
// Calculate MTA resource path
if ( resourceName != "local" )
path = ":" + resourceName + "/" + resourcePath;
else
path = resourcePath;

auto handler = new CAjaxResourceHandler ( vecGet, vecPost, mimeType );
pWebView->HandleAjaxRequest ( path, handler );
return handler;
}
else
{
if ( !pWebView->GetFullPathFromLocal ( path ) )
return nullptr;
// Calculate absolute path
if ( !pWebView->GetFullPathFromLocal ( path ) )
return nullptr;

auto stream = CefStreamReader::CreateForFile ( path );
if ( stream.get () )
return new CefStreamResourceHandler ( mimeType, stream );
// Finally, load the file stream
auto stream = CefStreamReader::CreateForFile ( path );
if ( stream.get () )
return new CefStreamResourceHandler ( mimeType, stream );
}

return nullptr;
}

// Return null if there is no matching scheme
return nullptr;
}
43 changes: 27 additions & 16 deletions MTA10/core/CWebView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -603,10 +603,16 @@ bool CWebView::OnBeforeBrowse ( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFram
WString scheme = urlParts.scheme.str;
if ( scheme == L"http" || scheme == L"https" )
{
if ( IsLocal () || g_pCore->GetWebCore ()->GetURLState ( UTF16ToMbUTF8 ( urlParts.host.str ), true ) != eURLState::WEBPAGE_ALLOWED )
bResult = true; // Block remote here
SString host = UTF16ToMbUTF8 ( urlParts.host.str );
if ( host != "mta" )
{
if ( IsLocal () || g_pCore->GetWebCore ()->GetURLState ( host, true ) != eURLState::WEBPAGE_ALLOWED )
bResult = true; // Block remote here
else
bResult = false; // Allow
}
else
bResult = false; // Allow
bResult = false;
}
else if ( scheme == L"mtalocal" )
bResult = false; // Allow mtalocal:// URLs
Expand Down Expand Up @@ -658,22 +664,27 @@ CefRequestHandler::ReturnValue CWebView::OnBeforeResourceLoad ( CefRefPtr<CefBro
if ( scheme == L"http" || scheme == L"https" )
{
SString domain = UTF16ToMbUTF8 ( urlParts.host.str );
if ( IsLocal () )
return RV_CANCEL; // Block remote requests in local mode generally

eURLState urlState = g_pCore->GetWebCore ()->GetURLState ( domain, true );
if ( urlState != eURLState::WEBPAGE_ALLOWED )
if ( domain != "mta" )
{
// Trigger onClientBrowserResourceBlocked event
auto func = std::bind ( &CWebBrowserEventsInterface::Events_OnResourceBlocked, m_pEventsInterface,
SString ( request->GetURL () ), domain, urlState == eURLState::WEBPAGE_NOT_LISTED ? 0 : 1 );
g_pCore->GetWebCore ()->AddEventToEventQueue ( func, this, "OnResourceBlocked" );
if ( IsLocal () )
return RV_CANCEL; // Block remote requests in local mode generally

return RV_CANCEL; // Block if explicitly forbidden
}
eURLState urlState = g_pCore->GetWebCore ()->GetURLState ( domain, true );
if ( urlState != eURLState::WEBPAGE_ALLOWED )
{
// Trigger onClientBrowserResourceBlocked event
auto func = std::bind ( &CWebBrowserEventsInterface::Events_OnResourceBlocked, m_pEventsInterface,
SString ( request->GetURL () ), domain, urlState == eURLState::WEBPAGE_NOT_LISTED ? 0 : 1 );
g_pCore->GetWebCore ()->AddEventToEventQueue ( func, this, "OnResourceBlocked" );

// Allow
return RV_CONTINUE;
return RV_CANCEL; // Block if explicitly forbidden
}

// Allow
return RV_CONTINUE;
}
else
return RV_CONTINUE;
}
else if ( scheme == L"mtalocal" )
{
Expand Down
12 changes: 11 additions & 1 deletion MTA10/mods/shared_logic/lua/CLuaFunctionDefs.Browser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,14 @@ int CLuaFunctionDefs::LoadBrowserURL ( lua_State* luaVM )
// Are we dealing with a remote website?
if ( strURL.substr ( 0, 7 ) == "http://" || strURL.substr ( 0, 8 ) == "https://" )
{
lua_pushboolean ( luaVM, pWebBrowser->LoadURL ( strURL, true, strPostData, bURLEncoded ) );
bool isLocalURL = strURL.substr ( 0, 11 ) == "http://mta/";
if ( pWebBrowser->IsLocal () != isLocalURL )
{
lua_pushboolean ( luaVM, false );
return 1;
}

lua_pushboolean ( luaVM, pWebBrowser->LoadURL ( strURL, !isLocalURL, strPostData, bURLEncoded ) );
return 1;
}

Expand All @@ -114,6 +121,9 @@ int CLuaFunctionDefs::LoadBrowserURL ( lua_State* luaVM )
CResource* pResource = pLuaMain->GetResource ();
if ( CResourceManager::ParseResourcePathInput ( strURL, pResource, strAbsPath ) && pWebBrowser->IsLocal () )
{
// Output deprecated warning, TODO: Remove this at a later point
m_pScriptDebugging->LogWarning ( luaVM, "This URL scheme is deprecated and may not work in future versions. Please consider using http://mta/* instead. See https://wiki.mtasa.com/wiki/LoadBrowserURL for details" );

pWebBrowser->SetTempURL ( strURL );
lua_pushboolean ( luaVM, pWebBrowser->LoadURL ( "mtalocal://" + strURL, false, strPostData, bURLEncoded ) );
return 1;
Expand Down

1 comment on commit b8e2994

@PeewX
Copy link

@PeewX PeewX commented on b8e2994 Aug 16, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Please sign in to comment.