mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-02-18 05:00:48 +01:00
Allow CefV8Context::Eval to bypass CSP (issue #2024)
This commit is contained in:
parent
3a77b243a3
commit
499813b294
@ -123,14 +123,16 @@ typedef struct _cef_v8context_t {
|
|||||||
struct _cef_v8context_t* that);
|
struct _cef_v8context_t* that);
|
||||||
|
|
||||||
///
|
///
|
||||||
// Evaluates the specified JavaScript code using this context's global object.
|
// Execute a string of JavaScript code in this V8 context. The |script_url|
|
||||||
|
// parameter is the URL where the script in question can be found, if any. The
|
||||||
|
// |start_line| parameter is the base line number to use for error reporting.
|
||||||
// On success |retval| will be set to the return value, if any, and the
|
// On success |retval| will be set to the return value, if any, and the
|
||||||
// function will return true (1). On failure |exception| will be set to the
|
// function will return true (1). On failure |exception| will be set to the
|
||||||
// exception, if any, and the function will return false (0).
|
// exception, if any, and the function will return false (0).
|
||||||
///
|
///
|
||||||
int (CEF_CALLBACK *eval)(struct _cef_v8context_t* self,
|
int (CEF_CALLBACK *eval)(struct _cef_v8context_t* self,
|
||||||
const cef_string_t* code, struct _cef_v8value_t** retval,
|
const cef_string_t* code, const cef_string_t* script_url, int start_line,
|
||||||
struct _cef_v8exception_t** exception);
|
struct _cef_v8value_t** retval, struct _cef_v8exception_t** exception);
|
||||||
} cef_v8context_t;
|
} cef_v8context_t;
|
||||||
|
|
||||||
|
|
||||||
|
@ -206,13 +206,17 @@ class CefV8Context : public virtual CefBase {
|
|||||||
virtual bool IsSame(CefRefPtr<CefV8Context> that) =0;
|
virtual bool IsSame(CefRefPtr<CefV8Context> that) =0;
|
||||||
|
|
||||||
///
|
///
|
||||||
// Evaluates the specified JavaScript code using this context's global object.
|
// Execute a string of JavaScript code in this V8 context. The |script_url|
|
||||||
// On success |retval| will be set to the return value, if any, and the
|
// parameter is the URL where the script in question can be found, if any.
|
||||||
// function will return true. On failure |exception| will be set to the
|
// The |start_line| parameter is the base line number to use for error
|
||||||
|
// reporting. On success |retval| will be set to the return value, if any, and
|
||||||
|
// the function will return true. On failure |exception| will be set to the
|
||||||
// exception, if any, and the function will return false.
|
// exception, if any, and the function will return false.
|
||||||
///
|
///
|
||||||
/*--cef()--*/
|
/*--cef(optional_param=script_url)--*/
|
||||||
virtual bool Eval(const CefString& code,
|
virtual bool Eval(const CefString& code,
|
||||||
|
const CefString& script_url,
|
||||||
|
int start_line,
|
||||||
CefRefPtr<CefV8Value>& retval,
|
CefRefPtr<CefV8Value>& retval,
|
||||||
CefRefPtr<CefV8Exception>& exception) =0;
|
CefRefPtr<CefV8Exception>& exception) =0;
|
||||||
};
|
};
|
||||||
|
@ -162,8 +162,8 @@ void CefFrameImpl::ExecuteJavaScript(const CefString& jsCode,
|
|||||||
|
|
||||||
if (jsCode.empty())
|
if (jsCode.empty())
|
||||||
return;
|
return;
|
||||||
if (startLine < 0)
|
if (startLine < 1)
|
||||||
startLine = 0;
|
startLine = 1;
|
||||||
|
|
||||||
if (frame_) {
|
if (frame_) {
|
||||||
GURL gurl = GURL(scriptUrl.ToString());
|
GURL gurl = GURL(scriptUrl.ToString());
|
||||||
|
@ -1014,8 +1014,13 @@ bool CefV8ContextImpl::IsSame(CefRefPtr<CefV8Context> that) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CefV8ContextImpl::Eval(const CefString& code,
|
bool CefV8ContextImpl::Eval(const CefString& code,
|
||||||
|
const CefString& script_url,
|
||||||
|
int start_line,
|
||||||
CefRefPtr<CefV8Value>& retval,
|
CefRefPtr<CefV8Value>& retval,
|
||||||
CefRefPtr<CefV8Exception>& exception) {
|
CefRefPtr<CefV8Exception>& exception) {
|
||||||
|
retval = NULL;
|
||||||
|
exception = NULL;
|
||||||
|
|
||||||
CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false);
|
CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false);
|
||||||
|
|
||||||
if (webkit_glue::IsScriptForbidden())
|
if (webkit_glue::IsScriptForbidden())
|
||||||
@ -1030,33 +1035,28 @@ bool CefV8ContextImpl::Eval(const CefString& code,
|
|||||||
v8::HandleScope handle_scope(isolate);
|
v8::HandleScope handle_scope(isolate);
|
||||||
v8::Local<v8::Context> context = GetV8Context();
|
v8::Local<v8::Context> context = GetV8Context();
|
||||||
v8::Context::Scope context_scope(context);
|
v8::Context::Scope context_scope(context);
|
||||||
v8::Local<v8::Object> obj = context->Global();
|
|
||||||
|
|
||||||
// Retrieve the eval function.
|
const blink::WebString& source = code.ToString16();
|
||||||
v8::Local<v8::Value> val = obj->Get(v8::String::NewFromUtf8(isolate, "eval"));
|
const blink::WebString& source_url = script_url.ToString16();
|
||||||
if (val.IsEmpty() || !val->IsFunction())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(val);
|
|
||||||
v8::Local<v8::Value> code_val = GetV8String(isolate, code);
|
|
||||||
|
|
||||||
v8::TryCatch try_catch(isolate);
|
v8::TryCatch try_catch(isolate);
|
||||||
try_catch.SetVerbose(true);
|
try_catch.SetVerbose(true);
|
||||||
|
|
||||||
retval = NULL;
|
|
||||||
exception = NULL;
|
|
||||||
|
|
||||||
v8::MaybeLocal<v8::Value> func_rv =
|
v8::MaybeLocal<v8::Value> func_rv =
|
||||||
webkit_glue::CallV8Function(context, func, obj, 1, &code_val,
|
webkit_glue::ExecuteV8ScriptAndReturnValue(source, source_url, start_line,
|
||||||
handle_->isolate());
|
context, isolate, try_catch,
|
||||||
|
blink::AccessControlStatus::NotSharableCrossOrigin);
|
||||||
|
|
||||||
if (try_catch.HasCaught()) {
|
if (try_catch.HasCaught()) {
|
||||||
exception = new CefV8ExceptionImpl(context, try_catch.Message());
|
exception = new CefV8ExceptionImpl(context, try_catch.Message());
|
||||||
return false;
|
return false;
|
||||||
} else if (!func_rv.IsEmpty()) {
|
} else if (!func_rv.IsEmpty()) {
|
||||||
retval = new CefV8ValueImpl(isolate, context, func_rv.ToLocalChecked());
|
retval = new CefV8ValueImpl(isolate, context, func_rv.ToLocalChecked());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
NOTREACHED();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
v8::Local<v8::Context> CefV8ContextImpl::GetV8Context() {
|
v8::Local<v8::Context> CefV8ContextImpl::GetV8Context() {
|
||||||
|
@ -182,6 +182,8 @@ class CefV8ContextImpl : public CefV8Context {
|
|||||||
bool Exit() override;
|
bool Exit() override;
|
||||||
bool IsSame(CefRefPtr<CefV8Context> that) override;
|
bool IsSame(CefRefPtr<CefV8Context> that) override;
|
||||||
bool Eval(const CefString& code,
|
bool Eval(const CefString& code,
|
||||||
|
const CefString& script_url,
|
||||||
|
int start_line,
|
||||||
CefRefPtr<CefV8Value>& retval,
|
CefRefPtr<CefV8Value>& retval,
|
||||||
CefRefPtr<CefV8Exception>& exception) override;
|
CefRefPtr<CefV8Exception>& exception) override;
|
||||||
|
|
||||||
|
@ -25,12 +25,14 @@ MSVC_PUSH_WARNING_LEVEL(0);
|
|||||||
#include "third_party/WebKit/public/web/WebViewClient.h"
|
#include "third_party/WebKit/public/web/WebViewClient.h"
|
||||||
|
|
||||||
#include "third_party/WebKit/Source/bindings/core/v8/ScriptController.h"
|
#include "third_party/WebKit/Source/bindings/core/v8/ScriptController.h"
|
||||||
|
#include "third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.h"
|
||||||
#include "third_party/WebKit/Source/bindings/core/v8/V8Binding.h"
|
#include "third_party/WebKit/Source/bindings/core/v8/V8Binding.h"
|
||||||
#include "third_party/WebKit/Source/core/dom/Document.h"
|
#include "third_party/WebKit/Source/core/dom/Document.h"
|
||||||
#include "third_party/WebKit/Source/core/dom/Element.h"
|
#include "third_party/WebKit/Source/core/dom/Element.h"
|
||||||
#include "third_party/WebKit/Source/core/dom/Node.h"
|
#include "third_party/WebKit/Source/core/dom/Node.h"
|
||||||
#include "third_party/WebKit/Source/core/editing/serializers/Serialization.h"
|
#include "third_party/WebKit/Source/core/editing/serializers/Serialization.h"
|
||||||
#include "third_party/WebKit/Source/core/frame/LocalFrame.h"
|
#include "third_party/WebKit/Source/core/frame/LocalFrame.h"
|
||||||
|
#include "third_party/WebKit/Source/core/frame/Settings.h"
|
||||||
#include "third_party/WebKit/Source/web/WebLocalFrameImpl.h"
|
#include "third_party/WebKit/Source/web/WebLocalFrameImpl.h"
|
||||||
#include "third_party/WebKit/Source/web/WebViewImpl.h"
|
#include "third_party/WebKit/Source/web/WebViewImpl.h"
|
||||||
MSVC_POP_WARNING();
|
MSVC_POP_WARNING();
|
||||||
@ -184,6 +186,51 @@ v8::MaybeLocal<v8::Value> CallV8Function(v8::Local<v8::Context> context,
|
|||||||
return func_rv;
|
return func_rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v8::MaybeLocal<v8::Value> ExecuteV8ScriptAndReturnValue(
|
||||||
|
const blink::WebString& source,
|
||||||
|
const blink::WebString& source_url,
|
||||||
|
int start_line,
|
||||||
|
v8::Local<v8::Context> context,
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
v8::TryCatch& tryCatch,
|
||||||
|
blink::AccessControlStatus accessControlStatus) {
|
||||||
|
// Based on ScriptController::executeScriptAndReturnValue
|
||||||
|
DCHECK(isolate);
|
||||||
|
|
||||||
|
if (start_line < 1)
|
||||||
|
start_line = 1;
|
||||||
|
|
||||||
|
const blink::KURL kurl = source_url.isEmpty() ?
|
||||||
|
blink::KURL() : blink::KURL(blink::ParsedURLString, source_url);
|
||||||
|
|
||||||
|
const blink::ScriptSourceCode ssc = blink::ScriptSourceCode(source, kurl,
|
||||||
|
WTF::TextPosition(WTF::OrdinalNumber::fromOneBasedInt(start_line),
|
||||||
|
WTF::OrdinalNumber::fromZeroBasedInt(0)));
|
||||||
|
|
||||||
|
v8::MaybeLocal<v8::Value> result;
|
||||||
|
|
||||||
|
blink::LocalFrame* frame =
|
||||||
|
toLocalFrame(blink::toFrameIfNotDetached(context));
|
||||||
|
DCHECK(frame);
|
||||||
|
|
||||||
|
if (frame) {
|
||||||
|
blink::V8CacheOptions v8CacheOptions(blink::V8CacheOptionsDefault);
|
||||||
|
if (frame && frame->settings())
|
||||||
|
v8CacheOptions = frame->settings()->v8CacheOptions();
|
||||||
|
|
||||||
|
v8::Local<v8::Script> script;
|
||||||
|
if (!blink::v8Call(blink::V8ScriptRunner::compileScript(ssc, isolate,
|
||||||
|
accessControlStatus, v8CacheOptions), script, tryCatch)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = blink::V8ScriptRunner::runCompiledScript(isolate, script,
|
||||||
|
blink::toExecutionContext(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsScriptForbidden() {
|
bool IsScriptForbidden() {
|
||||||
return blink::ScriptForbiddenScope::isScriptForbidden();
|
return blink::ScriptForbiddenScope::isScriptForbidden();
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "include/internal/cef_types.h"
|
#include "include/internal/cef_types.h"
|
||||||
|
#include "third_party/WebKit/Source/core/fetch/AccessControlStatus.h"
|
||||||
#include "v8/include/v8.h"
|
#include "v8/include/v8.h"
|
||||||
|
|
||||||
namespace blink {
|
namespace blink {
|
||||||
@ -52,6 +53,15 @@ v8::MaybeLocal<v8::Value> CallV8Function(v8::Local<v8::Context> context,
|
|||||||
v8::Local<v8::Value> args[],
|
v8::Local<v8::Value> args[],
|
||||||
v8::Isolate* isolate);
|
v8::Isolate* isolate);
|
||||||
|
|
||||||
|
v8::MaybeLocal<v8::Value> ExecuteV8ScriptAndReturnValue(
|
||||||
|
const blink::WebString& source,
|
||||||
|
const blink::WebString& source_url,
|
||||||
|
int start_line,
|
||||||
|
v8::Local<v8::Context> context,
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
v8::TryCatch& tryCatch,
|
||||||
|
blink::AccessControlStatus accessControlStatus);
|
||||||
|
|
||||||
bool IsScriptForbidden();
|
bool IsScriptForbidden();
|
||||||
|
|
||||||
} // webkit_glue
|
} // webkit_glue
|
||||||
|
@ -178,8 +178,8 @@ int CEF_CALLBACK v8context_is_same(struct _cef_v8context_t* self,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int CEF_CALLBACK v8context_eval(struct _cef_v8context_t* self,
|
int CEF_CALLBACK v8context_eval(struct _cef_v8context_t* self,
|
||||||
const cef_string_t* code, struct _cef_v8value_t** retval,
|
const cef_string_t* code, const cef_string_t* script_url, int start_line,
|
||||||
struct _cef_v8exception_t** exception) {
|
struct _cef_v8value_t** retval, struct _cef_v8exception_t** exception) {
|
||||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||||
|
|
||||||
DCHECK(self);
|
DCHECK(self);
|
||||||
@ -197,6 +197,7 @@ int CEF_CALLBACK v8context_eval(struct _cef_v8context_t* self,
|
|||||||
DCHECK(exception);
|
DCHECK(exception);
|
||||||
if (!exception)
|
if (!exception)
|
||||||
return 0;
|
return 0;
|
||||||
|
// Unverified params: script_url
|
||||||
|
|
||||||
// Translate param: retval; type: refptr_same_byref
|
// Translate param: retval; type: refptr_same_byref
|
||||||
CefRefPtr<CefV8Value> retvalPtr;
|
CefRefPtr<CefV8Value> retvalPtr;
|
||||||
@ -212,6 +213,8 @@ int CEF_CALLBACK v8context_eval(struct _cef_v8context_t* self,
|
|||||||
// Execute
|
// Execute
|
||||||
bool _retval = CefV8ContextCppToC::Get(self)->Eval(
|
bool _retval = CefV8ContextCppToC::Get(self)->Eval(
|
||||||
CefString(code),
|
CefString(code),
|
||||||
|
CefString(script_url),
|
||||||
|
start_line,
|
||||||
retvalPtr,
|
retvalPtr,
|
||||||
exceptionPtr);
|
exceptionPtr);
|
||||||
|
|
||||||
|
@ -172,7 +172,8 @@ bool CefV8ContextCToCpp::IsSame(CefRefPtr<CefV8Context> that) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CefV8ContextCToCpp::Eval(const CefString& code,
|
bool CefV8ContextCToCpp::Eval(const CefString& code,
|
||||||
CefRefPtr<CefV8Value>& retval, CefRefPtr<CefV8Exception>& exception) {
|
const CefString& script_url, int start_line, CefRefPtr<CefV8Value>& retval,
|
||||||
|
CefRefPtr<CefV8Exception>& exception) {
|
||||||
cef_v8context_t* _struct = GetStruct();
|
cef_v8context_t* _struct = GetStruct();
|
||||||
if (CEF_MEMBER_MISSING(_struct, eval))
|
if (CEF_MEMBER_MISSING(_struct, eval))
|
||||||
return false;
|
return false;
|
||||||
@ -183,6 +184,7 @@ bool CefV8ContextCToCpp::Eval(const CefString& code,
|
|||||||
DCHECK(!code.empty());
|
DCHECK(!code.empty());
|
||||||
if (code.empty())
|
if (code.empty())
|
||||||
return false;
|
return false;
|
||||||
|
// Unverified params: script_url
|
||||||
|
|
||||||
// Translate param: retval; type: refptr_same_byref
|
// Translate param: retval; type: refptr_same_byref
|
||||||
cef_v8value_t* retvalStruct = NULL;
|
cef_v8value_t* retvalStruct = NULL;
|
||||||
@ -198,6 +200,8 @@ bool CefV8ContextCToCpp::Eval(const CefString& code,
|
|||||||
// Execute
|
// Execute
|
||||||
int _retval = _struct->eval(_struct,
|
int _retval = _struct->eval(_struct,
|
||||||
code.GetStruct(),
|
code.GetStruct(),
|
||||||
|
script_url.GetStruct(),
|
||||||
|
start_line,
|
||||||
&retvalStruct,
|
&retvalStruct,
|
||||||
&exceptionStruct);
|
&exceptionStruct);
|
||||||
|
|
||||||
|
@ -38,7 +38,8 @@ class CefV8ContextCToCpp
|
|||||||
bool Enter() OVERRIDE;
|
bool Enter() OVERRIDE;
|
||||||
bool Exit() OVERRIDE;
|
bool Exit() OVERRIDE;
|
||||||
bool IsSame(CefRefPtr<CefV8Context> that) OVERRIDE;
|
bool IsSame(CefRefPtr<CefV8Context> that) OVERRIDE;
|
||||||
bool Eval(const CefString& code, CefRefPtr<CefV8Value>& retval,
|
bool Eval(const CefString& code, const CefString& script_url, int start_line,
|
||||||
|
CefRefPtr<CefV8Value>& retval,
|
||||||
CefRefPtr<CefV8Exception>& exception) OVERRIDE;
|
CefRefPtr<CefV8Exception>& exception) OVERRIDE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -291,7 +291,7 @@ PERF_TEST_FUNC(V8ContextEval) {
|
|||||||
CefRefPtr<CefV8Exception> exception;
|
CefRefPtr<CefV8Exception> exception;
|
||||||
|
|
||||||
PERF_ITERATIONS_START()
|
PERF_ITERATIONS_START()
|
||||||
context->Eval(jsCode, retval, exception);
|
context->Eval(jsCode, CefString(), 0, retval, exception);
|
||||||
PERF_ITERATIONS_END()
|
PERF_ITERATIONS_END()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,6 +260,20 @@ void TestHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
|
|||||||
browser_count_--;
|
browser_count_--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
CefResponse::HeaderMap ToCefHeaderMap(
|
||||||
|
const ResourceContent::HeaderMap& headerMap) {
|
||||||
|
CefResponse::HeaderMap result;
|
||||||
|
ResourceContent::HeaderMap::const_iterator it = headerMap.begin();
|
||||||
|
for (; it != headerMap.end(); ++it) {
|
||||||
|
result.insert(std::pair<CefString, CefString>(it->first, it->second));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
CefRefPtr<CefResourceHandler> TestHandler::GetResourceHandler(
|
CefRefPtr<CefResourceHandler> TestHandler::GetResourceHandler(
|
||||||
CefRefPtr<CefBrowser> browser,
|
CefRefPtr<CefBrowser> browser,
|
||||||
CefRefPtr<CefFrame> frame,
|
CefRefPtr<CefFrame> frame,
|
||||||
@ -280,9 +294,10 @@ CefRefPtr<CefResourceHandler> TestHandler::GetResourceHandler(
|
|||||||
// Return the previously mapped resource
|
// Return the previously mapped resource
|
||||||
CefRefPtr<CefStreamReader> stream =
|
CefRefPtr<CefStreamReader> stream =
|
||||||
CefStreamReader::CreateForData(
|
CefStreamReader::CreateForData(
|
||||||
static_cast<void*>(const_cast<char*>(it->second.first.c_str())),
|
static_cast<void*>(const_cast<char*>(it->second.content().c_str())),
|
||||||
it->second.first.length());
|
it->second.content().length());
|
||||||
return new CefStreamResourceHandler(it->second.second, stream);
|
return new CefStreamResourceHandler(200, "OK", it->second.mimeType(),
|
||||||
|
ToCefHeaderMap(it->second.headerMap()), stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,10 +416,25 @@ void TestHandler::CloseBrowser(CefRefPtr<CefBrowser> browser,
|
|||||||
|
|
||||||
void TestHandler::AddResource(const std::string& url,
|
void TestHandler::AddResource(const std::string& url,
|
||||||
const std::string& content,
|
const std::string& content,
|
||||||
const std::string& mimeType) {
|
const std::string& mime_type) {
|
||||||
|
ResourceContent::HeaderMap headerMap = ResourceContent::HeaderMap();
|
||||||
|
ResourceContent rc = ResourceContent(content, mime_type, headerMap);
|
||||||
|
AddResourceEx(url, rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestHandler::AddResource(const std::string& url,
|
||||||
|
const std::string& content,
|
||||||
|
const std::string& mime_type,
|
||||||
|
const ResourceContent::HeaderMap& header_map) {
|
||||||
|
ResourceContent rc = ResourceContent(content, mime_type, header_map);
|
||||||
|
AddResourceEx(url, rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestHandler::AddResourceEx(const std::string& url,
|
||||||
|
const ResourceContent& content) {
|
||||||
if (!CefCurrentlyOn(TID_IO)) {
|
if (!CefCurrentlyOn(TID_IO)) {
|
||||||
CefPostTask(TID_IO,
|
CefPostTask(TID_IO,
|
||||||
base::Bind(&TestHandler::AddResource, this, url, content, mimeType));
|
base::Bind(&TestHandler::AddResourceEx, this, url, content));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,8 +444,7 @@ void TestHandler::AddResource(const std::string& url,
|
|||||||
if (idx > 0)
|
if (idx > 0)
|
||||||
urlStr = urlStr.substr(0, idx);
|
urlStr = urlStr.substr(0, idx);
|
||||||
|
|
||||||
resource_map_.insert(
|
resource_map_.insert(std::make_pair(urlStr, content));
|
||||||
std::make_pair(urlStr, std::make_pair(content, mimeType)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestHandler::ClearResources() {
|
void TestHandler::ClearResources() {
|
||||||
|
@ -32,6 +32,28 @@ class TrackCallback {
|
|||||||
bool gotit_;
|
bool gotit_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ResourceContent {
|
||||||
|
public:
|
||||||
|
typedef std::multimap<std::string, std::string> HeaderMap;
|
||||||
|
|
||||||
|
ResourceContent(const std::string& content,
|
||||||
|
const std::string& mime_type,
|
||||||
|
const HeaderMap& header_map)
|
||||||
|
: content_(content),
|
||||||
|
mime_type_(mime_type),
|
||||||
|
header_map_(header_map)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const std::string& content() const { return content_; }
|
||||||
|
const std::string& mimeType() const { return mime_type_; }
|
||||||
|
const HeaderMap& headerMap() const { return header_map_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string content_;
|
||||||
|
std::string mime_type_;
|
||||||
|
HeaderMap header_map_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Base implementation of CefClient for unit tests. Add new interfaces as needed
|
// Base implementation of CefClient for unit tests. Add new interfaces as needed
|
||||||
// by test cases.
|
// by test cases.
|
||||||
@ -235,7 +257,15 @@ class TestHandler : public CefClient,
|
|||||||
|
|
||||||
void AddResource(const std::string& url,
|
void AddResource(const std::string& url,
|
||||||
const std::string& content,
|
const std::string& content,
|
||||||
const std::string& mimeType);
|
const std::string& mime_type);
|
||||||
|
|
||||||
|
void AddResource(const std::string& url,
|
||||||
|
const std::string& content,
|
||||||
|
const std::string& mime_type,
|
||||||
|
const ResourceContent::HeaderMap& header_map);
|
||||||
|
|
||||||
|
void AddResourceEx(const std::string& url, const ResourceContent& content);
|
||||||
|
|
||||||
void ClearResources();
|
void ClearResources();
|
||||||
|
|
||||||
void SetSignalCompletionWhenAllBrowsersClose(bool val) {
|
void SetSignalCompletionWhenAllBrowsersClose(bool val) {
|
||||||
@ -274,8 +304,7 @@ class TestHandler : public CefClient,
|
|||||||
|
|
||||||
// Map of resources that can be automatically loaded. Only accessed on the
|
// Map of resources that can be automatically loaded. Only accessed on the
|
||||||
// IO thread.
|
// IO thread.
|
||||||
typedef std::map<std::string, std::pair<std::string, std::string> >
|
typedef std::map<std::string, ResourceContent > ResourceMap;
|
||||||
ResourceMap;
|
|
||||||
ResourceMap resource_map_;
|
ResourceMap resource_map_;
|
||||||
|
|
||||||
// If true test completion will be signaled when all browsers have closed.
|
// If true test completion will be signaled when all browsers have closed.
|
||||||
|
@ -31,6 +31,10 @@ const char kV8BindingTestUrl[] = "http://tests/V8Test.BindingTest";
|
|||||||
const char kV8ContextParentTestUrl[] = "http://tests/V8Test.ContextParentTest";
|
const char kV8ContextParentTestUrl[] = "http://tests/V8Test.ContextParentTest";
|
||||||
const char kV8ContextChildTestUrl[] = "http://tests/V8Test.ContextChildTest";
|
const char kV8ContextChildTestUrl[] = "http://tests/V8Test.ContextChildTest";
|
||||||
const char kV8NavTestUrl[] = "http://tests/V8Test.NavTest";
|
const char kV8NavTestUrl[] = "http://tests/V8Test.NavTest";
|
||||||
|
const char kV8ContextEvalCspBypassUnsafeEval[] =
|
||||||
|
"http://tests/V8Test.ContextEvalCspBypassUnsafeEval";
|
||||||
|
const char kV8ContextEvalCspBypassSandbox[] =
|
||||||
|
"http://tests/V8Test.ContextEvalCspBypassSandbox";
|
||||||
const char kV8OnUncaughtExceptionTestUrl[] =
|
const char kV8OnUncaughtExceptionTestUrl[] =
|
||||||
"http://tests/V8Test.OnUncaughtException";
|
"http://tests/V8Test.OnUncaughtException";
|
||||||
const char kV8TestMsg[] = "V8Test.Test";
|
const char kV8TestMsg[] = "V8Test.Test";
|
||||||
@ -71,6 +75,8 @@ enum V8TestMode {
|
|||||||
V8TEST_FUNCTION_HANDLER_EMPTY_STRING,
|
V8TEST_FUNCTION_HANDLER_EMPTY_STRING,
|
||||||
V8TEST_CONTEXT_EVAL,
|
V8TEST_CONTEXT_EVAL,
|
||||||
V8TEST_CONTEXT_EVAL_EXCEPTION,
|
V8TEST_CONTEXT_EVAL_EXCEPTION,
|
||||||
|
V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL,
|
||||||
|
V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX,
|
||||||
V8TEST_CONTEXT_ENTERED,
|
V8TEST_CONTEXT_ENTERED,
|
||||||
V8TEST_CONTEXT_INVALID,
|
V8TEST_CONTEXT_INVALID,
|
||||||
V8TEST_BINDING,
|
V8TEST_BINDING,
|
||||||
@ -213,6 +219,12 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
case V8TEST_CONTEXT_EVAL_EXCEPTION:
|
case V8TEST_CONTEXT_EVAL_EXCEPTION:
|
||||||
RunContextEvalExceptionTest();
|
RunContextEvalExceptionTest();
|
||||||
break;
|
break;
|
||||||
|
case V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL:
|
||||||
|
RunContextEvalCspBypassUnsafeEval();
|
||||||
|
break;
|
||||||
|
case V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX:
|
||||||
|
RunContextEvalCspBypassSandbox();
|
||||||
|
break;
|
||||||
case V8TEST_CONTEXT_ENTERED:
|
case V8TEST_CONTEXT_ENTERED:
|
||||||
RunContextEnteredTest();
|
RunContextEnteredTest();
|
||||||
break;
|
break;
|
||||||
@ -890,7 +902,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
CefRefPtr<CefV8Value> retval;
|
CefRefPtr<CefV8Value> retval;
|
||||||
CefRefPtr<CefV8Exception> exception;
|
CefRefPtr<CefV8Exception> exception;
|
||||||
|
|
||||||
EXPECT_TRUE(context->Eval(test.str(), retval, exception));
|
EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
|
||||||
if (exception.get())
|
if (exception.get())
|
||||||
ADD_FAILURE() << exception->GetMessage().c_str();
|
ADD_FAILURE() << exception->GetMessage().c_str();
|
||||||
|
|
||||||
@ -929,7 +941,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
CefRefPtr<CefV8Value> retval;
|
CefRefPtr<CefV8Value> retval;
|
||||||
CefRefPtr<CefV8Exception> exception;
|
CefRefPtr<CefV8Exception> exception;
|
||||||
|
|
||||||
EXPECT_TRUE(context->Eval(test.str(), retval, exception));
|
EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
|
||||||
if (exception.get())
|
if (exception.get())
|
||||||
ADD_FAILURE() << exception->GetMessage().c_str();
|
ADD_FAILURE() << exception->GetMessage().c_str();
|
||||||
|
|
||||||
@ -971,7 +983,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
CefRefPtr<CefV8Value> retval;
|
CefRefPtr<CefV8Value> retval;
|
||||||
CefRefPtr<CefV8Exception> exception;
|
CefRefPtr<CefV8Exception> exception;
|
||||||
|
|
||||||
EXPECT_TRUE(context->Eval(test.str(), retval, exception));
|
EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
|
||||||
if (exception.get())
|
if (exception.get())
|
||||||
ADD_FAILURE() << exception->GetMessage().c_str();
|
ADD_FAILURE() << exception->GetMessage().c_str();
|
||||||
|
|
||||||
@ -1013,7 +1025,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
CefRefPtr<CefV8Value> retval;
|
CefRefPtr<CefV8Value> retval;
|
||||||
CefRefPtr<CefV8Exception> exception;
|
CefRefPtr<CefV8Exception> exception;
|
||||||
|
|
||||||
EXPECT_TRUE(context->Eval(test.str(), retval, exception));
|
EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
|
||||||
if (exception.get())
|
if (exception.get())
|
||||||
ADD_FAILURE() << exception->GetMessage().c_str();
|
ADD_FAILURE() << exception->GetMessage().c_str();
|
||||||
|
|
||||||
@ -1053,7 +1065,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
CefRefPtr<CefV8Value> retval;
|
CefRefPtr<CefV8Value> retval;
|
||||||
CefRefPtr<CefV8Exception> exception;
|
CefRefPtr<CefV8Exception> exception;
|
||||||
|
|
||||||
EXPECT_TRUE(context->Eval(test.str(), retval, exception));
|
EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
|
||||||
if (exception.get())
|
if (exception.get())
|
||||||
ADD_FAILURE() << exception->GetMessage().c_str();
|
ADD_FAILURE() << exception->GetMessage().c_str();
|
||||||
|
|
||||||
@ -1093,7 +1105,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
CefRefPtr<CefV8Value> retval;
|
CefRefPtr<CefV8Value> retval;
|
||||||
CefRefPtr<CefV8Exception> exception;
|
CefRefPtr<CefV8Exception> exception;
|
||||||
|
|
||||||
EXPECT_TRUE(context->Eval(test.str(), retval, exception));
|
EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception));
|
||||||
if (exception.get())
|
if (exception.get())
|
||||||
ADD_FAILURE() << exception->GetMessage().c_str();
|
ADD_FAILURE() << exception->GetMessage().c_str();
|
||||||
|
|
||||||
@ -1514,7 +1526,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
CefRefPtr<CefV8Value> retval;
|
CefRefPtr<CefV8Value> retval;
|
||||||
CefRefPtr<CefV8Exception> exception;
|
CefRefPtr<CefV8Exception> exception;
|
||||||
|
|
||||||
EXPECT_TRUE(context->Eval("1+2", retval, exception));
|
EXPECT_TRUE(context->Eval("1+2", CefString(), 0, retval, exception));
|
||||||
EXPECT_TRUE(retval.get());
|
EXPECT_TRUE(retval.get());
|
||||||
EXPECT_TRUE(retval->IsInt());
|
EXPECT_TRUE(retval->IsInt());
|
||||||
EXPECT_EQ(3, retval->GetIntValue());
|
EXPECT_EQ(3, retval->GetIntValue());
|
||||||
@ -1529,9 +1541,58 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
CefRefPtr<CefV8Value> retval;
|
CefRefPtr<CefV8Value> retval;
|
||||||
CefRefPtr<CefV8Exception> exception;
|
CefRefPtr<CefV8Exception> exception;
|
||||||
|
|
||||||
EXPECT_FALSE(context->Eval("1+foo", retval, exception));
|
EXPECT_FALSE(context->Eval("\n\n\n1+foo", CefString(), 0, retval, exception));
|
||||||
EXPECT_FALSE(retval.get());
|
EXPECT_FALSE(retval.get());
|
||||||
EXPECT_TRUE(exception.get());
|
EXPECT_TRUE(exception.get());
|
||||||
|
EXPECT_EQ(4, exception->GetLineNumber());
|
||||||
|
|
||||||
|
DestroyTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunContextEvalCspBypassUnsafeEval() {
|
||||||
|
CefRefPtr<CefV8Context> context = GetContext();
|
||||||
|
|
||||||
|
CefRefPtr<CefV8Value> retval;
|
||||||
|
CefRefPtr<CefV8Exception> exception;
|
||||||
|
|
||||||
|
bool success = context->Eval(
|
||||||
|
"(document.getElementById('result').innerHTML)",
|
||||||
|
CefString(), 0, retval, exception);
|
||||||
|
if (exception.get()) {
|
||||||
|
ADD_FAILURE() << exception->GetMessage().c_str();
|
||||||
|
EXPECT_FALSE(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(success);
|
||||||
|
EXPECT_TRUE(retval.get());
|
||||||
|
if (retval.get()) {
|
||||||
|
EXPECT_TRUE(retval->IsString());
|
||||||
|
EXPECT_EQ(CefString("CSP_BYPASSED"), retval->GetStringValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
DestroyTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunContextEvalCspBypassSandbox() {
|
||||||
|
CefRefPtr<CefV8Context> context = GetContext();
|
||||||
|
|
||||||
|
CefRefPtr<CefV8Value> retval;
|
||||||
|
CefRefPtr<CefV8Exception> exception;
|
||||||
|
|
||||||
|
bool success = context->Eval(
|
||||||
|
"(document.getElementById('result').innerHTML)",
|
||||||
|
CefString(), 0, retval, exception);
|
||||||
|
if (exception.get()) {
|
||||||
|
ADD_FAILURE() << exception->GetMessage().c_str();
|
||||||
|
EXPECT_FALSE(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(success);
|
||||||
|
EXPECT_TRUE(retval.get());
|
||||||
|
if (retval.get()) {
|
||||||
|
EXPECT_TRUE(retval->IsString());
|
||||||
|
EXPECT_EQ(CefString("CSP_BYPASSED"), retval->GetStringValue());
|
||||||
|
}
|
||||||
|
|
||||||
DestroyTest();
|
DestroyTest();
|
||||||
}
|
}
|
||||||
@ -1545,7 +1606,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
// Test value defined in OnContextCreated
|
// Test value defined in OnContextCreated
|
||||||
EXPECT_TRUE(context->Eval(
|
EXPECT_TRUE(context->Eval(
|
||||||
"document.getElementById('f').contentWindow.v8_context_entered_test()",
|
"document.getElementById('f').contentWindow.v8_context_entered_test()",
|
||||||
retval, exception));
|
CefString(), 0, retval, exception));
|
||||||
if (exception.get())
|
if (exception.get())
|
||||||
ADD_FAILURE() << exception->GetMessage().c_str();
|
ADD_FAILURE() << exception->GetMessage().c_str();
|
||||||
|
|
||||||
@ -1624,7 +1685,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
EXPECT_TRUE(context->Eval(
|
EXPECT_TRUE(context->Eval(
|
||||||
"function jsfunc() { return window.myfunc(); }\n"
|
"function jsfunc() { return window.myfunc(); }\n"
|
||||||
"jsfunc();",
|
"jsfunc();",
|
||||||
retval, exception));
|
CefString(), 0, retval, exception));
|
||||||
EXPECT_TRUE(retval.get());
|
EXPECT_TRUE(retval.get());
|
||||||
EXPECT_TRUE(retval->IsInt());
|
EXPECT_TRUE(retval->IsInt());
|
||||||
EXPECT_EQ(3, retval->GetIntValue());
|
EXPECT_EQ(3, retval->GetIntValue());
|
||||||
@ -1642,7 +1703,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
EXPECT_EQ(1, frame->GetLineNumber());
|
EXPECT_EQ(1, frame->GetLineNumber());
|
||||||
EXPECT_EQ(35, frame->GetColumn());
|
EXPECT_EQ(35, frame->GetColumn());
|
||||||
EXPECT_TRUE(frame.get());
|
EXPECT_TRUE(frame.get());
|
||||||
EXPECT_TRUE(frame->IsEval());
|
EXPECT_FALSE(frame->IsEval());
|
||||||
EXPECT_FALSE(frame->IsConstructor());
|
EXPECT_FALSE(frame->IsConstructor());
|
||||||
|
|
||||||
frame = handler->stack_trace_->GetFrame(1);
|
frame = handler->stack_trace_->GetFrame(1);
|
||||||
@ -1652,7 +1713,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
EXPECT_EQ(2, frame->GetLineNumber());
|
EXPECT_EQ(2, frame->GetLineNumber());
|
||||||
EXPECT_EQ(1, frame->GetColumn());
|
EXPECT_EQ(1, frame->GetColumn());
|
||||||
EXPECT_TRUE(frame.get());
|
EXPECT_TRUE(frame.get());
|
||||||
EXPECT_TRUE(frame->IsEval());
|
EXPECT_FALSE(frame->IsEval());
|
||||||
EXPECT_FALSE(frame->IsConstructor());
|
EXPECT_FALSE(frame->IsConstructor());
|
||||||
|
|
||||||
// Exit the V8 context.
|
// Exit the V8 context.
|
||||||
@ -1711,6 +1772,10 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
}
|
}
|
||||||
if (test_mode_ > V8TEST_NONE)
|
if (test_mode_ > V8TEST_NONE)
|
||||||
RunStartupTest();
|
RunStartupTest();
|
||||||
|
if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL ||
|
||||||
|
test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX) {
|
||||||
|
browser_ = browser;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CefRefPtr<CefLoadHandler> GetLoadHandler(
|
CefRefPtr<CefLoadHandler> GetLoadHandler(
|
||||||
@ -1930,7 +1995,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
|||||||
CefRefPtr<CefV8Value> retval;
|
CefRefPtr<CefV8Value> retval;
|
||||||
CefRefPtr<CefV8Exception> exception;
|
CefRefPtr<CefV8Exception> exception;
|
||||||
EXPECT_TRUE(browser->GetMainFrame()->GetV8Context()->Eval(
|
EXPECT_TRUE(browser->GetMainFrame()->GetV8Context()->Eval(
|
||||||
"window.close()", retval, exception));
|
"window.close()", CefString(), 0, retval, exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -1990,7 +2055,27 @@ class V8TestHandler : public TestHandler {
|
|||||||
|
|
||||||
void RunTest() override {
|
void RunTest() override {
|
||||||
// Nested script tag forces creation of the V8 context.
|
// Nested script tag forces creation of the V8 context.
|
||||||
if (test_mode_ == V8TEST_CONTEXT_ENTERED) {
|
if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL ||
|
||||||
|
test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX) {
|
||||||
|
std::string url;
|
||||||
|
ResourceContent::HeaderMap headers;
|
||||||
|
if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL) {
|
||||||
|
url = kV8ContextEvalCspBypassUnsafeEval;
|
||||||
|
headers.insert(std::pair<std::string, std::string>(
|
||||||
|
"Content-Security-Policy", "script-src 'self'"));
|
||||||
|
} else if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX) {
|
||||||
|
url = kV8ContextEvalCspBypassSandbox;
|
||||||
|
headers.insert(std::pair<std::string, std::string>(
|
||||||
|
"Content-Security-Policy", "sandbox"));
|
||||||
|
} else {
|
||||||
|
NOTREACHED();
|
||||||
|
}
|
||||||
|
AddResource(url, "<html><body>"
|
||||||
|
+ url +
|
||||||
|
"<p id='result' style='display:none'>CSP_BYPASSED</p>"
|
||||||
|
"</body></html>", "text/html", headers);
|
||||||
|
CreateBrowser(test_url_);
|
||||||
|
} else if (test_mode_ == V8TEST_CONTEXT_ENTERED) {
|
||||||
AddResource(kV8ContextParentTestUrl, "<html><body>"
|
AddResource(kV8ContextParentTestUrl, "<html><body>"
|
||||||
"<script>var i = 0;</script><iframe src=\"" +
|
"<script>var i = 0;</script><iframe src=\"" +
|
||||||
std::string(kV8ContextChildTestUrl) + "\" id=\"f\"></iframe></body>"
|
std::string(kV8ContextChildTestUrl) + "\" id=\"f\"></iframe></body>"
|
||||||
@ -2161,6 +2246,12 @@ V8_TEST(FunctionHandlerWithContext, V8TEST_FUNCTION_HANDLER_WITH_CONTEXT);
|
|||||||
V8_TEST(FunctionHandlerEmptyString, V8TEST_FUNCTION_HANDLER_EMPTY_STRING);
|
V8_TEST(FunctionHandlerEmptyString, V8TEST_FUNCTION_HANDLER_EMPTY_STRING);
|
||||||
V8_TEST(ContextEval, V8TEST_CONTEXT_EVAL);
|
V8_TEST(ContextEval, V8TEST_CONTEXT_EVAL);
|
||||||
V8_TEST(ContextEvalException, V8TEST_CONTEXT_EVAL_EXCEPTION);
|
V8_TEST(ContextEvalException, V8TEST_CONTEXT_EVAL_EXCEPTION);
|
||||||
|
V8_TEST_EX(ContextEvalCspBypassUnsafeEval,
|
||||||
|
V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL,
|
||||||
|
kV8ContextEvalCspBypassUnsafeEval);
|
||||||
|
V8_TEST_EX(ContextEvalCspBypassSandbox,
|
||||||
|
V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX,
|
||||||
|
kV8ContextEvalCspBypassSandbox);
|
||||||
V8_TEST_EX(ContextEntered, V8TEST_CONTEXT_ENTERED, NULL);
|
V8_TEST_EX(ContextEntered, V8TEST_CONTEXT_ENTERED, NULL);
|
||||||
V8_TEST(ContextInvalid, V8TEST_CONTEXT_INVALID);
|
V8_TEST(ContextInvalid, V8TEST_CONTEXT_INVALID);
|
||||||
V8_TEST_EX(Binding, V8TEST_BINDING, kV8BindingTestUrl);
|
V8_TEST_EX(Binding, V8TEST_BINDING, kV8BindingTestUrl);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user