#include "config.h"
#include "V8Proxy.h"
#include "ChromiumBridge.h"
#include "CSSMutableStyleDeclaration.h"
#include "DOMObjectsInclude.h"
#include "DocumentLoader.h"
#include "FrameLoaderClient.h"
#include "ScriptController.h"
#include "V8Binding.h"
#include "V8Collection.h"
#include "V8CustomBinding.h"
#include "V8DOMMap.h"
#include "V8DOMWindow.h"
#include "V8Index.h"
#include "V8IsolatedWorld.h"
#include "WorkerContextExecutionProxy.h"
#include <algorithm>
#include <utility>
#include <v8.h>
#include <v8-debug.h>
#include <wtf/Assertions.h>
#include <wtf/OwnArrayPtr.h>
#include <wtf/StdLibExtras.h>
#include <wtf/UnusedParam.h>
namespace WebCore {
v8::Persistent<v8::Context> V8Proxy::m_utilityContext;
V8ExtensionList V8Proxy::m_extensions;
const char* V8Proxy::kContextDebugDataType = "type";
const char* V8Proxy::kContextDebugDataValue = "value";
void batchConfigureAttributes(v8::Handle<v8::ObjectTemplate> instance, v8::Handle<v8::ObjectTemplate> proto, const BatchedAttribute* attributes, size_t attributeCount)
{
for (size_t i = 0; i < attributeCount; ++i) {
const BatchedAttribute* attribute = &attributes[i];
(attribute->onProto ? proto : instance)->SetAccessor(v8::String::New(attribute->name),
attribute->getter,
attribute->setter,
attribute->data == V8ClassIndex::INVALID_CLASS_INDEX ? v8::Handle<v8::Value>() : v8::Integer::New(V8ClassIndex::ToInt(attribute->data)),
attribute->settings,
attribute->attribute);
}
}
void batchConfigureConstants(v8::Handle<v8::FunctionTemplate> functionDescriptor, v8::Handle<v8::ObjectTemplate> proto, const BatchedConstant* constants, size_t constantCount)
{
for (size_t i = 0; i < constantCount; ++i) {
const BatchedConstant* constant = &constants[i];
functionDescriptor->Set(v8::String::New(constant->name), v8::Integer::New(constant->value), v8::ReadOnly);
proto->Set(v8::String::New(constant->name), v8::Integer::New(constant->value), v8::ReadOnly);
}
}
typedef HashMap<Node*, v8::Object*> DOMNodeMap;
typedef HashMap<void*, v8::Object*> DOMObjectMap;
#if ENABLE(SVG)
static HashMap<void*, SVGElement*>& svgObjectToContextMap()
{
typedef HashMap<void*, SVGElement*> SvgObjectToContextMap;
DEFINE_STATIC_LOCAL(SvgObjectToContextMap, staticSvgObjectToContextMap, ());
return staticSvgObjectToContextMap;
}
void V8Proxy::setSVGContext(void* object, SVGElement* context)
{
if (!object)
return;
SVGElement* oldContext = svgObjectToContextMap().get(object);
if (oldContext == context)
return;
if (oldContext)
oldContext->deref();
if (context)
context->ref();
svgObjectToContextMap().set(object, context);
}
SVGElement* V8Proxy::svgContext(void* object)
{
return svgObjectToContextMap().get(object);
}
#endif
typedef HashMap<int, v8::FunctionTemplate*> FunctionTemplateMap;
bool AllowAllocation::m_current = false;
class JavaScriptConsoleMessage {
public:
JavaScriptConsoleMessage(const String& string, const String& sourceID, unsigned lineNumber)
: m_string(string)
, m_sourceID(sourceID)
, m_lineNumber(lineNumber)
{
}
void addToPage(Page*) const;
private:
const String m_string;
const String m_sourceID;
const unsigned m_lineNumber;
};
void JavaScriptConsoleMessage::addToPage(Page* page) const
{
ASSERT(page);
Console* console = page->mainFrame()->domWindow()->console();
console->addMessage(JSMessageSource, ErrorMessageLevel, m_string, m_lineNumber, m_sourceID);
}
class ConsoleMessageManager {
public:
static void addMessage(Page*, const JavaScriptConsoleMessage&);
static void addDelayedMessage(const JavaScriptConsoleMessage&);
static void processDelayedMessages();
private:
static Vector<JavaScriptConsoleMessage>* m_delayed;
};
Vector<JavaScriptConsoleMessage>* ConsoleMessageManager::m_delayed = 0;
void ConsoleMessageManager::addMessage(Page* page, const JavaScriptConsoleMessage& message)
{
processDelayedMessages();
message.addToPage(page);
}
void ConsoleMessageManager::addDelayedMessage(const JavaScriptConsoleMessage& message)
{
if (!m_delayed) {
m_delayed = new Vector<JavaScriptConsoleMessage>();
}
m_delayed->append(message);
}
void ConsoleMessageManager::processDelayedMessages()
{
if (!m_delayed)
return;
ASSERT(!m_delayed->isEmpty());
Frame* frame = V8Proxy::retrieveFrameForEnteredContext();
Page* page = 0;
if (frame)
page = frame->page();
if (!page)
m_delayed->clear();
const int size = m_delayed->size();
for (int i = 0; i < size; i++)
m_delayed->at(i).addToPage(page);
delete m_delayed;
m_delayed = 0;
}
class ConsoleMessageScope {
public:
ConsoleMessageScope() { ConsoleMessageManager::processDelayedMessages(); }
~ConsoleMessageScope() { ConsoleMessageManager::processDelayedMessages(); }
};
void logInfo(Frame* frame, const String& message, const String& url)
{
Page* page = frame->page();
if (!page)
return;
JavaScriptConsoleMessage consoleMessage(message, url, 0);
ConsoleMessageManager::addMessage(page, consoleMessage);
}
static void handleConsoleMessage(v8::Handle<v8::Message> message, v8::Handle<v8::Value> data)
{
Frame* frame = V8Proxy::retrieveFrameForEnteredContext();
if (!frame)
return;
Page* page = frame->page();
if (!page)
return;
v8::Handle<v8::String> errorMessageString = message->Get();
ASSERT(!errorMessageString.IsEmpty());
String errorMessage = toWebCoreString(errorMessageString);
v8::Handle<v8::Value> resourceName = message->GetScriptResourceName();
bool useURL = resourceName.IsEmpty() || !resourceName->IsString();
String resourceNameString = useURL ? frame->document()->url() : toWebCoreString(resourceName);
JavaScriptConsoleMessage consoleMessage(errorMessage, resourceNameString, message->GetLineNumber());
ConsoleMessageManager::addMessage(page, consoleMessage);
}
enum DelayReporting {
ReportLater,
ReportNow
};
static void reportUnsafeAccessTo(Frame* target, DelayReporting delay)
{
ASSERT(target);
Document* targetDocument = target->document();
if (!targetDocument)
return;
Frame* source = V8Proxy::retrieveFrameForEnteredContext();
if (!source || !source->document())
return;
Document* sourceDocument = source->document();
String str = String::format("Unsafe JavaScript attempt to access frame "
"with URL %s from frame with URL %s. "
"Domains, protocols and ports must match.\n",
targetDocument->url().string().utf8().data(),
sourceDocument->url().string().utf8().data());
const String kSourceID = "";
const int kLineNumber = 1;
JavaScriptConsoleMessage message(str, kSourceID, kLineNumber);
if (delay == ReportNow) {
ConsoleMessageManager::addMessage(source->page(), message);
} else {
ASSERT(delay == ReportLater);
ConsoleMessageManager::addDelayedMessage(message);
}
}
static void reportUnsafeJavaScriptAccess(v8::Local<v8::Object> host, v8::AccessType type, v8::Local<v8::Value> data)
{
Frame* target = V8Custom::GetTargetFrame(host, data);
if (target)
reportUnsafeAccessTo(target, ReportLater);
}
static void handleFatalErrorInV8()
{
CRASH();
}
static void reportFatalErrorInV8(const char* location, const char* message)
{
printf("V8 error: %s (%s)\n", message, location);
handleFatalErrorInV8();
}
V8Proxy::~V8Proxy()
{
clearForClose();
destroyGlobal();
}
void V8Proxy::destroyGlobal()
{
if (!m_global.IsEmpty()) {
#ifndef NDEBUG
V8GCController::unregisterGlobalHandle(this, m_global);
#endif
m_global.Dispose();
m_global.Clear();
}
}
static V8EventListener* findEventListenerInList(V8EventListenerList& list, v8::Local<v8::Value> listener, bool isInline)
{
ASSERT(v8::Context::InContext());
if (!listener->IsObject())
return 0;
return list.find(listener->ToObject(), isInline);
}
PassRefPtr<V8EventListener> V8Proxy::findV8EventListener(v8::Local<v8::Value> listener, bool isInline)
{
return findEventListenerInList(m_eventListeners, listener, isInline);
}
PassRefPtr<V8EventListener> V8Proxy::findOrCreateV8EventListener(v8::Local<v8::Value> object, bool isInline)
{
ASSERT(v8::Context::InContext());
if (!object->IsObject())
return 0;
V8EventListener* wrapper = findEventListenerInList(m_eventListeners, object, isInline);
if (wrapper)
return wrapper;
RefPtr<V8EventListener> newListener = V8EventListener::create(m_frame, v8::Local<v8::Object>::Cast(object), isInline);
m_eventListeners.add(newListener.get());
return newListener;
}
PassRefPtr<V8EventListener> V8Proxy::findObjectEventListener( v8::Local<v8::Value> listener, bool isInline)
{
return findEventListenerInList(m_xhrListeners, listener, isInline);
}
PassRefPtr<V8EventListener> V8Proxy::findOrCreateObjectEventListener(v8::Local<v8::Value> object, bool isInline)
{
ASSERT(v8::Context::InContext());
if (!object->IsObject())
return 0;
V8EventListener* wrapper = findEventListenerInList(m_xhrListeners, object, isInline);
if (wrapper)
return wrapper;
RefPtr<V8EventListener> newListener = V8ObjectEventListener::create(m_frame, v8::Local<v8::Object>::Cast(object), isInline);
m_xhrListeners.add(newListener.get());
return newListener.release();
}
static void removeEventListenerFromList(V8EventListenerList& list, V8EventListener* listener)
{
list.remove(listener);
}
void V8Proxy::removeV8EventListener(V8EventListener* listener)
{
removeEventListenerFromList(m_eventListeners, listener);
}
void V8Proxy::removeObjectEventListener(V8ObjectEventListener* listener)
{
removeEventListenerFromList(m_xhrListeners, listener);
}
static void disconnectEventListenersInList(V8EventListenerList& list)
{
V8EventListenerList::iterator it = list.begin();
while (it != list.end()) {
(*it)->disconnectFrame();
++it;
}
list.clear();
}
void V8Proxy::disconnectEventListeners()
{
disconnectEventListenersInList(m_eventListeners);
disconnectEventListenersInList(m_xhrListeners);
}
v8::Handle<v8::Script> V8Proxy::compileScript(v8::Handle<v8::String> code, const String& fileName, int baseLine)
{
const uint16_t* fileNameString = fromWebCoreString(fileName);
v8::Handle<v8::String> name = v8::String::New(fileNameString, fileName.length());
v8::Handle<v8::Integer> line = v8::Integer::New(baseLine);
v8::ScriptOrigin origin(name, line);
v8::Handle<v8::Script> script = v8::Script::Compile(code, &origin);
return script;
}
bool V8Proxy::handleOutOfMemory()
{
v8::Local<v8::Context> context = v8::Context::GetCurrent();
if (!context->HasOutOfMemoryException())
return false;
Frame* frame = V8Proxy::retrieveFrame(context);
V8Proxy* proxy = V8Proxy::retrieve(frame);
if (proxy) {
proxy->clearForClose();
proxy->destroyGlobal();
}
ChromiumBridge::notifyJSOutOfMemory(frame);
Settings* settings = frame->settings();
ASSERT(settings);
settings->setJavaScriptEnabled(false);
return true;
}
void V8Proxy::evaluateInNewWorld(const Vector<ScriptSourceCode>& sources)
{
initContextIfNeeded();
V8IsolatedWorld::evaluate(sources, this);
}
void V8Proxy::evaluateInNewContext(const Vector<ScriptSourceCode>& sources)
{
initContextIfNeeded();
v8::HandleScope handleScope;
v8::Handle<v8::Context> windowContext = m_context;
v8::Handle<v8::Object> windowGlobal = windowContext->Global();
v8::Handle<v8::Value> windowWrapper = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, windowGlobal);
ASSERT(V8DOMWrapper::convertDOMWrapperToNative<DOMWindow>(windowWrapper) == m_frame->domWindow());
v8::Persistent<v8::Context> context = createNewContext(v8::Handle<v8::Object>());
v8::Context::Scope contextScope(context);
v8::Handle<v8::Object> contextData = v8::Object::New();
v8::Handle<v8::Value> windowContextData = windowContext->GetData();
if (windowContextData->IsObject()) {
v8::Handle<v8::String> propertyName = v8::String::New(kContextDebugDataValue);
contextData->Set(propertyName, v8::Object::Cast(*windowContextData)->Get(propertyName));
}
contextData->Set(v8::String::New(kContextDebugDataType), v8::String::New("injected"));
context->SetData(contextData);
v8::Handle<v8::Object> global = context->Global();
v8::Handle<v8::String> implicitProtoString = v8::String::New("__proto__");
global->Set(implicitProtoString, windowWrapper);
global->Set(v8::String::New("contentWindow"), windowGlobal);
for (size_t i = 0; i < sources.size(); ++i)
evaluate(sources[i], 0);
context->UseDefaultSecurityToken();
context.Dispose();
}
v8::Local<v8::Value> V8Proxy::evaluate(const ScriptSourceCode& source, Node* node)
{
ASSERT(v8::Context::InContext());
v8::Local<v8::String> code = v8ExternalString(source.source());
ChromiumBridge::traceEventBegin("v8.compile", node, "");
v8::Handle<v8::Script> script = compileScript(code, source.url(), source.startLine() - 1);
ChromiumBridge::traceEventEnd("v8.compile", node, "");
ChromiumBridge::traceEventBegin("v8.run", node, "");
v8::Local<v8::Value> result;
{
v8::TryCatch tryCatch;
tryCatch.SetVerbose(true);
result = runScript(script, source.url().string().isNull());
}
ChromiumBridge::traceEventEnd("v8.run", node, "");
return result;
}
v8::Local<v8::Value> V8Proxy::runScript(v8::Handle<v8::Script> script, bool isInlineCode)
{
if (script.IsEmpty())
return notHandledByInterceptor();
if (m_recursion >= kMaxRecursionDepth) {
v8::Local<v8::String> code = v8ExternalString("throw RangeError('Recursion too deep')");
script = compileScript(code, "", 0);
}
if (handleOutOfMemory())
ASSERT(script.IsEmpty());
if (script.IsEmpty())
return notHandledByInterceptor();
bool previousInlineCode = inlineCode();
setInlineCode(isInlineCode);
v8::Local<v8::Value> result;
{
ConsoleMessageScope scope;
m_recursion++;
m_frame->keepAlive();
result = script->Run();
m_recursion--;
}
if (handleOutOfMemory())
ASSERT(result.IsEmpty());
if (result.IsEmpty())
return notHandledByInterceptor();
setInlineCode(previousInlineCode);
if (v8::V8::IsDead())
handleFatalErrorInV8();
return result;
}
v8::Local<v8::Value> V8Proxy::callFunction(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
{
v8::Local<v8::Value> result;
{
ConsoleMessageScope scope;
m_frame->keepAlive();
result = function->Call(receiver, argc, args);
}
if (v8::V8::IsDead())
handleFatalErrorInV8();
return result;
}
v8::Local<v8::Value> V8Proxy::newInstance(v8::Handle<v8::Function> constructor, int argc, v8::Handle<v8::Value> args[])
{
v8::Local<v8::Value> result;
{
ConsoleMessageScope scope;
m_frame->keepAlive();
result = constructor->NewInstance(argc, args);
}
if (v8::V8::IsDead())
handleFatalErrorInV8();
return result;
}
v8::Local<v8::Function> V8Proxy::getConstructor(V8ClassIndex::V8WrapperType type)
{
ASSERT(isContextInitialized());
v8::Context::Scope scope(m_context);
v8::Handle<v8::FunctionTemplate> functionTemplate = V8DOMWrapper::getTemplate(type);
v8::TryCatch tryCatch;
v8::Local<v8::Function> value = functionTemplate->GetFunction();
if (value.IsEmpty())
return v8::Local<v8::Function>();
value->Set(v8::String::New("__proto__"), m_objectPrototype);
return value;
}
v8::Local<v8::Object> V8Proxy::createWrapperFromCache(V8ClassIndex::V8WrapperType type)
{
int classIndex = V8ClassIndex::ToInt(type);
v8::Local<v8::Object> clone(m_wrapperBoilerplates->CloneElementAt(classIndex));
if (!clone.IsEmpty())
return clone;
initContextIfNeeded();
v8::Context::Scope scope(m_context);
v8::Local<v8::Function> function = getConstructor(type);
v8::Local<v8::Object> instance = SafeAllocation::newInstance(function);
if (!instance.IsEmpty()) {
m_wrapperBoilerplates->Set(v8::Integer::New(classIndex), instance);
return instance->Clone();
}
return notHandledByInterceptor();
}
bool V8Proxy::isContextInitialized()
{
ASSERT(m_context.IsEmpty() || !m_global.IsEmpty());
ASSERT(m_context.IsEmpty() || !m_objectPrototype.IsEmpty());
ASSERT(m_context.IsEmpty() || !m_wrapperBoilerplates.IsEmpty());
return !m_context.IsEmpty();
}
DOMWindow* V8Proxy::retrieveWindow()
{
return retrieveWindow(v8::Context::GetCurrent());
}
DOMWindow* V8Proxy::retrieveWindow(v8::Handle<v8::Context> context)
{
v8::Handle<v8::Object> global = context->Global();
ASSERT(!global.IsEmpty());
global = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, global);
ASSERT(!global.IsEmpty());
return V8DOMWrapper::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, global);
}
Frame* V8Proxy::retrieveFrame(v8::Handle<v8::Context> context)
{
return retrieveWindow(context)->frame();
}
Frame* V8Proxy::retrieveFrameForEnteredContext()
{
v8::Handle<v8::Context> context = v8::Context::GetEntered();
if (context.IsEmpty())
return 0;
return retrieveFrame(context);
}
Frame* V8Proxy::retrieveFrameForCurrentContext()
{
v8::Handle<v8::Context> context = v8::Context::GetCurrent();
if (context.IsEmpty())
return 0;
return retrieveFrame(context);
}
Frame* V8Proxy::retrieveFrameForCallingContext()
{
v8::Handle<v8::Context> context = v8::Context::GetCalling();
if (context.IsEmpty())
return 0;
return retrieveFrame(context);
}
Frame* V8Proxy::retrieveFrame()
{
DOMWindow* window = retrieveWindow();
return window ? window->frame() : 0;
}
V8Proxy* V8Proxy::retrieve()
{
DOMWindow* window = retrieveWindow();
ASSERT(window);
return retrieve(window->frame());
}
V8Proxy* V8Proxy::retrieve(Frame* frame)
{
if (!frame)
return 0;
return frame->script()->isEnabled() ? frame->script()->proxy() : 0;
}
V8Proxy* V8Proxy::retrieve(ScriptExecutionContext* context)
{
if (!context->isDocument())
return 0;
return retrieve(static_cast<Document*>(context)->frame());
}
void V8Proxy::disconnectFrame()
{
disconnectEventListeners();
}
bool V8Proxy::isEnabled()
{
Settings* settings = m_frame->settings();
if (!settings)
return false;
if (settings->isJavaScriptEnabled())
return true;
Document* document = m_frame->document();
if (!document)
return false;
SecurityOrigin* origin = document->securityOrigin();
if (origin->protocol().isEmpty())
return false;
if (origin->protocol() == "http" || origin->protocol() == "https")
return false;
if (origin->protocol() == ChromiumBridge::uiResourceProtocol())
return true;
const char* kDirProtocols[] = { "ftp", "file" };
for (size_t i = 0; i < arraysize(kDirProtocols); ++i) {
if (origin->protocol() == kDirProtocols[i]) {
const KURL& url = document->url();
return url.pathAfterLastSlash() == url.pathEnd();
}
}
return false; }
void V8Proxy::updateDocumentWrapper(v8::Handle<v8::Value> wrapper)
{
clearDocumentWrapper();
ASSERT(m_document.IsEmpty());
m_document = v8::Persistent<v8::Value>::New(wrapper);
#ifndef NDEBUG
V8GCController::registerGlobalHandle(PROXY, this, m_document);
#endif
}
void V8Proxy::clearDocumentWrapper()
{
if (!m_document.IsEmpty()) {
#ifndef NDEBUG
V8GCController::unregisterGlobalHandle(this, m_document);
#endif
m_document.Dispose();
m_document.Clear();
}
}
void V8Proxy::updateDocumentWrapperCache()
{
v8::HandleScope handleScope;
v8::Context::Scope contextScope(context());
if (!m_frame->document()->frame()) {
clearDocumentWrapperCache();
return;
}
v8::Handle<v8::Value> documentWrapper = V8DOMWrapper::convertNodeToV8Object(m_frame->document());
if (documentWrapper.IsEmpty()) {
clearDocumentWrapperCache();
return;
}
m_context->Global()->ForceSet(v8::String::New("document"), documentWrapper, static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
}
void V8Proxy::clearDocumentWrapperCache()
{
ASSERT(!m_context.IsEmpty());
m_context->Global()->ForceDelete(v8::String::New("document"));
}
void V8Proxy::disposeContextHandles()
{
if (!m_context.IsEmpty()) {
m_frame->loader()->client()->didDestroyScriptContext();
m_context.Dispose();
m_context.Clear();
}
if (!m_wrapperBoilerplates.IsEmpty()) {
#ifndef NDEBUG
V8GCController::unregisterGlobalHandle(this, m_wrapperBoilerplates);
#endif
m_wrapperBoilerplates.Dispose();
m_wrapperBoilerplates.Clear();
}
if (!m_objectPrototype.IsEmpty()) {
#ifndef NDEBUG
V8GCController::unregisterGlobalHandle(this, m_objectPrototype);
#endif
m_objectPrototype.Dispose();
m_objectPrototype.Clear();
}
}
void V8Proxy::clearForClose()
{
if (!m_context.IsEmpty()) {
v8::HandleScope handleScope;
clearDocumentWrapper();
disposeContextHandles();
}
}
void V8Proxy::clearForNavigation()
{
disconnectEventListeners();
if (!m_context.IsEmpty()) {
v8::HandleScope handle;
clearDocumentWrapper();
v8::Context::Scope contextScope(m_context);
clearDocumentWrapperCache();
v8::Handle<v8::Object> wrapper = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, m_global);
ASSERT(!wrapper.IsEmpty());
wrapper->TurnOnAccessCheck();
m_context->DetachGlobal();
disposeContextHandles();
initContextIfNeeded();
}
}
void V8Proxy::setSecurityToken()
{
Document* document = m_frame->document();
if (!document) {
m_context->UseDefaultSecurityToken();
return;
}
SecurityOrigin* origin = document->securityOrigin();
String token;
if (!origin->domainWasSetInDOM())
token = document->securityOrigin()->toString();
if (token.isEmpty() || token == "null") {
m_context->UseDefaultSecurityToken();
return;
}
CString utf8Token = token.utf8();
m_context->SetSecurityToken(v8::String::NewSymbol(utf8Token.data(), utf8Token.length()));
}
void V8Proxy::updateDocument()
{
if (!m_frame->document())
return;
if (m_global.IsEmpty()) {
ASSERT(m_context.IsEmpty());
return;
}
updateDocumentWrapperCache();
updateSecurityOrigin();
}
void V8Proxy::updateSecurityOrigin()
{
v8::HandleScope scope;
setSecurityToken();
}
bool V8Proxy::canAccessPrivate(DOMWindow* targetWindow)
{
ASSERT(targetWindow);
String message;
DOMWindow* originWindow = retrieveWindow();
if (originWindow == targetWindow)
return true;
if (!originWindow)
return false;
const SecurityOrigin* activeSecurityOrigin = originWindow->securityOrigin();
const SecurityOrigin* targetSecurityOrigin = targetWindow->securityOrigin();
if (!targetSecurityOrigin)
return false;
if (activeSecurityOrigin->canAccess(targetSecurityOrigin))
return true;
if (targetSecurityOrigin->isEmpty() && originWindow->frame() == targetWindow->frame())
return true;
return false;
}
bool V8Proxy::canAccessFrame(Frame* target, bool reportError)
{
if (!target)
return false;
if (!canAccessPrivate(target->domWindow())) {
if (reportError)
reportUnsafeAccessTo(target, ReportNow);
return false;
}
return true;
}
bool V8Proxy::checkNodeSecurity(Node* node)
{
if (!node)
return false;
Frame* target = node->document()->frame();
if (!target)
return false;
return canAccessFrame(target, true);
}
v8::Persistent<v8::Context> V8Proxy::createNewContext(v8::Handle<v8::Object> global)
{
v8::Persistent<v8::Context> result;
v8::Persistent<v8::ObjectTemplate> globalTemplate = V8DOMWindow::GetShadowObjectTemplate();
if (globalTemplate.IsEmpty())
return result;
globalTemplate->SetAccessCheckCallbacks(V8Custom::v8DOMWindowNamedSecurityCheck, V8Custom::v8DOMWindowIndexedSecurityCheck, v8::Integer::New(V8ClassIndex::DOMWINDOW));
OwnArrayPtr<const char*> extensionNames(new const char*[m_extensions.size()]);
int index = 0;
for (V8ExtensionList::iterator it = m_extensions.begin(); it != m_extensions.end(); ++it) {
if (it->scheme.length() > 0 && (it->scheme != m_frame->loader()->activeDocumentLoader()->url().protocol() || it->scheme != m_frame->page()->mainFrame()->loader()->activeDocumentLoader()->url().protocol()))
continue;
extensionNames[index++] = it->extension->name();
}
v8::ExtensionConfiguration extensions(index, extensionNames.get());
result = v8::Context::New(&extensions, globalTemplate, global);
return result;
}
bool V8Proxy::installDOMWindow(v8::Handle<v8::Context> context, DOMWindow* window)
{
v8::Handle<v8::String> implicitProtoString = v8::String::New("__proto__");
if (implicitProtoString.IsEmpty())
return false;
v8::Handle<v8::Function> windowConstructor = getConstructor(V8ClassIndex::DOMWINDOW);
v8::Local<v8::Object> jsWindow = SafeAllocation::newInstance(windowConstructor);
if (jsWindow.IsEmpty())
return false;
V8DOMWrapper::setDOMWrapper(jsWindow, V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), window);
window->ref();
V8DOMWrapper::setJSWrapperForDOMObject(window, v8::Persistent<v8::Object>::New(jsWindow));
v8::Handle<v8::Object> v8Global = context->Global();
v8Global->Set(implicitProtoString, jsWindow);
return true;
}
void V8Proxy::initContextIfNeeded()
{
if (!m_context.IsEmpty())
return;
v8::HandleScope handleScope;
static bool isV8Initialized = false;
if (!isV8Initialized) {
v8::V8::IgnoreOutOfMemoryException();
v8::V8::SetFatalErrorHandler(reportFatalErrorInV8);
v8::V8::SetGlobalGCPrologueCallback(&V8GCController::gcPrologue);
v8::V8::SetGlobalGCEpilogueCallback(&V8GCController::gcEpilogue);
v8::V8::AddMessageListener(handleConsoleMessage);
v8::V8::SetFailedAccessCheckCallbackFunction(reportUnsafeJavaScriptAccess);
isV8Initialized = true;
}
m_context = createNewContext(m_global);
if (m_context.IsEmpty())
return;
v8::Local<v8::Context> v8Context = context();
v8::Context::Scope contextScope(v8Context);
if (m_global.IsEmpty()) {
m_global = v8::Persistent<v8::Object>::New(v8Context->Global());
if (m_global.IsEmpty()) {
disposeContextHandles();
return;
}
#ifndef NDEBUG
V8GCController::registerGlobalHandle(PROXY, this, m_global);
#endif
}
v8::Handle<v8::String> objectString = v8::String::New("Object");
v8::Handle<v8::String> prototypeString = v8::String::New("prototype");
if (objectString.IsEmpty() || prototypeString.IsEmpty()) {
disposeContextHandles();
return;
}
v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(m_global->Get(objectString));
m_objectPrototype = v8::Persistent<v8::Value>::New(object->Get(prototypeString));
m_wrapperBoilerplates = v8::Persistent<v8::Array>::New(v8::Array::New(V8ClassIndex::WRAPPER_TYPE_COUNT));
if (m_objectPrototype.IsEmpty()) {
disposeContextHandles();
return;
}
#ifndef NDEBUG
V8GCController::registerGlobalHandle(PROXY, this, m_objectPrototype);
V8GCController::registerGlobalHandle(PROXY, this, m_wrapperBoilerplates);
#endif
if (!installDOMWindow(v8Context, m_frame->domWindow()))
disposeContextHandles();
updateDocument();
setSecurityToken();
m_frame->loader()->client()->didCreateScriptContext();
m_frame->loader()->dispatchWindowObjectAvailable();
}
template <class T>
void setDOMExceptionHelper(V8ClassIndex::V8WrapperType type, PassRefPtr<T> exception)
{
v8::Handle<v8::Value> v8Exception;
if (WorkerContextExecutionProxy::retrieve())
v8Exception = WorkerContextExecutionProxy::ToV8Object(type, exception.get());
else
v8Exception = V8DOMWrapper::convertToV8Object(type, exception.get());
v8::ThrowException(v8Exception);
}
void V8Proxy::setDOMException(int exceptionCode)
{
if (exceptionCode <= 0)
return;
ExceptionCodeDescription description;
getExceptionCodeDescription(exceptionCode, description);
v8::Handle<v8::Value> exception;
switch (description.type) {
case DOMExceptionType:
setDOMExceptionHelper(V8ClassIndex::DOMCOREEXCEPTION, DOMCoreException::create(description));
break;
case RangeExceptionType:
setDOMExceptionHelper(V8ClassIndex::RANGEEXCEPTION, RangeException::create(description));
break;
case EventExceptionType:
setDOMExceptionHelper(V8ClassIndex::EVENTEXCEPTION, EventException::create(description));
break;
case XMLHttpRequestExceptionType:
setDOMExceptionHelper(V8ClassIndex::XMLHTTPREQUESTEXCEPTION, XMLHttpRequestException::create(description));
break;
#if ENABLE(SVG)
case SVGExceptionType:
setDOMExceptionHelper(V8ClassIndex::SVGEXCEPTION, SVGException::create(description));
break;
#endif
#if ENABLE(XPATH)
case XPathExceptionType:
setDOMExceptionHelper(V8ClassIndex::XPATHEXCEPTION, XPathException::create(description));
break;
#endif
default:
ASSERT_NOT_REACHED();
break;
}
}
v8::Handle<v8::Value> V8Proxy::throwError(ErrorType type, const char* message)
{
switch (type) {
case RangeError:
return v8::ThrowException(v8::Exception::RangeError(v8String(message)));
case ReferenceError:
return v8::ThrowException(v8::Exception::ReferenceError(v8String(message)));
case SyntaxError:
return v8::ThrowException(v8::Exception::SyntaxError(v8String(message)));
case TypeError:
return v8::ThrowException(v8::Exception::TypeError(v8String(message)));
case GeneralError:
return v8::ThrowException(v8::Exception::Error(v8String(message)));
default:
ASSERT_NOT_REACHED();
return notHandledByInterceptor();
}
}
v8::Local<v8::Context> V8Proxy::context(Frame* frame)
{
V8Proxy* proxy = retrieve(frame);
if (!proxy)
return v8::Local<v8::Context>();
proxy->initContextIfNeeded();
return proxy->context();
}
v8::Local<v8::Context> V8Proxy::currentContext()
{
return v8::Context::GetCurrent();
}
v8::Handle<v8::Value> V8Proxy::checkNewLegal(const v8::Arguments& args)
{
if (!AllowAllocation::m_current)
return throwError(TypeError, "Illegal constructor");
return args.This();
}
void V8Proxy::bindJsObjectToWindow(Frame* frame, const char* name, int type, v8::Handle<v8::FunctionTemplate> descriptor, void* impl)
{
v8::Handle<v8::Context> v8Context = V8Proxy::context(frame);
if (v8Context.IsEmpty())
return;
v8::Context::Scope scope(v8Context);
v8::Handle<v8::Object> instance = descriptor->GetFunction();
V8DOMWrapper::setDOMWrapper(instance, type, impl);
v8::Handle<v8::Object> global = v8Context->Global();
global->Set(v8::String::New(name), instance);
}
void V8Proxy::processConsoleMessages()
{
ConsoleMessageManager::processDelayedMessages();
}
void V8Proxy::createUtilityContext()
{
ASSERT(m_utilityContext.IsEmpty());
v8::HandleScope scope;
v8::Handle<v8::ObjectTemplate> globalTemplate = v8::ObjectTemplate::New();
m_utilityContext = v8::Context::New(0, globalTemplate);
v8::Context::Scope contextScope(m_utilityContext);
DEFINE_STATIC_LOCAL(const char*, frameSourceLineSource,
("function frameSourceLine(exec_state) {"
" return exec_state.frame(0).sourceLine();"
"}"));
v8::Script::Compile(v8::String::New(frameSourceLineSource))->Run();
DEFINE_STATIC_LOCAL(const char*, frameSourceNameSource,
("function frameSourceName(exec_state) {"
" var frame = exec_state.frame(0);"
" if (frame.func().resolved() && "
" frame.func().script() && "
" frame.func().script().name()) {"
" return frame.func().script().name();"
" }"
"}"));
v8::Script::Compile(v8::String::New(frameSourceNameSource))->Run();
}
int V8Proxy::sourceLineNumber()
{
v8::HandleScope scope;
v8::Handle<v8::Context> v8UtilityContext = V8Proxy::utilityContext();
if (v8UtilityContext.IsEmpty())
return 0;
v8::Context::Scope contextScope(v8UtilityContext);
v8::Handle<v8::Function> frameSourceLine;
frameSourceLine = v8::Local<v8::Function>::Cast(v8UtilityContext->Global()->Get(v8::String::New("frameSourceLine")));
if (frameSourceLine.IsEmpty())
return 0;
v8::Handle<v8::Value> result = v8::Debug::Call(frameSourceLine);
if (result.IsEmpty())
return 0;
return result->Int32Value();
}
String V8Proxy::sourceName()
{
v8::HandleScope scope;
v8::Handle<v8::Context> v8UtilityContext = utilityContext();
if (v8UtilityContext.IsEmpty())
return String();
v8::Context::Scope contextScope(v8UtilityContext);
v8::Handle<v8::Function> frameSourceName;
frameSourceName = v8::Local<v8::Function>::Cast(v8UtilityContext->Global()->Get(v8::String::New("frameSourceName")));
if (frameSourceName.IsEmpty())
return String();
return toWebCoreString(v8::Debug::Call(frameSourceName));
}
void V8Proxy::registerExtension(v8::Extension* extension, const String& schemeRestriction)
{
v8::RegisterExtension(extension);
V8ExtensionInfo info = {schemeRestriction, extension};
m_extensions.push_back(info);
}
bool V8Proxy::setContextDebugId(int debugId)
{
ASSERT(debugId > 0);
if (m_context.IsEmpty())
return false;
v8::HandleScope scope;
if (!m_context->GetData()->IsUndefined())
return false;
v8::Context::Scope contextScope(m_context);
v8::Handle<v8::Object> contextData = v8::Object::New();
contextData->Set(v8::String::New(kContextDebugDataType), v8::String::New("page"));
contextData->Set(v8::String::New(kContextDebugDataValue), v8::Integer::New(debugId));
m_context->SetData(contextData);
return true;
}
int V8Proxy::contextDebugId(v8::Handle<v8::Context> context)
{
v8::HandleScope scope;
if (!context->GetData()->IsObject())
return -1;
v8::Handle<v8::Value> data = context->GetData()->ToObject()->Get( v8::String::New(kContextDebugDataValue));
return data->IsInt32() ? data->Int32Value() : -1;
}
}