diff options
author | japhet@chromium.org <japhet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-29 16:26:01 +0000 |
---|---|---|
committer | japhet@chromium.org <japhet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-29 16:26:01 +0000 |
commit | f58705418e184e0c42ebb5a264ff25785950cc0a (patch) | |
tree | 70a48dd90aeca08579b846e40f2fd4ad02debf92 /webkit | |
parent | 25d238062198d6e683cf33ca1033dd4bf7e9c5e3 (diff) | |
download | chromium_src-f58705418e184e0c42ebb5a264ff25785950cc0a.zip chromium_src-f58705418e184e0c42ebb5a264ff25785950cc0a.tar.gz chromium_src-f58705418e184e0c42ebb5a264ff25785950cc0a.tar.bz2 |
Reverting 19489,19488.
Review URL: http://codereview.chromium.org/150024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19490 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r-- | webkit/api/src/WebKit.cpp | 4 | ||||
-rw-r--r-- | webkit/glue/chrome_client_impl.cc | 4 | ||||
-rw-r--r-- | webkit/glue/devtools/bound_object.cc | 6 | ||||
-rw-r--r-- | webkit/glue/devtools/debugger_agent_impl.cc | 19 | ||||
-rw-r--r-- | webkit/glue/devtools/debugger_agent_manager.cc | 6 | ||||
-rw-r--r-- | webkit/glue/npruntime_util.cc | 8 | ||||
-rw-r--r-- | webkit/glue/webdevtoolsagent_impl.cc | 6 | ||||
-rw-r--r-- | webkit/glue/webdevtoolsclient_impl.cc | 14 | ||||
-rw-r--r-- | webkit/glue/webframe_impl.cc | 2 | ||||
-rw-r--r-- | webkit/port/bindings/scripts/CodeGeneratorV8.pm | 94 | ||||
-rw-r--r-- | webkit/port/bindings/v8/NPV8Object.cpp | 16 | ||||
-rw-r--r-- | webkit/port/bindings/v8/V8NPObject.cpp | 22 | ||||
-rw-r--r-- | webkit/port/bindings/v8/V8NPUtils.cpp | 2 | ||||
-rw-r--r-- | webkit/port/bindings/v8/V8SVGPODTypeWrapper.h | 4 | ||||
-rw-r--r-- | webkit/port/bindings/v8/v8_proxy.cpp | 3569 | ||||
-rw-r--r-- | webkit/port/bindings/v8/v8_proxy.h | 673 | ||||
-rw-r--r-- | webkit/port/bindings/v8/v8_utility.h | 68 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell.cc | 4 | ||||
-rw-r--r-- | webkit/webkit.gyp | 4 |
19 files changed, 4419 insertions, 106 deletions
diff --git a/webkit/api/src/WebKit.cpp b/webkit/api/src/WebKit.cpp index b903aec..8ecc859 100644 --- a/webkit/api/src/WebKit.cpp +++ b/webkit/api/src/WebKit.cpp @@ -97,13 +97,13 @@ void registerURLSchemeAsNoAccess(const WebString& scheme) void registerExtension(v8::Extension* extension) { - WebCore::V8Proxy::registerExtension(extension, WebString()); + WebCore::V8Proxy::RegisterExtension(extension, WebString()); } void registerExtension(v8::Extension* extension, const WebString& schemeRestriction) { - WebCore::V8Proxy::registerExtension(extension, schemeRestriction); + WebCore::V8Proxy::RegisterExtension(extension, schemeRestriction); } void enableWebWorkers() diff --git a/webkit/glue/chrome_client_impl.cc b/webkit/glue/chrome_client_impl.cc index 6341f55..ff8aeb5 100644 --- a/webkit/glue/chrome_client_impl.cc +++ b/webkit/glue/chrome_client_impl.cc @@ -25,7 +25,7 @@ MSVC_PUSH_WARNING_LEVEL(0); #include "ScriptController.h" #include "WindowFeatures.h" #if USE(V8) -#include "V8Proxy.h" +#include "v8_proxy.h" #endif MSVC_POP_WARNING(); @@ -361,7 +361,7 @@ void ChromeClientImpl::runJavaScriptAlert(WebCore::Frame* frame, #if USE(V8) // Before showing the JavaScript dialog, we give the proxy implementation // a chance to process any pending console messages. - WebCore::V8Proxy::processConsoleMessages(); + WebCore::V8Proxy::ProcessConsoleMessages(); #endif std::wstring wstr = webkit_glue::StringToStdWString(message); diff --git a/webkit/glue/devtools/bound_object.cc b/webkit/glue/devtools/bound_object.cc index bc8310c..c9993c4 100644 --- a/webkit/glue/devtools/bound_object.cc +++ b/webkit/glue/devtools/bound_object.cc @@ -6,7 +6,7 @@ #include <string> -#include "V8Proxy.h" +#include "v8_proxy.h" #include "webkit/glue/devtools/bound_object.h" using namespace WebCore; @@ -22,7 +22,7 @@ BoundObject::BoundObject( v8_this_ = v8::Persistent<v8::External>::New(v8::External::New(v8_this)); v8::Local<v8::FunctionTemplate> local_template = - v8::FunctionTemplate::New(V8Proxy::checkNewLegal); + v8::FunctionTemplate::New(V8Proxy::CheckNewLegal); host_template_ = v8::Persistent<v8::FunctionTemplate>::New(local_template); host_template_->SetClassName(v8::String::New(object_name)); } @@ -54,7 +54,7 @@ void BoundObject::Build() { v8::Local<v8::Function> constructor = host_template_->GetFunction(); bound_object_ = v8::Persistent<v8::Object>::New( - SafeAllocation::newInstance(constructor)); + SafeAllocation::NewInstance(constructor)); v8::Handle<v8::Object> global = context_->Global(); global->Set(v8::String::New(object_name_), bound_object_); diff --git a/webkit/glue/devtools/debugger_agent_impl.cc b/webkit/glue/devtools/debugger_agent_impl.cc index dfbe7bc..4312cbe 100644 --- a/webkit/glue/devtools/debugger_agent_impl.cc +++ b/webkit/glue/devtools/debugger_agent_impl.cc @@ -10,14 +10,15 @@ #include "Document.h" #include "Page.h" -#include "V8Binding.h" -#include "V8DOMWindow.h" -#include "V8Index.h" -#include "V8Proxy.h" #undef LOG #include "base/string_piece.h" #include "grit/webkit_resources.h" +#include "V8Binding.h" +#include "V8DOMWindow.h" +#include "V8Index.h" +#include "v8_binding.h" +#include "v8_proxy.h" #include "webkit/glue/devtools/debugger_agent_impl.h" #include "webkit/glue/devtools/debugger_agent_manager.h" #include "webkit/glue/glue_util.h" @@ -61,8 +62,8 @@ void DebuggerAgentImpl::GetContextId() { void DebuggerAgentImpl::StartProfiling() { v8::HandleScope scope; WebCore::V8Proxy* proxy = V8Proxy::retrieve(GetPage()->mainFrame()); - DCHECK(proxy && proxy->isContextInitialized()); - v8::Context::Scope context_scope(proxy->context()); + DCHECK(proxy && proxy->ContextInitialized()); + v8::Context::Scope context_scope(proxy->GetContext()); v8::V8::ResumeProfiler(); } @@ -104,12 +105,12 @@ void DebuggerAgentImpl::ResetUtilityContext( // TODO(pfeldman): Validate against Soeren. // Set up the DOM window as the prototype of the new global object. v8::Handle<v8::Context> window_context = - V8Proxy::context(document->frame()); + V8Proxy::GetContext(document->frame()); v8::Handle<v8::Object> window_global = window_context->Global(); v8::Handle<v8::Value> window_wrapper = - V8Proxy::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, window_global); + V8Proxy::LookupDOMWrapper(V8ClassIndex::DOMWINDOW, window_global); - ASSERT(V8Proxy::convertDOMWrapperToNative<DOMWindow>(window_wrapper) == + ASSERT(V8Proxy::DOMWrapperToNative<DOMWindow>(window_wrapper) == document->frame()->domWindow()); // Create a new environment using an empty template for the shadow diff --git a/webkit/glue/devtools/debugger_agent_manager.cc b/webkit/glue/devtools/debugger_agent_manager.cc index d2f41d3..102224f 100644 --- a/webkit/glue/devtools/debugger_agent_manager.cc +++ b/webkit/glue/devtools/debugger_agent_manager.cc @@ -6,7 +6,7 @@ #include "Frame.h" #include "PageGroupLoadDeferrer.h" -#include "V8Proxy.h" +#include "v8_proxy.h" #include <wtf/HashSet.h> #undef LOG @@ -188,7 +188,7 @@ void DebuggerAgentManager::OnV8DebugMessage(const v8::Debug::Message& message) { // If the context is from one of the inpected tabs or injected extension // scripts it must have host_id in the data field. - int host_id = WebCore::V8Proxy::contextDebugId(context); + int host_id = WebCore::V8Proxy::GetContextDebugId(context); if (host_id != -1) { DebuggerAgentImpl* agent = DebuggerAgentForHostId(host_id); if (agent) { @@ -222,7 +222,7 @@ void DebuggerAgentManager::SetHostId(WebFrameImpl* webframe, int host_id) { DCHECK(host_id > 0); WebCore::V8Proxy* proxy = WebCore::V8Proxy::retrieve(webframe->frame()); if (proxy) { - proxy->setContextDebugId(host_id); + proxy->SetContextDebugId(host_id); } } diff --git a/webkit/glue/npruntime_util.cc b/webkit/glue/npruntime_util.cc index 687afb1..f1c23eb 100644 --- a/webkit/glue/npruntime_util.cc +++ b/webkit/glue/npruntime_util.cc @@ -13,7 +13,7 @@ #include "MouseEvent.h" #include "NPV8Object.h" // for PrivateIdentifier #include "V8Helpers.h" -#include "V8Proxy.h" +#include "v8_proxy.h" #elif USE(JAVASCRIPTCORE_BINDINGS) #include "bridge/c/c_utility.h" #endif @@ -102,20 +102,20 @@ static bool DragEventData(NPObject* npobj, int* event_id, WebDragData* data) { // Get the current WebCore event. v8::Handle<v8::Value> current_event(GetEvent(context)); - WebCore::Event* event = V8Proxy::convertToNativeEvent(current_event); + WebCore::Event* event = V8Proxy::ToNativeEvent(current_event); if (event == NULL) return false; // Check that the given npobj is that event. V8NPObject* object = reinterpret_cast<V8NPObject*>(npobj); - WebCore::Event* given = V8Proxy::convertToNativeEvent(object->v8Object); + WebCore::Event* given = V8Proxy::ToNativeEvent(object->v8Object); if (given != event) return false; // Check the execution frames are same origin. V8Proxy* current = V8Proxy::retrieve(V8Proxy::retrieveFrame()); WebCore::Frame* frame = V8Proxy::retrieveFrame(context); - if (!current || !current->canAccessFrame(frame, false)) + if (!current || !current->CanAccessFrame(frame, false)) return false; const WebCore::EventNames& event_names(WebCore::eventNames()); diff --git a/webkit/glue/webdevtoolsagent_impl.cc b/webkit/glue/webdevtoolsagent_impl.cc index 7f62701..ac50d37 100644 --- a/webkit/glue/webdevtoolsagent_impl.cc +++ b/webkit/glue/webdevtoolsagent_impl.cc @@ -17,11 +17,11 @@ #include "ScriptObject.h" #include "ScriptState.h" #include "ScriptValue.h" -#include "V8Binding.h" -#include "V8Proxy.h" +#include "v8_proxy.h" #include <wtf/OwnPtr.h> #undef LOG +#include "V8Binding.h" #include "base/values.h" #include "webkit/api/public/WebDataSource.h" #include "webkit/api/public/WebURL.h" @@ -317,7 +317,7 @@ v8::Handle<v8::Value> WebDevToolsAgentImpl::JsGetNodeForId( WebDevToolsAgentImpl* agent = static_cast<WebDevToolsAgentImpl*>( v8::External::Cast(*args.Data())->Value()); Node* node = agent->dom_agent_impl_->GetNodeForId(node_id); - return V8Proxy::convertToV8Object(V8ClassIndex::NODE, node); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, node); } // static diff --git a/webkit/glue/webdevtoolsclient_impl.cc b/webkit/glue/webdevtoolsclient_impl.cc index 16d19c5..085a0b8 100644 --- a/webkit/glue/webdevtoolsclient_impl.cc +++ b/webkit/glue/webdevtoolsclient_impl.cc @@ -14,14 +14,14 @@ #include "Page.h" #include "PlatformString.h" #include "SecurityOrigin.h" -#include "V8Binding.h" -#include "V8CustomBinding.h" -#include "V8Proxy.h" -#include "V8Utilities.h" #include <wtf/OwnPtr.h> #include <wtf/Vector.h> #undef LOG +#include "V8Binding.h" +#include "V8CustomBinding.h" +#include "v8_proxy.h" +#include "v8_utility.h" #include "base/string_util.h" #include "base/values.h" #include "webkit/api/public/WebScriptSource.h" @@ -149,7 +149,7 @@ WebDevToolsClientImpl::WebDevToolsClientImpl( new JsToolsAgentBoundObj(this, frame, L"RemoteToolsAgent")); v8::HandleScope scope; - v8::Handle<v8::Context> frame_context = V8Proxy::context(frame->frame()); + v8::Handle<v8::Context> frame_context = V8Proxy::GetContext(frame->frame()); dev_tools_host_.set(new BoundObject(frame_context, this, "DevToolsHost")); dev_tools_host_->AddProtoFunction( "reset", @@ -253,7 +253,7 @@ v8::Handle<v8::Value> WebDevToolsClientImpl::JsAddSourceToFrame( if (source_string.isEmpty() || exception_catcher.HasCaught()) { return v8::Undefined(); } - Node* node = V8Proxy::convertDOMWrapperToNode<Node>(args[2]); + Node* node = V8Proxy::DOMWrapperToNode<Node>(args[2]); if (!node || !node->attached()) { return v8::Undefined(); } @@ -272,7 +272,7 @@ v8::Handle<v8::Value> WebDevToolsClientImpl::JsAddResourceSourceToFrame( if (mime_type.isEmpty()) { return v8::Undefined(); } - Node* node = V8Proxy::convertDOMWrapperToNode<Node>(args[2]); + Node* node = V8Proxy::DOMWrapperToNode<Node>(args[2]); WebDevToolsClientImpl* client = static_cast<WebDevToolsClientImpl*>( v8::External::Cast(*args.Data())->Value()); client->AddResourceSourceToFrame(resource_id, mime_type, node); diff --git a/webkit/glue/webframe_impl.cc b/webkit/glue/webframe_impl.cc index 8c13cb3..a02e73a 100644 --- a/webkit/glue/webframe_impl.cc +++ b/webkit/glue/webframe_impl.cc @@ -826,7 +826,7 @@ v8::Local<v8::Context> WebFrameImpl::GetScriptContext() { if (!frame_) return v8::Local<v8::Context>(); - return frame_->script()->proxy()->context(); + return frame_->script()->proxy()->GetContext(); } #endif diff --git a/webkit/port/bindings/scripts/CodeGeneratorV8.pm b/webkit/port/bindings/scripts/CodeGeneratorV8.pm index 24f5f4b..69eb445 100644 --- a/webkit/port/bindings/scripts/CodeGeneratorV8.pm +++ b/webkit/port/bindings/scripts/CodeGeneratorV8.pm @@ -315,7 +315,7 @@ sub GenerateSetDOMException my $result = ""; $result .= $indent . "if (ec) {\n"; - $result .= $indent . " V8Proxy::setDOMException(ec);\n"; + $result .= $indent . " V8Proxy::SetDOMException(ec);\n"; $result .= $indent . " return v8::Handle<v8::Value>();\n"; $result .= $indent . "}\n"; @@ -341,12 +341,12 @@ sub HolderToNative if (IsNodeSubType($dataNode)) { push(@implContentDecls, <<END); - $implClassName* imp = V8Proxy::convertDOMWrapperToNode<$implClassName>(holder); + $implClassName* imp = V8Proxy::DOMWrapperToNode<$implClassName>(holder); END } else { push(@implContentDecls, <<END); - $implClassName* imp = V8Proxy::convertToNativeObject<$implClassName>(V8ClassIndex::$classIndex, holder); + $implClassName* imp = V8Proxy::ToNativeObject<$implClassName>(V8ClassIndex::$classIndex, holder); END } @@ -369,14 +369,14 @@ sub GenerateDomainSafeFunctionGetter my $newTemplateString = GenerateNewFunctionTemplate($function, $dataNode, $signature); - $implIncludes{"V8Proxy.h"} = 1; + $implIncludes{"v8_proxy.h"} = 1; push(@implContentDecls, <<END); static v8::Handle<v8::Value> ${funcName}AttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) { INC_STATS(\"DOM.$implClassName.$funcName._get\"); static v8::Persistent<v8::FunctionTemplate> private_template = v8::Persistent<v8::FunctionTemplate>::New($newTemplateString); - v8::Handle<v8::Object> holder = V8Proxy::lookupDOMWrapper(V8ClassIndex::$classIndex, info.This()); + v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper(V8ClassIndex::$classIndex, info.This()); if (holder.IsEmpty()) { // can only reach here by 'object.__proto__.func', and it should passed // domain security check already @@ -388,7 +388,7 @@ END HolderToNative($dataNode, $implClassName, $classIndex); push(@implContentDecls, <<END); - if (!V8Proxy::canAccessFrame(imp->frame(), false)) { + if (!V8Proxy::CanAccessFrame(imp->frame(), false)) { static v8::Persistent<v8::FunctionTemplate> shared_template = v8::Persistent<v8::FunctionTemplate>::New($newTemplateString); return shared_template->GetFunction(); @@ -416,13 +416,13 @@ END if ($classIndex eq "DOMWINDOW") { push(@implContentDecls, <<END); - DOMWindow* window = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, info.Holder()); + DOMWindow* window = V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, info.Holder()); Frame* frame = window->frame(); if (frame) { // Get the proxy corresponding to the DOMWindow if possible to // make sure that the constructor function is constructed in the // context of the DOMWindow and not in the context of the caller. - return V8Proxy::retrieve(frame)->getConstructor(type); + return V8Proxy::retrieve(frame)->GetConstructor(type); } END } @@ -453,7 +453,7 @@ sub GenerateNormalAttrGetter my $attrExt = $attribute->signature->extendedAttributes; my $attrName = $attribute->signature->name; - $implIncludes{"V8Proxy.h"} = 1; + $implIncludes{"v8_proxy.h"} = 1; my $attrType = $codeGenerator->StripModule($attribute->signature->type); my $attrIsPodType = $codeGenerator->IsPodType($attrType); @@ -494,7 +494,7 @@ END if ($isPodType) { push(@implContentDecls, <<END); - V8SVGPODTypeWrapper<$implClassName>* imp_wrapper = V8Proxy::convertToNativeObject<V8SVGPODTypeWrapper<$implClassName> >(V8ClassIndex::$classIndex, info.Holder()); + V8SVGPODTypeWrapper<$implClassName>* imp_wrapper = V8Proxy::ToNativeObject<V8SVGPODTypeWrapper<$implClassName> >(V8ClassIndex::$classIndex, info.Holder()); $implClassName imp_instance = *imp_wrapper; END if ($getterStringUsesImp) { @@ -506,7 +506,7 @@ END } elsif ($attrExt->{"v8OnProto"} || $attrExt->{"V8DisallowShadowing"}) { # perform lookup first push(@implContentDecls, <<END); - v8::Handle<v8::Object> holder = V8Proxy::lookupDOMWrapper(V8ClassIndex::$classIndex, info.This()); + v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper(V8ClassIndex::$classIndex, info.This()); if (holder.IsEmpty()) return v8::Undefined(); END HolderToNative($dataNode, $implClassName, $classIndex); @@ -519,9 +519,9 @@ END # Generate security checks if necessary if ($attribute->signature->extendedAttributes->{"CheckNodeSecurity"}) { - push(@implContentDecls, " if (!V8Proxy::checkNodeSecurity(imp->$attrName())) return v8::Undefined();\n\n"); + push(@implContentDecls, " if (!V8Proxy::CheckNodeSecurity(imp->$attrName())) return v8::Undefined();\n\n"); } elsif ($attribute->signature->extendedAttributes->{"CheckFrameSecurity"}) { - push(@implContentDecls, " if (!V8Proxy::checkNodeSecurity(imp->contentDocument())) return v8::Undefined();\n\n"); + push(@implContentDecls, " if (!V8Proxy::CheckNodeSecurity(imp->contentDocument())) return v8::Undefined();\n\n"); } my $useExceptions = 1 if @{$attribute->getterExceptions} and !($isPodType); @@ -618,7 +618,7 @@ END if ($attrIsPodType) { my $classIndex = uc($attrType); - push(@implContentDecls, " return V8Proxy::convertToV8Object(V8ClassIndex::$classIndex, wrapper);\n"); + push(@implContentDecls, " return V8Proxy::ToV8Object(V8ClassIndex::$classIndex, wrapper);\n"); } else { push(@implContentDecls, " return " . NativeToJSValue($attribute->signature, $result). ";\n"); } @@ -631,7 +631,7 @@ sub GenerateReplaceableAttrSetter { my $implClassName = shift; - $implIncludes{"V8Proxy.h"} = 1; + $implIncludes{"v8_proxy.h"} = 1; push(@implContentDecls, " static void ${attrName}AttrSetter(v8::Local<v8::String> name," . @@ -655,7 +655,7 @@ sub GenerateNormalAttrSetter my $attrExt = $attribute->signature->extendedAttributes; - $implIncludes{"V8Proxy.h"} = 1; + $implIncludes{"v8_proxy.h"} = 1; push(@implContentDecls, " static void ${attrName}AttrSetter(v8::Local<v8::String> name," . @@ -668,14 +668,14 @@ sub GenerateNormalAttrSetter if ($isPodType) { $implClassName = GetNativeType($implClassName); $implIncludes{"V8SVGPODTypeWrapper.h"} = 1; - push(@implContentDecls, " V8SVGPODTypeWrapper<$implClassName>* wrapper = V8Proxy::convertToNativeObject<V8SVGPODTypeWrapper<$implClassName> >(V8ClassIndex::$classIndex, info.Holder());\n"); + push(@implContentDecls, " V8SVGPODTypeWrapper<$implClassName>* wrapper = V8Proxy::ToNativeObject<V8SVGPODTypeWrapper<$implClassName> >(V8ClassIndex::$classIndex, info.Holder());\n"); push(@implContentDecls, " $implClassName imp_instance = *wrapper;\n"); push(@implContentDecls, " $implClassName* imp = &imp_instance;\n"); } elsif ($attrExt->{"v8OnProto"}) { # perform lookup first push(@implContentDecls, <<END); - v8::Handle<v8::Object> holder = V8Proxy::lookupDOMWrapper(V8ClassIndex::$classIndex, info.This()); + v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper(V8ClassIndex::$classIndex, info.This()); if (holder.IsEmpty()) return v8::Undefined(); END HolderToNative($dataNode, $implClassName, $classIndex); @@ -718,11 +718,11 @@ END } if ($useExceptions) { - push(@implContentDecls, " V8Proxy::setDOMException(ec);\n"); + push(@implContentDecls, " V8Proxy::SetDOMException(ec);\n"); } if ($isPodType) { - push(@implContentDecls, " wrapper->commitChange(*imp, V8Proxy::svgContext(wrapper));\n"); + push(@implContentDecls, " wrapper->commitChange(*imp, V8Proxy::GetSVGContext(wrapper));\n"); } elsif (IsSVGTypeNeedingContextParameter($implClassName)) { $implIncludes{"SVGElement.h"} = 1; @@ -731,7 +731,7 @@ END $currentObject = "wrapper"; } - push(@implContentDecls, " if (SVGElement* context = V8Proxy::svgContext($currentObject)) {\n"); + push(@implContentDecls, " if (SVGElement* context = V8Proxy::GetSVGContext($currentObject)) {\n"); push(@implContentDecls, " context->svgAttributeChanged(imp->associatedAttributeName());\n"); push(@implContentDecls, " }\n"); } @@ -791,7 +791,7 @@ sub GenerateFunctionCallback if ($codeGenerator->IsPodType($implClassName)) { my $nativeClassName = GetNativeType($implClassName); - push(@implContentDecls, " V8SVGPODTypeWrapper<$nativeClassName>* imp_wrapper = V8Proxy::convertToNativeObject<V8SVGPODTypeWrapper<$nativeClassName> >(V8ClassIndex::$classIndex, args.Holder());\n"); + push(@implContentDecls, " V8SVGPODTypeWrapper<$nativeClassName>* imp_wrapper = V8Proxy::ToNativeObject<V8SVGPODTypeWrapper<$nativeClassName> >(V8ClassIndex::$classIndex, args.Holder());\n"); push(@implContentDecls, " $nativeClassName imp_instance = *imp_wrapper;\n"); push(@implContentDecls, " $nativeClassName* imp = &imp_instance;\n"); } else { @@ -807,7 +807,7 @@ END && !$function->signature->extendedAttributes->{"DoNotCheckDomainSecurity"}) { # We have not find real use cases yet. push(@implContentDecls, -" if (!V8Proxy::canAccessFrame(imp->frame(), true)) {\n". +" if (!V8Proxy::CanAccessFrame(imp->frame(), true)) {\n". " return v8::Undefined();\n" . " }\n"); } @@ -849,7 +849,7 @@ END $implIncludes{"ExceptionCode.h"} = 1; push(@implContentDecls, " if (!$parameterName" . (BasicTypeCanFailConversion($parameter) ? "Ok" : "") . ") {\n" . -" V8Proxy::setDOMException(TYPE_MISMATCH_ERR);\n" . +" V8Proxy::SetDOMException(TYPE_MISMATCH_ERR);\n" . " return v8::Handle<v8::Value>();\n" . " }\n"); } @@ -858,7 +858,7 @@ END $implIncludes{"ExceptionCode.h"} = 1; push(@implContentDecls, " if ($parameterName < 0) {\n" . -" V8Proxy::setDOMException(INDEX_SIZE_ERR);\n" . +" V8Proxy::SetDOMException(INDEX_SIZE_ERR);\n" . " return v8::Handle<v8::Value>();\n" . " }\n"); } @@ -1050,7 +1050,7 @@ sub GenerateImplementation push(@implFixedHeader, "#include \"config.h\"\n" . - "#include \"V8Proxy.h\"\n" . + "#include \"v8_proxy.h\"\n" . "#include \"v8_binding.h\"\n\n" . "#undef LOG\n\n"); @@ -1067,7 +1067,7 @@ sub GenerateImplementation $implIncludes{"${className}.h"} = 1; AddIncludesForType($interfaceName); - $implIncludes{"V8Proxy.h"} = 1; + $implIncludes{"v8_proxy.h"} = 1; push(@implContentDecls, "namespace WebCore {\n"); push(@implContentDecls, "namespace ${interfaceName}Internal {\n\n"); @@ -1217,7 +1217,7 @@ END if ($implClassName eq "DOMWindow") { push(@implContent, <<END); static v8::Persistent<v8::ObjectTemplate> ConfigureShadowObjectTemplate(v8::Persistent<v8::ObjectTemplate> templ) { - batchConfigureAttributes(templ, + BatchConfigureAttributes(templ, v8::Handle<v8::ObjectTemplate>(), shadow_attrs, sizeof(shadow_attrs)/sizeof(*shadow_attrs)); @@ -1240,7 +1240,7 @@ END # Set up our attributes if we have them if ($has_attributes) { push(@implContent, <<END); - batchConfigureAttributes(instance, proto, attrs, sizeof(attrs)/sizeof(*attrs)); + BatchConfigureAttributes(instance, proto, attrs, sizeof(attrs)/sizeof(*attrs)); END } @@ -1325,7 +1325,7 @@ END if ($parent eq "EventTarget") { next; } $implIncludes{"V8${parent}.h"} = 1; my $parentClassIndex = uc($codeGenerator->StripModule($parent)); - push(@implContent, " desc->Inherit(V8Proxy::getTemplate(V8ClassIndex::${parentClassIndex}));\n"); + push(@implContent, " desc->Inherit(V8Proxy::GetTemplate(V8ClassIndex::${parentClassIndex}));\n"); last; } @@ -1334,7 +1334,7 @@ END if ($has_constants) { push(@implContent, <<END); - batchConfigureConstants(desc, proto, consts, sizeof(consts)/sizeof(*consts)); + BatchConfigureConstants(desc, proto, consts, sizeof(consts)/sizeof(*consts)); END } @@ -1346,7 +1346,7 @@ v8::Persistent<v8::FunctionTemplate> ${className}::GetRawTemplate() { static v8::Persistent<v8::FunctionTemplate> ${className}_raw_cache_; if (${className}_raw_cache_.IsEmpty()) { v8::HandleScope scope; - v8::Local<v8::FunctionTemplate> result = v8::FunctionTemplate::New(V8Proxy::checkNewLegal); + v8::Local<v8::FunctionTemplate> result = v8::FunctionTemplate::New(V8Proxy::CheckNewLegal); ${className}_raw_cache_ = v8::Persistent<v8::FunctionTemplate>::New(result); } return ${className}_raw_cache_; @@ -1489,7 +1489,7 @@ sub GenerateFunctionCallString() } $result .= $indent . "if (success)\n"; $result .= $indent . " " . - "return V8Proxy::convertNodeToV8Object($nodeToReturn);\n"; + "return V8Proxy::NodeToV8Object($nodeToReturn);\n"; $result .= $indent . "return v8::Null();\n"; return $result; } elsif ($returnType eq "void") { @@ -1554,7 +1554,7 @@ sub GenerateFunctionCallString() if ($returnsPodType) { my $classIndex = uc($returnType); - $result .= $indent . "return V8Proxy::convertToV8Object(V8ClassIndex::$classIndex, wrapper);\n"; + $result .= $indent . "return V8Proxy::ToV8Object(V8ClassIndex::$classIndex, wrapper);\n"; } else { $result .= $indent . "return " . NativeToJSValue($function->signature, $return) . ";\n"; } @@ -1818,7 +1818,7 @@ sub JSValueToNative } if ($type eq "NodeFilter") { - return "V8Proxy::wrapNativeNodeFilter($value)"; + return "V8Proxy::ToNativeNodeFilter($value)"; } if ($type eq "SVGRect") { @@ -1830,12 +1830,12 @@ sub JSValueToNative } # Default, assume autogenerated type conversion routines - $implIncludes{"V8Proxy.h"} = 1; + $implIncludes{"v8_proxy.h"} = 1; if ($type eq "EventTarget") { $implIncludes{"V8Node.h"} = 1; # EventTarget is not in DOM hierarchy, but all Nodes are EventTarget. - return "V8Node::HasInstance($value) ? V8Proxy::convertDOMWrapperToNode<Node>($value) : 0"; + return "V8Node::HasInstance($value) ? V8Proxy::DOMWrapperToNode<Node>($value) : 0"; } AddIncludesForType($type); @@ -1846,7 +1846,7 @@ sub JSValueToNative # Perform type checks on the parameter, if it is expected Node type, # return NULL. - return "V8${type}::HasInstance($value) ? V8Proxy::convertDOMWrapperToNode<${type}>($value) : 0"; + return "V8${type}::HasInstance($value) ? V8Proxy::DOMWrapperToNode<${type}>($value) : 0"; } else { # TODO: Temporary to avoid Window name conflict. @@ -1866,7 +1866,7 @@ sub JSValueToNative # Perform type checks on the parameter, if it is expected Node type, # return NULL. - return "V8${type}::HasInstance($value) ? V8Proxy::convertToNativeObject<${implClassName}>(V8ClassIndex::${classIndex}, $value) : 0"; + return "V8${type}::HasInstance($value) ? V8Proxy::ToNativeObject<${implClassName}>(V8ClassIndex::${classIndex}, $value) : 0"; } } @@ -2020,23 +2020,23 @@ sub NativeToJSValue # special case for non-DOM node interfaces if (IsDOMNodeType($type)) { - return "V8Proxy::convertNodeToV8Object($value)"; + return "V8Proxy::NodeToV8Object($value)"; } if ($type eq "EventTarget" or $type eq "SVGElementInstance") { - return "V8Proxy::convertEventTargetToV8Object($value)"; + return "V8Proxy::EventTargetToV8Object($value)"; } if ($type eq "Event") { - return "V8Proxy::convertEventToV8Object($value)"; + return "V8Proxy::EventToV8Object($value)"; } if ($type eq "EventListener") { - return "V8Proxy::convertEventListenerToV8Object($value)"; + return "V8Proxy::EventListenerToV8Object($value)"; } if ($type eq "RGBColor") { - return "V8Proxy::convertToV8Object(V8ClassIndex::RGBCOLOR, new RGBColor($value))"; + return "V8Proxy::ToV8Object(V8ClassIndex::RGBCOLOR, new RGBColor($value))"; } if ($type eq "WorkerContext" or $type eq "WorkerLocation" or $type eq "WorkerNavigator") { @@ -2055,7 +2055,7 @@ sub NativeToJSValue $value = GenerateSVGStaticPodTypeWrapper($type, $value); } - return "V8Proxy::convertToV8Object(V8ClassIndex::$classIndex, $value)"; + return "V8Proxy::ToV8Object(V8ClassIndex::$classIndex, $value)"; } } @@ -2127,7 +2127,7 @@ sub GenerateSVGContextAssignment my $indent = shift; $result = GenerateSVGContextRetrieval($srcType, $indent); - $result .= $indent . "V8Proxy::setSVGContext($value, context);\n"; + $result .= $indent . "V8Proxy::SetSVGContext($value, context);\n"; return $result; } @@ -2147,7 +2147,7 @@ sub GenerateSVGContextRetrieval my $contextDecl; if (IsSVGTypeNeedingContextParameter($srcType)) { - $contextDecl = "V8Proxy::svgContext($srcObject)"; + $contextDecl = "V8Proxy::GetSVGContext($srcObject)"; } else { $contextDecl = $srcObject; } diff --git a/webkit/port/bindings/v8/NPV8Object.cpp b/webkit/port/bindings/v8/NPV8Object.cpp index 5238ceb..24381c8 100644 --- a/webkit/port/bindings/v8/NPV8Object.cpp +++ b/webkit/port/bindings/v8/NPV8Object.cpp @@ -41,7 +41,7 @@ #include "V8CustomBinding.h" #include "V8Helpers.h" #include "V8NPUtils.h" -#include "V8Proxy.h" +#include "v8_proxy.h" #include "DOMWindow.h" using WebCore::toV8Context; @@ -60,7 +60,7 @@ static void FreeV8NPObject(NPObject* npobj) { V8NPObject *object = reinterpret_cast<V8NPObject*>(npobj); #ifndef NDEBUG - V8Proxy::unregisterGlobalHandle(object, object->v8Object); + V8Proxy::UnregisterGlobalHandle(object, object->v8Object); #endif object->v8Object.Dispose(); free(object); @@ -105,7 +105,7 @@ NPObject* npCreateV8ScriptObject(NPP npp, v8::Handle<v8::Object> object, WebCore object->GetInternalField(V8Custom::kDOMWrapperTypeIndex)->IsNumber() && object->GetInternalField(V8Custom::kDOMWrapperTypeIndex)->Uint32Value() == V8ClassIndex::NPOBJECT) { - NPObject* rv = V8Proxy::convertToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, object); + NPObject* rv = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, object); NPN_RetainObject(rv); return rv; } @@ -113,7 +113,7 @@ NPObject* npCreateV8ScriptObject(NPP npp, v8::Handle<v8::Object> object, WebCore V8NPObject* obj = reinterpret_cast<V8NPObject*>(NPN_CreateObject(npp, &V8NPObjectClass)); obj->v8Object = v8::Persistent<v8::Object>::New(object); #ifndef NDEBUG - V8Proxy::registerGlobalHandle(WebCore::NPOBJECT, obj, obj->v8Object); + V8Proxy::RegisterGlobalHandle(WebCore::NPOBJECT, obj, obj->v8Object); #endif obj->rootObject = root; return reinterpret_cast<NPObject*>(obj); @@ -167,7 +167,7 @@ bool NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName, v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(funcObj); // Create list of args to pass to v8 v8::Handle<v8::Value>* argv = listFromVariantArgs(args, argCount, npobj); - v8::Local<v8::Value> resultObj = proxy->callFunction(func, object->v8Object, argCount, argv); + v8::Local<v8::Value> resultObj = proxy->CallFunction(func, object->v8Object, argCount, argv); delete[] argv; // If we had an error, return false. The spec is a little unclear here, but @@ -221,7 +221,7 @@ bool NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args, // Create list of args to pass to v8 v8::Handle<v8::Value>* argv = listFromVariantArgs(args, argCount, npobj); - resultObj = proxy->callFunction(func, funcObj, argCount, argv); + resultObj = proxy->CallFunction(func, funcObj, argCount, argv); delete[] argv; } @@ -420,7 +420,7 @@ void NPN_SetException(NPObject *npobj, const NPUTF8 *message) return; v8::Context::Scope scope(context); - V8Proxy::throwError(V8Proxy::GeneralError, message); + V8Proxy::ThrowError(V8Proxy::GENERAL_ERROR, message); } bool NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier, uint32_t *count) @@ -507,7 +507,7 @@ bool NPN_Construct(NPP npp, NPObject* npobj, const NPVariant* args, uint32_t arg // Create list of args to pass to v8. v8::Handle<v8::Value>* argv = listFromVariantArgs(args, argCount, npobj); - resultObj = proxy->newInstance(ctor, argCount, argv); + resultObj = proxy->NewInstance(ctor, argCount, argv); delete[] argv; } diff --git a/webkit/port/bindings/v8/V8NPObject.cpp b/webkit/port/bindings/v8/V8NPObject.cpp index 09a56d8..35be7e0 100644 --- a/webkit/port/bindings/v8/V8NPObject.cpp +++ b/webkit/port/bindings/v8/V8NPObject.cpp @@ -63,25 +63,25 @@ static v8::Handle<v8::Value> npObjectInvokeImpl(const v8::Arguments& args, Invok if (V8HTMLAppletElement::HasInstance(args.Holder()) || V8HTMLEmbedElement::HasInstance(args.Holder()) || V8HTMLObjectElement::HasInstance(args.Holder())) { // The holder object is a subtype of HTMLPlugInElement. - HTMLPlugInElement* element = V8Proxy::convertDOMWrapperToNode<HTMLPlugInElement>(args.Holder()); + HTMLPlugInElement* element = V8Proxy::DOMWrapperToNode<HTMLPlugInElement>(args.Holder()); ScriptInstance scriptInstance = element->getInstance(); if (scriptInstance) - npObject = V8Proxy::convertToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, scriptInstance->instance()); + npObject = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, scriptInstance->instance()); else npObject = 0; } else { // The holder object is not a subtype of HTMLPlugInElement, it // must be an NPObject which has three internal fields. if (args.Holder()->InternalFieldCount() != V8Custom::kNPObjectInternalFieldCount) - return throwError("NPMethod called on non-NPObject", V8Proxy::ReferenceError); + return throwError("NPMethod called on non-NPObject", V8Proxy::REFERENCE_ERROR); - npObject = V8Proxy::convertToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, args.Holder()); + npObject = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, args.Holder()); } // Verify that our wrapper wasn't using a NPObject which // has already been deleted. if (!npObject || !_NPN_IsAlive(npObject)) - return throwError("NPObject deleted", V8Proxy::ReferenceError); + return throwError("NPObject deleted", V8Proxy::REFERENCE_ERROR); // Wrap up parameters. int numArgs = args.Length(); @@ -158,12 +158,12 @@ static v8::Handle<v8::Value> npObjectGetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> key) { - NPObject* npObject = V8Proxy::convertToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, self); + NPObject* npObject = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, self); // Verify that our wrapper wasn't using a NPObject which // has already been deleted. if (!npObject || !_NPN_IsAlive(npObject)) - return throwError("NPObject deleted", V8Proxy::ReferenceError); + return throwError("NPObject deleted", V8Proxy::REFERENCE_ERROR); if (npObject->_class->hasProperty && npObject->_class->hasProperty(npObject, identifier) @@ -231,12 +231,12 @@ static v8::Handle<v8::Value> npObjectSetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> value) { - NPObject* npObject = V8Proxy::convertToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, self); + NPObject* npObject = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, self); // Verify that our wrapper wasn't using a NPObject which // has already been deleted. if (!npObject || !_NPN_IsAlive(npObject)) { - throwError("NPObject deleted", V8Proxy::ReferenceError); + throwError("NPObject deleted", V8Proxy::REFERENCE_ERROR); return value; // Intercepted, but an exception was thrown. } @@ -339,7 +339,7 @@ v8::Local<v8::Object> createV8ObjectForNPObject(NPObject* object, NPObject* root } v8::Handle<v8::Function> v8Function = npObjectDesc->GetFunction(); - v8::Local<v8::Object> value = SafeAllocation::newInstance(v8Function); + v8::Local<v8::Object> value = SafeAllocation::NewInstance(v8Function); // If we were unable to allocate the instance, we avoid wrapping // and registering the NP object. @@ -365,7 +365,7 @@ void forgetV8ObjectForNPObject(NPObject* object) if (staticNPObjectMap.contains(object)) { v8::HandleScope scope; v8::Persistent<v8::Object> handle(staticNPObjectMap.get(object)); - WebCore::V8Proxy::setDOMWrapper(handle, WebCore::V8ClassIndex::NPOBJECT, 0); + WebCore::V8Proxy::SetDOMWrapper(handle, WebCore::V8ClassIndex::NPOBJECT, 0); staticNPObjectMap.forget(object); NPN_ReleaseObject(object); } diff --git a/webkit/port/bindings/v8/V8NPUtils.cpp b/webkit/port/bindings/v8/V8NPUtils.cpp index fa0d76f..3a68288 100644 --- a/webkit/port/bindings/v8/V8NPUtils.cpp +++ b/webkit/port/bindings/v8/V8NPUtils.cpp @@ -39,7 +39,7 @@ #include "npruntime_priv.h" #include "NPV8Object.h" #include "V8NPObject.h" -#include "V8Proxy.h" +#include "v8_proxy.h" void convertV8ObjectToNPVariant(v8::Local<v8::Value> object, NPObject *owner, NPVariant* result) { diff --git a/webkit/port/bindings/v8/V8SVGPODTypeWrapper.h b/webkit/port/bindings/v8/V8SVGPODTypeWrapper.h index 4373b8e..0fb1fb8 100644 --- a/webkit/port/bindings/v8/V8SVGPODTypeWrapper.h +++ b/webkit/port/bindings/v8/V8SVGPODTypeWrapper.h @@ -36,7 +36,7 @@ #include <wtf/Assertions.h> #include <wtf/RefCounted.h> #include <wtf/HashMap.h> -#include "V8Proxy.h" +#include "v8_proxy.h" namespace WebCore { @@ -370,7 +370,7 @@ template <class P> P V8SVGPODTypeUtil::ToSVGPODType(V8ClassIndex::V8WrapperType type, v8::Handle<v8::Value> object, bool& ok) { - void *wrapper = V8Proxy::convertToSVGPODTypeImpl(type, object); + void *wrapper = V8Proxy::ToSVGPODTypeImpl(type, object); if (wrapper == NULL) { ok = false; return P(); diff --git a/webkit/port/bindings/v8/v8_proxy.cpp b/webkit/port/bindings/v8/v8_proxy.cpp new file mode 100644 index 0000000..e1f23ef --- /dev/null +++ b/webkit/port/bindings/v8/v8_proxy.cpp @@ -0,0 +1,3569 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "config.h" + +#include <algorithm> +#include <utility> + +#include <v8.h> +#include <v8-debug.h> + +#include "v8_proxy.h" +#include "v8_binding.h" +#include "V8Collection.h" +#include "V8DOMWindow.h" +#include "V8IsolatedWorld.h" + +#include "ChromiumBridge.h" +#include "CSSMutableStyleDeclaration.h" +#include "DOMObjectsInclude.h" +#include "DocumentLoader.h" +#include "FrameLoaderClient.h" +#include "ScriptController.h" +#include "V8CustomBinding.h" +#include "V8DOMMap.h" +#include "V8Index.h" +#include "WorkerContextExecutionProxy.h" + +namespace WebCore { + +// Static utility context. +v8::Persistent<v8::Context> V8Proxy::m_utilityContext; + +// Static list of registered extensions +V8ExtensionList V8Proxy::m_extensions; + +// static +const char* V8Proxy::kContextDebugDataType = "type"; +const char* V8Proxy::kContextDebugDataValue = "value"; + +#ifndef NDEBUG +// Keeps track of global handles created (not JS wrappers +// of DOM objects). Often these global handles are source +// of leaks. +// +// If you want to let a C++ object hold a persistent handle +// to a JS object, you should register the handle here to +// keep track of leaks. +// +// When creating a persistent handle, call: +// +// #ifndef NDEBUG +// V8Proxy::RegisterGlobalHandle(type, host, handle); +// #endif +// +// When releasing the handle, call: +// +// #ifndef NDEBUG +// V8Proxy::UnregisterGlobalHandle(type, host, handle); +// #endif +// +typedef HashMap<v8::Value*, GlobalHandleInfo*> GlobalHandleMap; + +static GlobalHandleMap& global_handle_map() +{ + static GlobalHandleMap static_global_handle_map; + return static_global_handle_map; +} + + +// The USE_VAR(x) template is used to silence C++ compiler warnings +// issued for unused variables (typically parameters or values that +// we want to watch in the debugger). +template <typename T> +static inline void USE_VAR(T) { } + +// The function is the place to set the break point to inspect +// live global handles. Leaks are often come from leaked global handles. +static void EnumerateGlobalHandles() +{ + for (GlobalHandleMap::iterator it = global_handle_map().begin(), + end = global_handle_map().end(); it != end; ++it) { + GlobalHandleInfo* info = it->second; + USE_VAR(info); + v8::Value* handle = it->first; + USE_VAR(handle); + } +} + +void V8Proxy::RegisterGlobalHandle(GlobalHandleType type, void* host, + v8::Persistent<v8::Value> handle) +{ + ASSERT(!global_handle_map().contains(*handle)); + global_handle_map().set(*handle, new GlobalHandleInfo(host, type)); +} + + +void V8Proxy::UnregisterGlobalHandle(void* host, v8::Persistent<v8::Value> handle) +{ + ASSERT(global_handle_map().contains(*handle)); + GlobalHandleInfo* info = global_handle_map().take(*handle); + ASSERT(info->host_ == host); + delete info; +} +#endif // ifndef NDEBUG + +void BatchConfigureAttributes(v8::Handle<v8::ObjectTemplate> inst, + v8::Handle<v8::ObjectTemplate> proto, + const BatchedAttribute* attrs, + size_t num_attrs) +{ + for (size_t i = 0; i < num_attrs; ++i) { + const BatchedAttribute* a = &attrs[i]; + (a->on_proto ? proto : inst)->SetAccessor( + v8::String::New(a->name), + a->getter, + a->setter, + a->data == V8ClassIndex::INVALID_CLASS_INDEX + ? v8::Handle<v8::Value>() + : v8::Integer::New(V8ClassIndex::ToInt(a->data)), + a->settings, + a->attribute); + } +} + +void BatchConfigureConstants(v8::Handle<v8::FunctionTemplate> desc, + v8::Handle<v8::ObjectTemplate> proto, + const BatchedConstant* consts, + size_t num_consts) +{ + for (size_t i = 0; i < num_consts; ++i) { + const BatchedConstant* c = &consts[i]; + desc->Set(v8::String::New(c->name), + v8::Integer::New(c->value), + v8::ReadOnly); + proto->Set(v8::String::New(c->name), + v8::Integer::New(c->value), + v8::ReadOnly); + } +} + +typedef HashMap<Node*, v8::Object*> DOMNodeMap; +typedef HashMap<void*, v8::Object*> DOMObjectMap; + +#ifndef NDEBUG + +static void EnumerateDOMObjectMap(DOMObjectMap& wrapper_map) +{ + for (DOMObjectMap::iterator it = wrapper_map.begin(), end = wrapper_map.end(); + it != end; ++it) { + v8::Persistent<v8::Object> wrapper(it->second); + V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); + void* obj = it->first; + USE_VAR(type); + USE_VAR(obj); + } +} + +class DOMObjectVisitor : public DOMWrapperMap<void>::Visitor { + public: + void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper) { + V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); + USE_VAR(type); + USE_VAR(object); + } +}; + +class EnsureWeakDOMNodeVisitor : public DOMWrapperMap<Node>::Visitor { + public: + void visitDOMWrapper(Node* object, v8::Persistent<v8::Object> wrapper) { + USE_VAR(object); + ASSERT(wrapper.IsWeak()); + } +}; + +#endif // NDEBUG + +#if ENABLE(SVG) +v8::Handle<v8::Value> V8Proxy::SVGElementInstanceToV8Object( + SVGElementInstance* instance) +{ + if (!instance) + return v8::Null(); + + v8::Handle<v8::Object> existing_instance = getDOMSVGElementInstanceMap().get(instance); + if (!existing_instance.IsEmpty()) + return existing_instance; + + instance->ref(); + + // Instantiate the V8 object and remember it + v8::Handle<v8::Object> result = + InstantiateV8Object(V8ClassIndex::SVGELEMENTINSTANCE, + V8ClassIndex::SVGELEMENTINSTANCE, + instance); + if (!result.IsEmpty()) { + // Only update the DOM SVG element map if the result is non-empty. + getDOMSVGElementInstanceMap().set(instance, + v8::Persistent<v8::Object>::New(result)); + } + return result; +} + +// Map of SVG objects with contexts to their contexts +static HashMap<void*, SVGElement*>& svg_object_to_context_map() +{ + static HashMap<void*, SVGElement*> static_svg_object_to_context_map; + return static_svg_object_to_context_map; +} + +v8::Handle<v8::Value> V8Proxy::SVGObjectWithContextToV8Object( + V8ClassIndex::V8WrapperType type, void* object) +{ + if (!object) + return v8::Null(); + + v8::Persistent<v8::Object> result = + getDOMSVGObjectWithContextMap().get(object); + if (!result.IsEmpty()) return result; + + // Special case: SVGPathSegs need to be downcast to their real type + if (type == V8ClassIndex::SVGPATHSEG) + type = V8Custom::DowncastSVGPathSeg(object); + + v8::Local<v8::Object> v8obj = InstantiateV8Object(type, type, object); + if (!v8obj.IsEmpty()) { + result = v8::Persistent<v8::Object>::New(v8obj); + switch (type) { +#define MAKE_CASE(TYPE, NAME) \ + case V8ClassIndex::TYPE: static_cast<NAME*>(object)->ref(); break; +SVG_OBJECT_TYPES(MAKE_CASE) +#undef MAKE_CASE +#define MAKE_CASE(TYPE, NAME) \ + case V8ClassIndex::TYPE: \ + static_cast<V8SVGPODTypeWrapper<NAME>*>(object)->ref(); break; +SVG_POD_NATIVE_TYPES(MAKE_CASE) +#undef MAKE_CASE + default: + ASSERT(false); + } + getDOMSVGObjectWithContextMap().set(object, result); + } + + return result; +} + +void V8Proxy::SetSVGContext(void* obj, SVGElement* context) +{ + if (obj == NULL) + return; + + SVGElement* old_context = svg_object_to_context_map().get(obj); + + if (old_context == context) + return; + + if (old_context) + old_context->deref(); + + if (context) + context->ref(); + + svg_object_to_context_map().set(obj, context); +} + +SVGElement* V8Proxy::GetSVGContext(void* obj) +{ + return svg_object_to_context_map().get(obj); +} + +#endif + +// A map from a DOM node to its JS wrapper, the wrapper +// is kept as a strong reference to survive GCs. +static DOMObjectMap& gc_protected_map() { + static DOMObjectMap static_gc_protected_map; + return static_gc_protected_map; +} + +// static +void V8Proxy::GCProtect(void* dom_object) +{ + if (!dom_object) + return; + if (gc_protected_map().contains(dom_object)) + return; + if (!getDOMObjectMap().contains(dom_object)) + return; + + // Create a new (strong) persistent handle for the object. + v8::Persistent<v8::Object> wrapper = getDOMObjectMap().get(dom_object); + if (wrapper.IsEmpty()) return; + + gc_protected_map().set(dom_object, *v8::Persistent<v8::Object>::New(wrapper)); +} + + +// static +void V8Proxy::GCUnprotect(void* dom_object) +{ + if (!dom_object) + return; + if (!gc_protected_map().contains(dom_object)) + return; + + // Dispose the strong reference. + v8::Persistent<v8::Object> wrapper(gc_protected_map().take(dom_object)); + wrapper.Dispose(); +} + +class GCPrologueVisitor : public DOMWrapperMap<void>::Visitor { + public: + void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper) { + ASSERT(wrapper.IsWeak()); + V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); + switch (type) { +#define MAKE_CASE(TYPE, NAME) \ + case V8ClassIndex::TYPE: { \ + NAME* impl = static_cast<NAME*>(object); \ + if (impl->hasPendingActivity()) \ + wrapper.ClearWeak(); \ + break; \ + } +ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) + default: + ASSERT(false); +#undef MAKE_CASE + } + + // Additional handling of message port ensuring that entangled ports also + // have their wrappers entangled. This should ideally be handled when the + // ports are actually entangled in MessagePort::entangle, but to avoid + // forking MessagePort.* this is postponed to GC time. Having this postponed + // has the drawback that the wrappers are "entangled/unentangled" for each + // GC even though their entanglement most likely is still the same. + if (type == V8ClassIndex::MESSAGEPORT) { + // Get the port and its entangled port. + MessagePort* port1 = static_cast<MessagePort*>(object); + MessagePort* port2 = port1->locallyEntangledPort(); + + // If we are remotely entangled, then mark this object as reachable + // (we can't determine reachability directly as the remote object is + // out-of-proc). + if (port1->isEntangled() && !port2) + wrapper.ClearWeak(); + + if (port2 != NULL) { + // As ports are always entangled in pairs only perform the entanglement + // once for each pair (see ASSERT in MessagePort::unentangle()). + if (port1 < port2) { + v8::Handle<v8::Value> port1_wrapper = + V8Proxy::ToV8Object(V8ClassIndex::MESSAGEPORT, port1); + v8::Handle<v8::Value> port2_wrapper = + V8Proxy::ToV8Object(V8ClassIndex::MESSAGEPORT, port2); + ASSERT(port1_wrapper->IsObject()); + v8::Handle<v8::Object>::Cast(port1_wrapper)->SetInternalField( + V8Custom::kMessagePortEntangledPortIndex, port2_wrapper); + ASSERT(port2_wrapper->IsObject()); + v8::Handle<v8::Object>::Cast(port2_wrapper)->SetInternalField( + V8Custom::kMessagePortEntangledPortIndex, port1_wrapper); + } + } else { + // Remove the wrapper entanglement when a port is not entangled. + if (V8Proxy::DOMObjectHasJSWrapper(port1)) { + v8::Handle<v8::Value> wrapper = + V8Proxy::ToV8Object(V8ClassIndex::MESSAGEPORT, port1); + ASSERT(wrapper->IsObject()); + v8::Handle<v8::Object>::Cast(wrapper)->SetInternalField( + V8Custom::kMessagePortEntangledPortIndex, v8::Undefined()); + } + } + } + } +}; + +class GrouperItem { + public: + GrouperItem(uintptr_t group_id, Node* node, v8::Persistent<v8::Object> wrapper) + : group_id_(group_id), node_(node), wrapper_(wrapper) { } + + uintptr_t group_id() const { return group_id_; } + Node* node() const { return node_; } + v8::Persistent<v8::Object> wrapper() const { return wrapper_; } + + private: + uintptr_t group_id_; + Node* node_; + v8::Persistent<v8::Object> wrapper_; +}; + +bool operator<(const GrouperItem& a, const GrouperItem& b) { + return a.group_id() < b.group_id(); +} + +typedef Vector<GrouperItem> GrouperList; + +class ObjectGrouperVisitor : public DOMWrapperMap<Node>::Visitor { + public: + ObjectGrouperVisitor() { + // TODO(abarth): grouper_.reserveCapacity(node_map.size()); ? + } + + void visitDOMWrapper(Node* node, v8::Persistent<v8::Object> wrapper) { + // If the node is in document, put it in the ownerDocument's object group. + // + // If an image element was created by JavaScript "new Image", + // it is not in a document. However, if the load event has not + // been fired (still onloading), it is treated as in the document. + // + // Otherwise, the node is put in an object group identified by the root + // elment of the tree to which it belongs. + uintptr_t group_id; + if (node->inDocument() || + (node->hasTagName(HTMLNames::imgTag) && + !static_cast<HTMLImageElement*>(node)->haveFiredLoadEvent())) { + group_id = reinterpret_cast<uintptr_t>(node->document()); + } else { + Node* root = node; + while (root->parent()) + root = root->parent(); + + // If the node is alone in its DOM tree (doesn't have a parent or any + // children) then the group will be filtered out later anyway. + if (root == node && !node->hasChildNodes()) + return; + + group_id = reinterpret_cast<uintptr_t>(root); + } + grouper_.append(GrouperItem(group_id, node, wrapper)); + } + + void ApplyGrouping() { + // Group by sorting by the group id. + std::sort(grouper_.begin(), grouper_.end()); + + // TODO(deanm): Should probably work in iterators here, but indexes were + // easier for my simple mind. + for (size_t i = 0; i < grouper_.size(); ) { + // Seek to the next key (or the end of the list). + size_t next_key_index = grouper_.size(); + for (size_t j = i; j < grouper_.size(); ++j) { + if (grouper_[i].group_id() != grouper_[j].group_id()) { + next_key_index = j; + break; + } + } + + ASSERT(next_key_index > i); + + // We only care about a group if it has more than one object. If it only + // has one object, it has nothing else that needs to be kept alive. + if (next_key_index - i <= 1) { + i = next_key_index; + continue; + } + + Vector<v8::Persistent<v8::Value> > group; + group.reserveCapacity(next_key_index - i); + for (; i < next_key_index; ++i) { + Node* node = grouper_[i].node(); + v8::Persistent<v8::Value> wrapper = grouper_[i].wrapper(); + if (!wrapper.IsEmpty()) + group.append(wrapper); + /* TODO(abarth): Re-enabled this code to avoid GCing these wrappers! + Currently this depends on looking up the wrapper + during a GC, but we don't know which isolated world + we're in, so it's unclear which map to look in... + + // If the node is styled and there is a wrapper for the inline + // style declaration, we need to keep that style declaration + // wrapper alive as well, so we add it to the object group. + if (node->isStyledElement()) { + StyledElement* element = reinterpret_cast<StyledElement*>(node); + CSSStyleDeclaration* style = element->inlineStyleDecl(); + if (style != NULL) { + wrapper = getDOMObjectMap().get(style); + if (!wrapper.IsEmpty()) + group.append(wrapper); + } + } + */ + } + + if (group.size() > 1) + v8::V8::AddObjectGroup(&group[0], group.size()); + + ASSERT(i == next_key_index); + } + } + + private: + GrouperList grouper_; +}; + +// Create object groups for DOM tree nodes. +static void GCPrologue() +{ + v8::HandleScope scope; + +#ifndef NDEBUG + DOMObjectVisitor domObjectVisitor; + visitDOMObjectsInCurrentThread(&domObjectVisitor); +#endif + + // Run through all objects with possible pending activity making their + // wrappers non weak if there is pending activity. + GCPrologueVisitor prologueVisitor; + visitActiveDOMObjectsInCurrentThread(&prologueVisitor); + + // Create object groups. + ObjectGrouperVisitor objectGrouperVisitor; + visitDOMNodesInCurrentThread(&objectGrouperVisitor); + objectGrouperVisitor.ApplyGrouping(); +} + +class GCEpilogueVisitor : public DOMWrapperMap<void>::Visitor { + public: + void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper) + { + V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); + switch (type) { +#define MAKE_CASE(TYPE, NAME) \ + case V8ClassIndex::TYPE: { \ + NAME* impl = static_cast<NAME*>(object); \ + if (impl->hasPendingActivity()) { \ + ASSERT(!wrapper.IsWeak()); \ + wrapper.MakeWeak(impl, &weakActiveDOMObjectCallback); \ + } \ + break; \ + } +ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) + default: + ASSERT(false); +#undef MAKE_CASE + } + } +}; + +static void GCEpilogue() +{ + v8::HandleScope scope; + + // Run through all objects with pending activity making their wrappers weak + // again. + GCEpilogueVisitor epilogueVisitor; + visitActiveDOMObjectsInCurrentThread(&epilogueVisitor); + +#ifndef NDEBUG + // Check all survivals are weak. + DOMObjectVisitor domObjectVisitor; + visitDOMObjectsInCurrentThread(&domObjectVisitor); + + EnsureWeakDOMNodeVisitor weakDOMNodeVisitor; + visitDOMNodesInCurrentThread(&weakDOMNodeVisitor); + + EnumerateDOMObjectMap(gc_protected_map()); + EnumerateGlobalHandles(); +#undef USE_VAR +#endif +} + + +typedef HashMap<int, v8::FunctionTemplate*> FunctionTemplateMap; + +bool AllowAllocation::m_current = false; + + +// JavaScriptConsoleMessages encapsulate everything needed to +// log messages originating from JavaScript to the Chrome console. +class JavaScriptConsoleMessage { + public: + JavaScriptConsoleMessage(const String& str, + const String& sourceID, + unsigned lineNumber) + : m_string(str) + , m_sourceID(sourceID) + , m_lineNumber(lineNumber) { } + + void AddToPage(Page* 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); +} + +// The ConsoleMessageManager handles all console messages that stem +// from JavaScript. It keeps a list of messages that have been delayed but +// it makes sure to add all messages to the console in the right order. +class ConsoleMessageManager { + public: + // Add a message to the console. May end up calling JavaScript code + // indirectly through the inspector so only call this function when + // it is safe to do allocations. + static void AddMessage(Page* page, const JavaScriptConsoleMessage& message); + + // Add a message to the console but delay the reporting until it + // is safe to do so: Either when we leave JavaScript execution or + // when adding other console messages. The primary purpose of this + // method is to avoid calling into V8 to handle console messages + // when the VM is in a state that does not support GCs or allocations. + // Delayed messages are always reported in the page corresponding + // to the active context. + static void AddDelayedMessage(const JavaScriptConsoleMessage& message); + + // Process any delayed messages. May end up calling JavaScript code + // indirectly through the inspector so only call this function when + // it is safe to do allocations. + static void ProcessDelayedMessages(); + + private: + // All delayed messages are stored in this vector. If the vector + // is NULL, there are no delayed messages. + static Vector<JavaScriptConsoleMessage>* m_delayed; +}; + + +Vector<JavaScriptConsoleMessage>* ConsoleMessageManager::m_delayed = NULL; + + +void ConsoleMessageManager::AddMessage( + Page* page, + const JavaScriptConsoleMessage& message) +{ + // Process any delayed messages to make sure that messages + // appear in the right order in the console. + ProcessDelayedMessages(); + message.AddToPage(page); +} + + +void ConsoleMessageManager::AddDelayedMessage(const JavaScriptConsoleMessage& message) +{ + if (!m_delayed) + // Allocate a vector for the delayed messages. Will be + // deallocated when the delayed messages are processed + // in ProcessDelayedMessages(). + m_delayed = new Vector<JavaScriptConsoleMessage>(); + m_delayed->append(message); +} + + +void ConsoleMessageManager::ProcessDelayedMessages() +{ + // If we have a delayed vector it cannot be empty. + if (!m_delayed) + return; + ASSERT(!m_delayed->isEmpty()); + + // Add the delayed messages to the page of the active + // context. If that for some bizarre reason does not + // exist, we clear the list of delayed messages to avoid + // posting messages. We still deallocate the vector. + Frame* frame = V8Proxy::retrieveFrameForEnteredContext(); + Page* page = NULL; + if (frame) + page = frame->page(); + if (!page) + m_delayed->clear(); + + // Iterate through all the delayed messages and add them + // to the console. + const int size = m_delayed->size(); + for (int i = 0; i < size; i++) { + m_delayed->at(i).AddToPage(page); + } + + // Deallocate the delayed vector. + delete m_delayed; + m_delayed = NULL; +} + + +// Convenience class for ensuring that delayed messages in the +// ConsoleMessageManager are processed quickly. +class ConsoleMessageScope { + public: + ConsoleMessageScope() { ConsoleMessageManager::ProcessDelayedMessages(); } + ~ConsoleMessageScope() { ConsoleMessageManager::ProcessDelayedMessages(); } +}; + +void log_info(Frame* frame, const String& msg, const String& url) +{ + Page* page = frame->page(); + if (!page) + return; + JavaScriptConsoleMessage message(msg, url, 0); + ConsoleMessageManager::AddMessage(page, message); +} + +static void HandleConsoleMessage(v8::Handle<v8::Message> message, + v8::Handle<v8::Value> data) +{ + // Use the frame where JavaScript is called from. + 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 { + REPORT_LATER, + REPORT_NOW +}; + + +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; // Ignore error if the source document is gone. + + Document* sourceDocument = source->document(); + + // FIXME: This error message should contain more specifics of why the same + // origin check has failed. + 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()); + + // Build a console message with fake source ID and line number. + const String kSourceID = ""; + const int kLineNumber = 1; + JavaScriptConsoleMessage message(str, kSourceID, kLineNumber); + + if (delay == REPORT_NOW) { + // NOTE(tc): Apple prints the message in the target page, but it seems like + // it should be in the source page. Even for delayed messages, we put it in + // the source page; see ConsoleMessageManager::ProcessDelayedMessages(). + ConsoleMessageManager::AddMessage(source->page(), message); + + } else { + ASSERT(delay == REPORT_LATER); + // We cannot safely report the message eagerly, because this may cause + // allocations and GCs internally in V8 and we cannot handle that at this + // point. Therefore we delay the reporting. + 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, REPORT_LATER); +} + +static void HandleFatalErrorInV8() +{ + // TODO: We temporarily deal with V8 internal error situations + // such as out-of-memory by crashing the renderer. + CRASH(); +} + +static void ReportFatalErrorInV8(const char* location, const char* message) +{ + // V8 is shutdown, we cannot use V8 api. + // The only thing we can do is to disable JavaScript. + // TODO: clean up V8Proxy and disable JavaScript. + printf("V8 error: %s (%s)\n", message, location); + HandleFatalErrorInV8(); +} + +V8Proxy::~V8Proxy() +{ + clearForClose(); + DestroyGlobal(); +} + +void V8Proxy::DestroyGlobal() +{ + if (!m_global.IsEmpty()) { +#ifndef NDEBUG + UnregisterGlobalHandle(this, m_global); +#endif + m_global.Dispose(); + m_global.Clear(); + } +} + + +bool V8Proxy::DOMObjectHasJSWrapper(void* obj) { + return getDOMObjectMap().contains(obj) || + getActiveDOMObjectMap().contains(obj); +} + + +// The caller must have increased obj's ref count. +void V8Proxy::SetJSWrapperForDOMObject(void* obj, v8::Persistent<v8::Object> wrapper) +{ + ASSERT(MaybeDOMWrapper(wrapper)); +#ifndef NDEBUG + V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); + switch (type) { +#define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE: +ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) + ASSERT(false); +#undef MAKE_CASE + default: break; + } +#endif + getDOMObjectMap().set(obj, wrapper); +} + +// The caller must have increased obj's ref count. +void V8Proxy::SetJSWrapperForActiveDOMObject(void* obj, v8::Persistent<v8::Object> wrapper) +{ + ASSERT(MaybeDOMWrapper(wrapper)); +#ifndef NDEBUG + V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); + switch (type) { +#define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE: break; +ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) + default: ASSERT(false); +#undef MAKE_CASE + } +#endif + getActiveDOMObjectMap().set(obj, wrapper); +} + +// The caller must have increased node's ref count. +void V8Proxy::SetJSWrapperForDOMNode(Node* node, v8::Persistent<v8::Object> wrapper) +{ + ASSERT(MaybeDOMWrapper(wrapper)); + getDOMNodeMap().set(node, wrapper); +} + +// Event listeners + +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); +} + +// Find an existing wrapper for a JS event listener in the map. +PassRefPtr<V8EventListener> V8Proxy::FindV8EventListener(v8::Local<v8::Value> listener, + bool isInline) +{ + return FindEventListenerInList(m_event_listeners, listener, isInline); +} + +PassRefPtr<V8EventListener> V8Proxy::FindOrCreateV8EventListener(v8::Local<v8::Value> obj, bool isInline) +{ + ASSERT(v8::Context::InContext()); + + if (!obj->IsObject()) + return 0; + + V8EventListener* wrapper = + FindEventListenerInList(m_event_listeners, obj, isInline); + if (wrapper) + return wrapper; + + // Create a new one, and add to cache. + RefPtr<V8EventListener> new_listener = + V8EventListener::create(m_frame, v8::Local<v8::Object>::Cast(obj), isInline); + m_event_listeners.add(new_listener.get()); + + return new_listener; +} + + +// Object event listeners (such as XmlHttpRequest and MessagePort) are +// different from listeners on DOM nodes. An object event listener wrapper +// only holds a weak reference to the JS function. A strong reference can +// create a cycle. +// +// The lifetime of these objects is bounded by the life time of its JS +// wrapper. So we can create a hidden reference from the JS wrapper to +// to its JS function. +// +// (map) +// XHR <---------- JS_wrapper +// | (hidden) : ^ +// V V : (may reachable by closure) +// V8_listener --------> JS_function +// (weak) <-- may create a cycle if it is strong +// +// The persistent reference is made weak in the constructor +// of V8ObjectEventListener. + +PassRefPtr<V8EventListener> V8Proxy::FindObjectEventListener( + v8::Local<v8::Value> listener, bool isInline) +{ + return FindEventListenerInList(m_xhr_listeners, listener, isInline); +} + + +PassRefPtr<V8EventListener> V8Proxy::FindOrCreateObjectEventListener( + v8::Local<v8::Value> obj, bool isInline) +{ + ASSERT(v8::Context::InContext()); + + if (!obj->IsObject()) + return 0; + + V8EventListener* wrapper = + FindEventListenerInList(m_xhr_listeners, obj, isInline); + if (wrapper) + return wrapper; + + // Create a new one, and add to cache. + RefPtr<V8EventListener> new_listener = + V8ObjectEventListener::create(m_frame, v8::Local<v8::Object>::Cast(obj), isInline); + m_xhr_listeners.add(new_listener.get()); + + return new_listener.release(); +} + + +static void RemoveEventListenerFromList(V8EventListenerList& list, + V8EventListener* listener) +{ + list.remove(listener); +} + + +void V8Proxy::RemoveV8EventListener(V8EventListener* listener) +{ + RemoveEventListenerFromList(m_event_listeners, listener); +} + + +void V8Proxy::RemoveObjectEventListener(V8ObjectEventListener* listener) +{ + RemoveEventListenerFromList(m_xhr_listeners, listener); +} + + +static void DisconnectEventListenersInList(V8EventListenerList& list) +{ + V8EventListenerList::iterator p = list.begin(); + while (p != list.end()) { + (*p)->disconnectFrame(); + ++p; + } + list.clear(); +} + + +void V8Proxy::DisconnectEventListeners() +{ + DisconnectEventListenersInList(m_event_listeners); + DisconnectEventListenersInList(m_xhr_listeners); +} + + +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; + + // Warning, error, disable JS for this frame? + Frame* frame = V8Proxy::retrieveFrame(context); + + V8Proxy* proxy = V8Proxy::retrieve(frame); + if (proxy != NULL) { + // Clean m_context, and event handlers. + proxy->clearForClose(); + // Destroy the global object. + proxy->DestroyGlobal(); + } + + ChromiumBridge::notifyJSOutOfMemory(frame); + + // Disable JS. + 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; + + // Set up the DOM window as the prototype of the new global object. + v8::Handle<v8::Context> windowContext = m_context; + v8::Handle<v8::Object> windowGlobal = windowContext->Global(); + v8::Handle<v8::Value> windowWrapper = + V8Proxy::LookupDOMWrapper(V8ClassIndex::DOMWINDOW, windowGlobal); + + ASSERT(V8Proxy::DOMWrapperToNative<DOMWindow>(windowWrapper) == + m_frame->domWindow()); + + v8::Persistent<v8::Context> context = + createNewContext(v8::Handle<v8::Object>()); + v8::Context::Scope context_scope(context); + + // Setup context id for JS debugger. + v8::Handle<v8::Object> context_data = v8::Object::New(); + v8::Handle<v8::Value> window_context_data = windowContext->GetData(); + if (window_context_data->IsObject()) { + v8::Handle<v8::String> property_name = + v8::String::New(kContextDebugDataValue); + context_data->Set( + property_name, + v8::Object::Cast(*window_context_data)->Get(property_name)); + } + context_data->Set(v8::String::New(kContextDebugDataType), + v8::String::New("injected")); + context->SetData(context_data); + + v8::Handle<v8::Object> global = context->Global(); + + v8::Handle<v8::String> implicitProtoString = v8::String::New("__proto__"); + global->Set(implicitProtoString, windowWrapper); + + // Give the code running in the new context a way to get access to the + // original context. + global->Set(v8::String::New("contentWindow"), windowGlobal); + + // Run code in the new context. + for (size_t i = 0; i < sources.size(); ++i) + evaluate(sources[i], 0); + + // Using the default security token means that the canAccess is always + // called, which is slow. + // TODO(aa): Use tokens where possible. This will mean keeping track of all + // created contexts so that they can all be updated when the document domain + // changes. + context->UseDefaultSecurityToken(); + context.Dispose(); +} + +v8::Local<v8::Value> V8Proxy::evaluate(const ScriptSourceCode& source, Node* n) +{ + ASSERT(v8::Context::InContext()); + + // Compile the script. + v8::Local<v8::String> code = v8ExternalString(source.source()); + ChromiumBridge::traceEventBegin("v8.compile", n, ""); + + // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at + // 1, whereas v8 starts at 0. + v8::Handle<v8::Script> script = CompileScript(code, source.url(), + source.startLine() - 1); + ChromiumBridge::traceEventEnd("v8.compile", n, ""); + + ChromiumBridge::traceEventBegin("v8.run", n, ""); + v8::Local<v8::Value> result; + { + // Isolate exceptions that occur when executing the code. These + // exceptions should not interfere with javascript code we might + // evaluate from C++ when returning from here + v8::TryCatch try_catch; + try_catch.SetVerbose(true); + + // Set inlineCode to true for <a href="javascript:doSomething()"> + // and false for <script>doSomething</script>. We make a rough guess at + // this based on whether the script source has a URL. + result = RunScript(script, source.url().string().isNull()); + } + ChromiumBridge::traceEventEnd("v8.run", n, ""); + return result; +} + +v8::Local<v8::Value> V8Proxy::RunScript(v8::Handle<v8::Script> script, + bool inline_code) +{ + if (script.IsEmpty()) + return v8::Local<v8::Value>(); + + // Compute the source string and prevent against infinite recursion. + if (m_recursion >= kMaxRecursionDepth) { + v8::Local<v8::String> code = + v8ExternalString("throw RangeError('Recursion too deep')"); + // TODO(kasperl): Ideally, we should be able to re-use the origin of the + // script passed to us as the argument instead of using an empty string + // and 0 baseLine. + script = CompileScript(code, "", 0); + } + + if (HandleOutOfMemory()) + ASSERT(script.IsEmpty()); + + if (script.IsEmpty()) + return v8::Local<v8::Value>(); + + // Save the previous value of the inlineCode flag and update the flag for + // the duration of the script invocation. + bool previous_inline_code = inlineCode(); + setInlineCode(inline_code); + + // Run the script and keep track of the current recursion depth. + v8::Local<v8::Value> result; + { ConsoleMessageScope scope; + m_recursion++; + + // Evaluating the JavaScript could cause the frame to be deallocated, + // so we start the keep alive timer here. + // Frame::keepAlive method adds the ref count of the frame and sets a + // timer to decrease the ref count. It assumes that the current JavaScript + // execution finishs before firing the timer. + // See issue 1218756 and 914430. + m_frame->keepAlive(); + + result = script->Run(); + m_recursion--; + } + + if (HandleOutOfMemory()) + ASSERT(result.IsEmpty()); + + // Handle V8 internal error situation (Out-of-memory). + if (result.IsEmpty()) + return v8::Local<v8::Value>(); + + // Restore inlineCode flag. + setInlineCode(previous_inline_code); + + 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[]) +{ + // For now, we don't put any artificial limitations on the depth + // of recursion that stems from calling functions. This is in + // contrast to the script evaluations. + v8::Local<v8::Value> result; + { + ConsoleMessageScope scope; + + // Evaluating the JavaScript could cause the frame to be deallocated, + // so we start the keep alive timer here. + // Frame::keepAlive method adds the ref count of the frame and sets a + // timer to decrease the ref count. It assumes that the current JavaScript + // execution finishs before firing the timer. + // See issue 1218756 and 914430. + 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[]) +{ + // No artificial limitations on the depth of recursion, see comment in + // V8Proxy::CallFunction. + v8::Local<v8::Value> result; + { + ConsoleMessageScope scope; + + // See comment in V8Proxy::CallFunction. + m_frame->keepAlive(); + + result = constructor->NewInstance(argc, args); + } + + if (v8::V8::IsDead()) + HandleFatalErrorInV8(); + + return result; +} + + +v8::Local<v8::Function> V8Proxy::GetConstructor(V8ClassIndex::V8WrapperType t){ + // A DOM constructor is a function instance created from a DOM constructor + // template. There is one instance per context. A DOM constructor is + // different from a normal function in two ways: + // 1) it cannot be called as constructor (aka, used to create a DOM object) + // 2) its __proto__ points to Object.prototype rather than + // Function.prototype. + // The reason for 2) is that, in Safari, a DOM constructor is a normal JS + // object, but not a function. Hotmail relies on the fact that, in Safari, + // HTMLElement.__proto__ == Object.prototype. + // + // m_object_prototype is a cache of the original Object.prototype. + + ASSERT(ContextInitialized()); + // Enter the context of the proxy to make sure that the + // function is constructed in the context corresponding to + // this proxy. + v8::Context::Scope scope(m_context); + v8::Handle<v8::FunctionTemplate> templ = GetTemplate(t); + // Getting the function might fail if we're running out of + // stack or memory. + v8::TryCatch try_catch; + v8::Local<v8::Function> value = templ->GetFunction(); + if (value.IsEmpty()) + return v8::Local<v8::Function>(); + // Hotmail fix, see comments above. + value->Set(v8::String::New("__proto__"), m_object_prototype); + return value; +} + + +v8::Local<v8::Object> V8Proxy::CreateWrapperFromCache(V8ClassIndex::V8WrapperType type) { + int class_index = V8ClassIndex::ToInt(type); + v8::Local<v8::Value> cached_object = + m_wrapper_boilerplates->Get(v8::Integer::New(class_index)); + if (cached_object->IsObject()) { + v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(cached_object); + return object->Clone(); + } + + // Not in cache. + 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_wrapper_boilerplates->Set(v8::Integer::New(class_index), instance); + return instance->Clone(); + } + return v8::Local<v8::Object>(); +} + + +// Get the string 'toString'. +static v8::Persistent<v8::String> GetToStringName() { + static v8::Persistent<v8::String> value; + if (value.IsEmpty()) + value = v8::Persistent<v8::String>::New(v8::String::New("toString")); + return value; +} + + +static v8::Handle<v8::Value> ConstructorToString(const v8::Arguments& args) { + // The DOM constructors' toString functions grab the current toString + // for Functions by taking the toString function of itself and then + // calling it with the constructor as its receiver. This means that + // changes to the Function prototype chain or toString function are + // reflected when printing DOM constructors. The only wart is that + // changes to a DOM constructor's toString's toString will cause the + // toString of the DOM constructor itself to change. This is extremely + // obscure and unlikely to be a problem. + v8::Handle<v8::Value> val = args.Callee()->Get(GetToStringName()); + if (!val->IsFunction()) return v8::String::New(""); + return v8::Handle<v8::Function>::Cast(val)->Call(args.This(), 0, NULL); +} + + +v8::Persistent<v8::FunctionTemplate> V8Proxy::GetTemplate( + V8ClassIndex::V8WrapperType type) +{ + v8::Persistent<v8::FunctionTemplate>* cache_cell = + V8ClassIndex::GetCache(type); + if (!(*cache_cell).IsEmpty()) + return *cache_cell; + + // not found + FunctionTemplateFactory factory = V8ClassIndex::GetFactory(type); + v8::Persistent<v8::FunctionTemplate> desc = factory(); + // DOM constructors are functions and should print themselves as such. + // However, we will later replace their prototypes with Object + // prototypes so we need to explicitly override toString on the + // instance itself. If we later make DOM constructors full objects + // we can give them class names instead and Object.prototype.toString + // will work so we can remove this code. + static v8::Persistent<v8::FunctionTemplate> to_string_template; + if (to_string_template.IsEmpty()) { + to_string_template = v8::Persistent<v8::FunctionTemplate>::New( + v8::FunctionTemplate::New(ConstructorToString)); + } + desc->Set(GetToStringName(), to_string_template); + switch (type) { + case V8ClassIndex::CSSSTYLEDECLARATION: + // The named property handler for style declarations has a + // setter. Therefore, the interceptor has to be on the object + // itself and not on the prototype object. + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(CSSStyleDeclaration), + USE_NAMED_PROPERTY_SETTER(CSSStyleDeclaration)); + setCollectionStringOrNullIndexedGetter<CSSStyleDeclaration>(desc); + break; + case V8ClassIndex::CSSRULELIST: + setCollectionIndexedGetter<CSSRuleList, CSSRule>(desc, + V8ClassIndex::CSSRULE); + break; + case V8ClassIndex::CSSVALUELIST: + setCollectionIndexedGetter<CSSValueList, CSSValue>( + desc, + V8ClassIndex::CSSVALUE); + break; + case V8ClassIndex::CSSVARIABLESDECLARATION: + setCollectionStringOrNullIndexedGetter<CSSVariablesDeclaration>(desc); + break; + case V8ClassIndex::WEBKITCSSTRANSFORMVALUE: + setCollectionIndexedGetter<WebKitCSSTransformValue, CSSValue>( + desc, + V8ClassIndex::CSSVALUE); + break; + case V8ClassIndex::UNDETECTABLEHTMLCOLLECTION: + desc->InstanceTemplate()->MarkAsUndetectable(); // fall through + case V8ClassIndex::HTMLCOLLECTION: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLCollection)); + desc->InstanceTemplate()->SetCallAsFunctionHandler( + USE_CALLBACK(HTMLCollectionCallAsFunction)); + setCollectionIndexedGetter<HTMLCollection, Node>(desc, + V8ClassIndex::NODE); + break; + case V8ClassIndex::HTMLOPTIONSCOLLECTION: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLCollection)); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(HTMLOptionsCollection), + USE_INDEXED_PROPERTY_SETTER(HTMLOptionsCollection)); + desc->InstanceTemplate()->SetCallAsFunctionHandler( + USE_CALLBACK(HTMLCollectionCallAsFunction)); + break; + case V8ClassIndex::HTMLSELECTELEMENT: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLSelectElementCollection)); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + nodeCollectionIndexedPropertyGetter<HTMLSelectElement>, + USE_INDEXED_PROPERTY_SETTER(HTMLSelectElementCollection), + 0, + 0, + nodeCollectionIndexedPropertyEnumerator<HTMLSelectElement>, + v8::Integer::New(V8ClassIndex::NODE)); + break; + case V8ClassIndex::HTMLDOCUMENT: { + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLDocument), + 0, + 0, + USE_NAMED_PROPERTY_DELETER(HTMLDocument)); + + // We add an extra internal field to all Document wrappers for + // storing a per document DOMImplementation wrapper. + // + // Additionally, we add two extra internal fields for + // HTMLDocuments to implement temporary shadowing of + // document.all. One field holds an object that is used as a + // marker. The other field holds the marker object if + // document.all is not shadowed and some other value if + // document.all is shadowed. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + ASSERT(instance_template->InternalFieldCount() == + V8Custom::kDefaultWrapperInternalFieldCount); + instance_template->SetInternalFieldCount( + V8Custom::kHTMLDocumentInternalFieldCount); + break; + } +#if ENABLE(SVG) + case V8ClassIndex::SVGDOCUMENT: // fall through +#endif + case V8ClassIndex::DOCUMENT: { + // We add an extra internal field to all Document wrappers for + // storing a per document DOMImplementation wrapper. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + ASSERT(instance_template->InternalFieldCount() == + V8Custom::kDefaultWrapperInternalFieldCount); + instance_template->SetInternalFieldCount( + V8Custom::kDocumentMinimumInternalFieldCount); + break; + } + case V8ClassIndex::HTMLAPPLETELEMENT: // fall through + case V8ClassIndex::HTMLEMBEDELEMENT: // fall through + case V8ClassIndex::HTMLOBJECTELEMENT: + // HTMLAppletElement, HTMLEmbedElement and HTMLObjectElement are + // inherited from HTMLPlugInElement, and they share the same property + // handling code. + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLPlugInElement), + USE_NAMED_PROPERTY_SETTER(HTMLPlugInElement)); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(HTMLPlugInElement), + USE_INDEXED_PROPERTY_SETTER(HTMLPlugInElement)); + desc->InstanceTemplate()->SetCallAsFunctionHandler( + USE_CALLBACK(HTMLPlugInElement)); + break; + case V8ClassIndex::HTMLFRAMESETELEMENT: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLFrameSetElement)); + break; + case V8ClassIndex::HTMLFORMELEMENT: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLFormElement)); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(HTMLFormElement), + 0, + 0, + 0, + nodeCollectionIndexedPropertyEnumerator<HTMLFormElement>, + v8::Integer::New(V8ClassIndex::NODE)); + break; + case V8ClassIndex::CANVASPIXELARRAY: + desc->InstanceTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(CanvasPixelArray), + USE_INDEXED_PROPERTY_SETTER(CanvasPixelArray)); + break; + case V8ClassIndex::STYLESHEET: // fall through + case V8ClassIndex::CSSSTYLESHEET: { + // We add an extra internal field to hold a reference to + // the owner node. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + ASSERT(instance_template->InternalFieldCount() == + V8Custom::kDefaultWrapperInternalFieldCount); + instance_template->SetInternalFieldCount( + V8Custom::kStyleSheetInternalFieldCount); + break; + } + case V8ClassIndex::MEDIALIST: + setCollectionStringOrNullIndexedGetter<MediaList>(desc); + break; + case V8ClassIndex::MIMETYPEARRAY: + setCollectionIndexedAndNamedGetters<MimeTypeArray, MimeType>( + desc, + V8ClassIndex::MIMETYPE); + break; + case V8ClassIndex::NAMEDNODEMAP: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(NamedNodeMap)); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(NamedNodeMap), + 0, + 0, + 0, + collectionIndexedPropertyEnumerator<NamedNodeMap>, + v8::Integer::New(V8ClassIndex::NODE)); + break; +#if ENABLE(DOM_STORAGE) + case V8ClassIndex::STORAGE: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(Storage), + USE_NAMED_PROPERTY_SETTER(Storage), + 0, + USE_NAMED_PROPERTY_DELETER(Storage), + V8Custom::v8StorageNamedPropertyEnumerator); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(Storage), + USE_INDEXED_PROPERTY_SETTER(Storage), + 0, + USE_INDEXED_PROPERTY_DELETER(Storage)); + break; +#endif + case V8ClassIndex::NODELIST: + setCollectionIndexedGetter<NodeList, Node>(desc, V8ClassIndex::NODE); + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(NodeList)); + break; + case V8ClassIndex::PLUGIN: + setCollectionIndexedAndNamedGetters<Plugin, MimeType>( + desc, + V8ClassIndex::MIMETYPE); + break; + case V8ClassIndex::PLUGINARRAY: + setCollectionIndexedAndNamedGetters<PluginArray, Plugin>( + desc, + V8ClassIndex::PLUGIN); + break; + case V8ClassIndex::STYLESHEETLIST: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(StyleSheetList)); + setCollectionIndexedGetter<StyleSheetList, StyleSheet>( + desc, + V8ClassIndex::STYLESHEET); + break; + case V8ClassIndex::DOMWINDOW: { + v8::Local<v8::Signature> default_signature = v8::Signature::New(desc); + + desc->PrototypeTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(DOMWindow)); + desc->PrototypeTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(DOMWindow)); + + desc->SetHiddenPrototype(true); + + // Reserve spaces for references to location, history and + // navigator objects. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + instance_template->SetInternalFieldCount( + V8Custom::kDOMWindowInternalFieldCount); + + // Set access check callbacks, but turned off initially. + // When a context is detached from a frame, turn on the access check. + // Turning on checks also invalidates inline caches of the object. + instance_template->SetAccessCheckCallbacks( + V8Custom::v8DOMWindowNamedSecurityCheck, + V8Custom::v8DOMWindowIndexedSecurityCheck, + v8::Integer::New(V8ClassIndex::DOMWINDOW), + false); + break; + } + case V8ClassIndex::LOCATION: { + // For security reasons, these functions are on the instance + // instead of on the prototype object to insure that they cannot + // be overwritten. + v8::Local<v8::ObjectTemplate> instance = desc->InstanceTemplate(); + instance->SetAccessor( + v8::String::New("reload"), + V8Custom::v8LocationReloadAccessorGetter, + 0, + v8::Handle<v8::Value>(), + v8::ALL_CAN_READ, + static_cast<v8::PropertyAttribute>(v8::DontDelete|v8::ReadOnly)); + + instance->SetAccessor( + v8::String::New("replace"), + V8Custom::v8LocationReplaceAccessorGetter, + 0, + v8::Handle<v8::Value>(), + v8::ALL_CAN_READ, + static_cast<v8::PropertyAttribute>(v8::DontDelete|v8::ReadOnly)); + + instance->SetAccessor( + v8::String::New("assign"), + V8Custom::v8LocationAssignAccessorGetter, + 0, + v8::Handle<v8::Value>(), + v8::ALL_CAN_READ, + static_cast<v8::PropertyAttribute>(v8::DontDelete|v8::ReadOnly)); + break; + } + case V8ClassIndex::HISTORY: { + break; + } + + case V8ClassIndex::MESSAGECHANNEL: { + // Reserve two more internal fields for referencing the port1 + // and port2 wrappers. This ensures that the port wrappers are + // kept alive when the channel wrapper is. + desc->SetCallHandler(USE_CALLBACK(MessageChannelConstructor)); + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + instance_template->SetInternalFieldCount( + V8Custom::kMessageChannelInternalFieldCount); + break; + } + + case V8ClassIndex::MESSAGEPORT: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + instance_template->SetInternalFieldCount( + V8Custom::kMessagePortInternalFieldCount); + break; + } + +#if ENABLE(WORKERS) + case V8ClassIndex::WORKER: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + instance_template->SetInternalFieldCount( + V8Custom::kWorkerInternalFieldCount); + desc->SetCallHandler(USE_CALLBACK(WorkerConstructor)); + break; + } + + case V8ClassIndex::WORKERCONTEXT: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + instance_template->SetInternalFieldCount( + V8Custom::kWorkerContextInternalFieldCount); + break; + } +#endif // WORKERS + + + // The following objects are created from JavaScript. + case V8ClassIndex::DOMPARSER: + desc->SetCallHandler(USE_CALLBACK(DOMParserConstructor)); + break; +#if ENABLE(VIDEO) + case V8ClassIndex::HTMLAUDIOELEMENT: + desc->SetCallHandler(USE_CALLBACK(HTMLAudioElementConstructor)); + break; +#endif + case V8ClassIndex::HTMLIMAGEELEMENT: + desc->SetCallHandler(USE_CALLBACK(HTMLImageElementConstructor)); + break; + case V8ClassIndex::HTMLOPTIONELEMENT: + desc->SetCallHandler(USE_CALLBACK(HTMLOptionElementConstructor)); + break; + case V8ClassIndex::WEBKITCSSMATRIX: + desc->SetCallHandler(USE_CALLBACK(WebKitCSSMatrixConstructor)); + break; + case V8ClassIndex::WEBKITPOINT: + desc->SetCallHandler(USE_CALLBACK(WebKitPointConstructor)); + break; + case V8ClassIndex::XMLSERIALIZER: + desc->SetCallHandler(USE_CALLBACK(XMLSerializerConstructor)); + break; + case V8ClassIndex::XMLHTTPREQUEST: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + instance_template->SetInternalFieldCount( + V8Custom::kXMLHttpRequestInternalFieldCount); + desc->SetCallHandler(USE_CALLBACK(XMLHttpRequestConstructor)); + break; + } + case V8ClassIndex::XMLHTTPREQUESTUPLOAD: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + instance_template->SetInternalFieldCount( + V8Custom::kXMLHttpRequestInternalFieldCount); + break; + } + case V8ClassIndex::XPATHEVALUATOR: + desc->SetCallHandler(USE_CALLBACK(XPathEvaluatorConstructor)); + break; + case V8ClassIndex::XSLTPROCESSOR: + desc->SetCallHandler(USE_CALLBACK(XSLTProcessorConstructor)); + break; + case V8ClassIndex::CLIENTRECTLIST: + desc->InstanceTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(ClientRectList)); + break; + default: + break; + } + + *cache_cell = desc; + return desc; +} + + +bool V8Proxy::ContextInitialized() +{ + // m_context, m_global, m_object_prototype and m_wrapper_boilerplates should + // all be non-empty if if m_context is non-empty. + ASSERT(m_context.IsEmpty() || !m_global.IsEmpty()); + ASSERT(m_context.IsEmpty() || !m_object_prototype.IsEmpty()); + ASSERT(m_context.IsEmpty() || !m_wrapper_boilerplates.IsEmpty()); + return !m_context.IsEmpty(); +} + + +DOMWindow* V8Proxy::retrieveWindow() +{ + // TODO: This seems very fragile. How do we know that the global object + // from the current context is something sensible? Do we need to use the + // last entered here? Who calls this? + return retrieveWindow(v8::Context::GetCurrent()); +} + + +DOMWindow* V8Proxy::retrieveWindow(v8::Handle<v8::Context> context) +{ + v8::Handle<v8::Object> global = context->Global(); + ASSERT(!global.IsEmpty()); + global = LookupDOMWrapper(V8ClassIndex::DOMWINDOW, global); + ASSERT(!global.IsEmpty()); + return ToNativeObject<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() +{ + // disconnect all event listeners + DisconnectEventListeners(); +} + + +bool V8Proxy::isEnabled() +{ + Settings* settings = m_frame->settings(); + if (!settings) + return false; + + // In the common case, JavaScript is enabled and we're done. + if (settings->isJavaScriptEnabled()) + return true; + + // If JavaScript has been disabled, we need to look at the frame to tell + // whether this script came from the web or the embedder. Scripts from the + // embedder are safe to run, but scripts from the other sources are + // disallowed. + Document* document = m_frame->document(); + if (!document) + return false; + + SecurityOrigin* origin = document->securityOrigin(); + if (origin->protocol().isEmpty()) + return false; // Uninitialized document + + if (origin->protocol() == "http" || origin->protocol() == "https") + return false; // Web site + + // TODO(darin): the following are application decisions, and they should + // not be made at this layer. instead, we should bridge out to the + // embedder to allow them to override policy here. + + if (origin->protocol() == ChromiumBridge::uiResourceProtocol()) + return true; // Embedder's scripts are ok to run + + // If the scheme is ftp: or file:, an empty file name indicates a directory + // listing, which requires JavaScript to function properly. + 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; // Other protocols fall through to here +} + + +void V8Proxy::UpdateDocumentWrapper(v8::Handle<v8::Value> wrapper) { + ClearDocumentWrapper(); + + ASSERT(m_document.IsEmpty()); + m_document = v8::Persistent<v8::Value>::New(wrapper); +#ifndef NDEBUG + RegisterGlobalHandle(PROXY, this, m_document); +#endif +} + + +void V8Proxy::ClearDocumentWrapper() +{ + if (!m_document.IsEmpty()) { +#ifndef NDEBUG + UnregisterGlobalHandle(this, m_document); +#endif + m_document.Dispose(); + m_document.Clear(); + } +} + + +void V8Proxy::UpdateDocumentWrapperCache() +{ + v8::HandleScope handle_scope; + v8::Context::Scope context_scope(GetContext()); + + // If the document has no frame, NodeToV8Object might get the + // document wrapper for a document that is about to be deleted. + // If the ForceSet below causes a garbage collection, the document + // might get deleted and the global handle for the document + // wrapper cleared. Using the cleared global handle will lead to + // crashes. In this case we clear the cache and let the DOMWindow + // accessor handle access to the document. + if (!m_frame->document()->frame()) { + ClearDocumentWrapperCache(); + return; + } + + v8::Handle<v8::Value> document_wrapper = NodeToV8Object(m_frame->document()); + + // If instantiation of the document wrapper fails, clear the cache + // and let the DOMWindow accessor handle access to the document. + if (document_wrapper.IsEmpty()) { + ClearDocumentWrapperCache(); + return; + } + + m_context->Global()->ForceSet(v8::String::New("document"), + document_wrapper, + 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_wrapper_boilerplates.IsEmpty()) { +#ifndef NDEBUG + UnregisterGlobalHandle(this, m_wrapper_boilerplates); +#endif + m_wrapper_boilerplates.Dispose(); + m_wrapper_boilerplates.Clear(); + } + + if (!m_object_prototype.IsEmpty()) { +#ifndef NDEBUG + UnregisterGlobalHandle(this, m_object_prototype); +#endif + m_object_prototype.Dispose(); + m_object_prototype.Clear(); + } +} + +void V8Proxy::clearForClose() +{ + if (!m_context.IsEmpty()) { + v8::HandleScope handle_scope; + + ClearDocumentWrapper(); + DisposeContextHandles(); + } +} + + +void V8Proxy::clearForNavigation() +{ + // disconnect all event listeners + DisconnectEventListeners(); + + if (!m_context.IsEmpty()) { + v8::HandleScope handle; + ClearDocumentWrapper(); + + v8::Context::Scope context_scope(m_context); + + // Clear the document wrapper cache before turning on access checks on + // the old DOMWindow wrapper. This way, access to the document wrapper + // will be protected by the security checks on the DOMWindow wrapper. + ClearDocumentWrapperCache(); + + // Turn on access check on the old DOMWindow wrapper. + v8::Handle<v8::Object> wrapper = + LookupDOMWrapper(V8ClassIndex::DOMWINDOW, m_global); + ASSERT(!wrapper.IsEmpty()); + wrapper->TurnOnAccessCheck(); + + // Separate the context from its global object. + m_context->DetachGlobal(); + + DisposeContextHandles(); + + // Reinitialize the context so the global object points to + // the new DOM window. + InitContextIfNeeded(); + } +} + + +void V8Proxy::SetSecurityToken() { + Document* document = m_frame->document(); + // Setup security origin and security token + if (!document) { + m_context->UseDefaultSecurityToken(); + return; + } + + // Ask the document's SecurityOrigin to generate a security token. + // If two tokens are equal, then the SecurityOrigins canAccess each other. + // If two tokens are not equal, then we have to call canAccess. + // Note: we can't use the HTTPOrigin if it was set from the DOM. + SecurityOrigin* origin = document->securityOrigin(); + String token; + if (!origin->domainWasSetInDOM()) + token = document->securityOrigin()->toString(); + + // An empty or "null" token means we always have to call + // canAccess. The toString method on securityOrigins returns the + // string "null" for empty security origins and for security + // origins that should only allow access to themselves. In this + // case, we use the global object as the security token to avoid + // calling canAccess when a script accesses its own objects. + if (token.isEmpty() || token == "null") { + m_context->UseDefaultSecurityToken(); + return; + } + + CString utf8_token = token.utf8(); + // NOTE: V8 does identity comparison in fast path, must use a symbol + // as the security token. + m_context->SetSecurityToken( + v8::String::NewSymbol(utf8_token.data(), utf8_token.length())); +} + + +void V8Proxy::updateDocument() +{ + if (!m_frame->document()) + return; + + if (m_global.IsEmpty()) { + ASSERT(m_context.IsEmpty()); + return; + } + + // We have a new document and we need to update the cache. + UpdateDocumentWrapperCache(); + + updateSecurityOrigin(); +} + +void V8Proxy::updateSecurityOrigin() +{ + v8::HandleScope scope; + SetSecurityToken(); +} + +// Same origin policy implementation: +// +// Same origin policy prevents JS code from domain A access JS & DOM objects +// in a different domain B. There are exceptions and several objects are +// accessible by cross-domain code. For example, the window.frames object is +// accessible by code from a different domain, but window.document is not. +// +// The binding code sets security check callbacks on a function template, +// and accessing instances of the template calls the callback function. +// The callback function checks same origin policy. +// +// Callback functions are expensive. V8 uses a security token string to do +// fast access checks for the common case where source and target are in the +// same domain. A security token is a string object that represents +// the protocol/url/port of a domain. +// +// There are special cases where a security token matching is not enough. +// For example, JavaScript can set its domain to a super domain by calling +// document.setDomain(...). In these cases, the binding code can reset +// a context's security token to its global object so that the fast access +// check will always fail. + +// Check if the current execution context can access a target frame. +// First it checks same domain policy using the lexical context +// +// This is equivalent to KJS::Window::allowsAccessFrom(ExecState*, String&). +bool V8Proxy::CanAccessPrivate(DOMWindow* target_window) +{ + ASSERT(target_window); + + String message; + + DOMWindow* origin_window = retrieveWindow(); + if (origin_window == target_window) + return true; + + if (!origin_window) + return false; + + const SecurityOrigin* active_security_origin = origin_window->securityOrigin(); + const SecurityOrigin* target_security_origin = target_window->securityOrigin(); + + // We have seen crashes were the security origin of the target has not been + // initialized. Defend against that. + if (!target_security_origin) + return false; + + if (active_security_origin->canAccess(target_security_origin)) + return true; + + // Allow access to a "about:blank" page if the dynamic context is a + // detached context of the same frame as the blank page. + if (target_security_origin->isEmpty() && + origin_window->frame() == target_window->frame()) + return true; + + return false; +} + + +bool V8Proxy::CanAccessFrame(Frame* target, bool report_error) +{ + // The subject is detached from a frame, deny accesses. + if (!target) + return false; + + if (!CanAccessPrivate(target->domWindow())) { + if (report_error) + ReportUnsafeAccessTo(target, REPORT_NOW); + 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; + + // Create a new environment using an empty template for the shadow + // object. Reuse the global object if one has been created earlier. + v8::Persistent<v8::ObjectTemplate> globalTemplate = + V8DOMWindow::GetShadowObjectTemplate(); + if (globalTemplate.IsEmpty()) + return result; + + // Install a security handler with V8. + globalTemplate->SetAccessCheckCallbacks( + V8Custom::v8DOMWindowNamedSecurityCheck, + V8Custom::v8DOMWindowIndexedSecurityCheck, + v8::Integer::New(V8ClassIndex::DOMWINDOW)); + + // Dynamically tell v8 about our extensions now. + const char** extensionNames = new const char*[m_extensions.size()]; + int index = 0; + for (V8ExtensionList::iterator it = m_extensions.begin(); + it != m_extensions.end(); ++it) { + // Note: we check the loader URL here instead of the document URL + // because we might be currently loading an URL into a blank page. + // See http://code.google.com/p/chromium/issues/detail?id=10924 + 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); + result = v8::Context::New(&extensions, globalTemplate, global); + delete [] extensionNames; + extensionNames = 0; + + return result; +} + +bool V8Proxy::installDOMWindow(v8::Handle<v8::Context> context, + DOMWindow* window) +{ + v8::Handle<v8::String> implicit_proto_string = v8::String::New("__proto__"); + if (implicit_proto_string.IsEmpty()) + return false; + + // Create a new JS window object and use it as the prototype for the + // shadow global object. + v8::Handle<v8::Function> window_constructor = + GetConstructor(V8ClassIndex::DOMWINDOW); + v8::Local<v8::Object> js_window = + SafeAllocation::NewInstance(window_constructor); + // Bail out if allocation failed. + if (js_window.IsEmpty()) + return false; + + // Wrap the window. + SetDOMWrapper(js_window, + V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), + window); + + window->ref(); + V8Proxy::SetJSWrapperForDOMObject(window, + v8::Persistent<v8::Object>::New(js_window)); + + // Insert the window instance as the prototype of the shadow object. + v8::Handle<v8::Object> v8_global = context->Global(); + v8_global->Set(implicit_proto_string, js_window); + return true; +} + +// Create a new environment and setup the global object. +// +// The global object corresponds to a DOMWindow instance. However, to +// allow properties of the JS DOMWindow instance to be shadowed, we +// use a shadow object as the global object and use the JS DOMWindow +// instance as the prototype for that shadow object. The JS DOMWindow +// instance is undetectable from javascript code because the __proto__ +// accessors skip that object. +// +// The shadow object and the DOMWindow instance are seen as one object +// from javascript. The javascript object that corresponds to a +// DOMWindow instance is the shadow object. When mapping a DOMWindow +// instance to a V8 object, we return the shadow object. +// +// To implement split-window, see +// 1) https://bugs.webkit.org/show_bug.cgi?id=17249 +// 2) https://wiki.mozilla.org/Gecko:SplitWindow +// 3) https://bugzilla.mozilla.org/show_bug.cgi?id=296639 +// we need to split the shadow object further into two objects: +// an outer window and an inner window. The inner window is the hidden +// prototype of the outer window. The inner window is the default +// global object of the context. A variable declared in the global +// scope is a property of the inner window. +// +// The outer window sticks to a Frame, it is exposed to JavaScript +// via window.window, window.self, window.parent, etc. The outer window +// has a security token which is the domain. The outer window cannot +// have its own properties. window.foo = 'x' is delegated to the +// inner window. +// +// When a frame navigates to a new page, the inner window is cut off +// the outer window, and the outer window identify is preserved for +// the frame. However, a new inner window is created for the new page. +// If there are JS code holds a closure to the old inner window, +// it won't be able to reach the outer window via its global object. +void V8Proxy::InitContextIfNeeded() +{ + // Bail out if the context has already been initialized. + if (!m_context.IsEmpty()) + return; + + // Create a handle scope for all local handles. + v8::HandleScope handle_scope; + + // Setup the security handlers and message listener. This only has + // to be done once. + static bool v8_initialized = false; + if (!v8_initialized) { + // Tells V8 not to call the default OOM handler, binding code + // will handle it. + v8::V8::IgnoreOutOfMemoryException(); + v8::V8::SetFatalErrorHandler(ReportFatalErrorInV8); + + v8::V8::SetGlobalGCPrologueCallback(&GCPrologue); + v8::V8::SetGlobalGCEpilogueCallback(&GCEpilogue); + + v8::V8::AddMessageListener(HandleConsoleMessage); + + v8::V8::SetFailedAccessCheckCallbackFunction(ReportUnsafeJavaScriptAccess); + + v8_initialized = true; + } + + m_context = createNewContext(m_global); + if (m_context.IsEmpty()) + return; + + // Starting from now, use local context only. + v8::Local<v8::Context> context = GetContext(); + v8::Context::Scope context_scope(context); + + // Store the first global object created so we can reuse it. + if (m_global.IsEmpty()) { + m_global = v8::Persistent<v8::Object>::New(context->Global()); + // Bail out if allocation of the first global objects fails. + if (m_global.IsEmpty()) { + DisposeContextHandles(); + return; + } +#ifndef NDEBUG + RegisterGlobalHandle(PROXY, this, m_global); +#endif + } + + // Allocate strings used during initialization. + v8::Handle<v8::String> object_string = v8::String::New("Object"); + v8::Handle<v8::String> prototype_string = v8::String::New("prototype"); + // Bail out if allocation failed. + if (object_string.IsEmpty() || + prototype_string.IsEmpty()) { + DisposeContextHandles(); + return; + } + + // Allocate clone cache and pre-allocated objects + v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast( + m_global->Get(object_string)); + m_object_prototype = v8::Persistent<v8::Value>::New( + object->Get(prototype_string)); + m_wrapper_boilerplates = v8::Persistent<v8::Array>::New( + v8::Array::New(V8ClassIndex::WRAPPER_TYPE_COUNT)); + // Bail out if allocation failed. + if (m_object_prototype.IsEmpty()) { + DisposeContextHandles(); + return; + } +#ifndef NDEBUG + RegisterGlobalHandle(PROXY, this, m_object_prototype); + RegisterGlobalHandle(PROXY, this, m_wrapper_boilerplates); +#endif + + if (!installDOMWindow(context, 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 = V8Proxy::ToV8Object(type, exception.get()); + + v8::ThrowException(v8Exception); +} + +void V8Proxy::SetDOMException(int exception_code) +{ + if (exception_code <= 0) + return; + + ExceptionCodeDescription description; + getExceptionCodeDescription(exception_code, 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(false); + break; + } +} + +v8::Handle<v8::Value> V8Proxy::ThrowError(ErrorType type, const char* message) +{ + switch (type) { + case RANGE_ERROR: + return v8::ThrowException(v8::Exception::RangeError(v8String(message))); + case REFERENCE_ERROR: + return v8::ThrowException( + v8::Exception::ReferenceError(v8String(message))); + case SYNTAX_ERROR: + return v8::ThrowException(v8::Exception::SyntaxError(v8String(message))); + case TYPE_ERROR: + return v8::ThrowException(v8::Exception::TypeError(v8String(message))); + case GENERAL_ERROR: + return v8::ThrowException(v8::Exception::Error(v8String(message))); + default: + ASSERT(false); + return v8::Handle<v8::Value>(); + } +} + +v8::Local<v8::Context> V8Proxy::GetContext(Frame* frame) +{ + V8Proxy* proxy = retrieve(frame); + if (!proxy) + return v8::Local<v8::Context>(); + + proxy->InitContextIfNeeded(); + return proxy->GetContext(); +} + +v8::Local<v8::Context> V8Proxy::GetCurrentContext() +{ + return v8::Context::GetCurrent(); +} + +v8::Handle<v8::Value> V8Proxy::ToV8Object(V8ClassIndex::V8WrapperType type, void* imp) +{ + ASSERT(type != V8ClassIndex::EVENTLISTENER); + ASSERT(type != V8ClassIndex::EVENTTARGET); + ASSERT(type != V8ClassIndex::EVENT); + + bool is_active_dom_object = false; + switch (type) { +#define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE: + DOM_NODE_TYPES(MAKE_CASE) +#if ENABLE(SVG) + SVG_NODE_TYPES(MAKE_CASE) +#endif + return NodeToV8Object(static_cast<Node*>(imp)); + case V8ClassIndex::CSSVALUE: + return CSSValueToV8Object(static_cast<CSSValue*>(imp)); + case V8ClassIndex::CSSRULE: + return CSSRuleToV8Object(static_cast<CSSRule*>(imp)); + case V8ClassIndex::STYLESHEET: + return StyleSheetToV8Object(static_cast<StyleSheet*>(imp)); + case V8ClassIndex::DOMWINDOW: + return WindowToV8Object(static_cast<DOMWindow*>(imp)); +#if ENABLE(SVG) + SVG_NONNODE_TYPES(MAKE_CASE) + if (type == V8ClassIndex::SVGELEMENTINSTANCE) + return SVGElementInstanceToV8Object(static_cast<SVGElementInstance*>(imp)); + return SVGObjectWithContextToV8Object(type, imp); +#endif + + ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) + is_active_dom_object = true; + break; + default: + break; + } + +#undef MAKE_CASE + + if (!imp) return v8::Null(); + + // Non DOM node + v8::Persistent<v8::Object> result = is_active_dom_object ? + getActiveDOMObjectMap().get(imp) : + getDOMObjectMap().get(imp); + if (result.IsEmpty()) { + v8::Local<v8::Object> v8obj = InstantiateV8Object(type, type, imp); + if (!v8obj.IsEmpty()) { + // Go through big switch statement, it has some duplications + // that were handled by code above (such as CSSVALUE, CSSRULE, etc). + switch (type) { +#define MAKE_CASE(TYPE, NAME) \ + case V8ClassIndex::TYPE: static_cast<NAME*>(imp)->ref(); break; + DOM_OBJECT_TYPES(MAKE_CASE) +#undef MAKE_CASE + default: + ASSERT(false); + } + result = v8::Persistent<v8::Object>::New(v8obj); + if (is_active_dom_object) + SetJSWrapperForActiveDOMObject(imp, result); + else + SetJSWrapperForDOMObject(imp, result); + + // Special case for non-node objects associated with a + // DOMWindow. Both Safari and FF let the JS wrappers for these + // objects survive GC. To mimic their behavior, V8 creates + // hidden references from the DOMWindow to these wrapper + // objects. These references get cleared when the DOMWindow is + // reused by a new page. + switch (type) { + case V8ClassIndex::CONSOLE: + SetHiddenWindowReference(static_cast<Console*>(imp)->frame(), + V8Custom::kDOMWindowConsoleIndex, result); + break; + case V8ClassIndex::HISTORY: + SetHiddenWindowReference(static_cast<History*>(imp)->frame(), + V8Custom::kDOMWindowHistoryIndex, result); + break; + case V8ClassIndex::NAVIGATOR: + SetHiddenWindowReference(static_cast<Navigator*>(imp)->frame(), + V8Custom::kDOMWindowNavigatorIndex, result); + break; + case V8ClassIndex::SCREEN: + SetHiddenWindowReference(static_cast<Screen*>(imp)->frame(), + V8Custom::kDOMWindowScreenIndex, result); + break; + case V8ClassIndex::LOCATION: + SetHiddenWindowReference(static_cast<Location*>(imp)->frame(), + V8Custom::kDOMWindowLocationIndex, result); + break; + case V8ClassIndex::DOMSELECTION: + SetHiddenWindowReference(static_cast<DOMSelection*>(imp)->frame(), + V8Custom::kDOMWindowDOMSelectionIndex, result); + break; + case V8ClassIndex::BARINFO: { + BarInfo* barinfo = static_cast<BarInfo*>(imp); + Frame* frame = barinfo->frame(); + switch (barinfo->type()) { + case BarInfo::Locationbar: + SetHiddenWindowReference(frame, V8Custom::kDOMWindowLocationbarIndex, result); + break; + case BarInfo::Menubar: + SetHiddenWindowReference(frame, V8Custom::kDOMWindowMenubarIndex, result); + break; + case BarInfo::Personalbar: + SetHiddenWindowReference(frame, V8Custom::kDOMWindowPersonalbarIndex, result); + break; + case BarInfo::Scrollbars: + SetHiddenWindowReference(frame, V8Custom::kDOMWindowScrollbarsIndex, result); + break; + case BarInfo::Statusbar: + SetHiddenWindowReference(frame, V8Custom::kDOMWindowStatusbarIndex, result); + break; + case BarInfo::Toolbar: + SetHiddenWindowReference(frame, V8Custom::kDOMWindowToolbarIndex, result); + break; + } + break; + } + default: + break; + } + } + } + return result; +} + + +void V8Proxy::SetHiddenWindowReference(Frame* frame, + const int internal_index, + v8::Handle<v8::Object> jsobj) +{ + // Get DOMWindow + if (!frame) return; // Object might be detached from window + v8::Handle<v8::Context> context = GetContext(frame); + if (context.IsEmpty()) return; + + ASSERT(internal_index < V8Custom::kDOMWindowInternalFieldCount); + + v8::Handle<v8::Object> global = context->Global(); + // Look for real DOM wrapper. + global = LookupDOMWrapper(V8ClassIndex::DOMWINDOW, global); + ASSERT(!global.IsEmpty()); + ASSERT(global->GetInternalField(internal_index)->IsUndefined()); + global->SetInternalField(internal_index, jsobj); +} + + +V8ClassIndex::V8WrapperType V8Proxy::GetDOMWrapperType(v8::Handle<v8::Object> object) +{ + ASSERT(MaybeDOMWrapper(object)); + v8::Handle<v8::Value> type = + object->GetInternalField(V8Custom::kDOMWrapperTypeIndex); + return V8ClassIndex::FromInt(type->Int32Value()); +} + + +void* V8Proxy::ToNativeObjectImpl(V8ClassIndex::V8WrapperType type, + v8::Handle<v8::Value> object) +{ + // Native event listener is per frame, it cannot be handled + // by this generic function. + ASSERT(type != V8ClassIndex::EVENTLISTENER); + ASSERT(type != V8ClassIndex::EVENTTARGET); + + ASSERT(MaybeDOMWrapper(object)); + + switch (type) { +#define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE: + DOM_NODE_TYPES(MAKE_CASE) +#if ENABLE(SVG) + SVG_NODE_TYPES(MAKE_CASE) +#endif + ASSERT(false); + return NULL; + case V8ClassIndex::XMLHTTPREQUEST: + return DOMWrapperToNative<XMLHttpRequest>(object); + case V8ClassIndex::EVENT: + return DOMWrapperToNative<Event>(object); + case V8ClassIndex::CSSRULE: + return DOMWrapperToNative<CSSRule>(object); + default: + break; + } +#undef MAKE_CASE + + return DOMWrapperToNative<void>(object); +} + + +void* V8Proxy::ToSVGPODTypeImpl(V8ClassIndex::V8WrapperType type, + v8::Handle<v8::Value> object) { + return IsWrapperOfType(object, type) + ? DOMWrapperToNative<void>(object) + : NULL; +} + + +v8::Handle<v8::Object> V8Proxy::LookupDOMWrapper( + V8ClassIndex::V8WrapperType type, v8::Handle<v8::Value> value) +{ + if (value.IsEmpty()) + return v8::Handle<v8::Object>(); + + v8::Handle<v8::FunctionTemplate> desc = V8Proxy::GetTemplate(type); + while (value->IsObject()) { + v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value); + if (desc->HasInstance(object)) + return object; + + value = object->GetPrototype(); + } + return v8::Handle<v8::Object>(); +} + + +// static +void* V8Proxy::DOMWrapperToNodeHelper(v8::Handle<v8::Value> value) { + ASSERT(MaybeDOMWrapper(value)); + + v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value); + + ASSERT(GetDOMWrapperType(object) == V8ClassIndex::NODE); + + v8::Handle<v8::Value> wrapper = + object->GetInternalField(V8Custom::kDOMWrapperObjectIndex); + return ExtractCPointer<Node>(wrapper); +} + + +PassRefPtr<NodeFilter> V8Proxy::ToNativeNodeFilter(v8::Handle<v8::Value> filter) +{ + // A NodeFilter is used when walking through a DOM tree or iterating tree + // nodes. + // TODO: we may want to cache NodeFilterCondition and NodeFilter + // object, but it is minor. + // NodeFilter is passed to NodeIterator that has a ref counted pointer + // to NodeFilter. NodeFilter has a ref counted pointer to NodeFilterCondition. + // In NodeFilterCondition, filter object is persisted in its constructor, + // and disposed in its destructor. + if (!filter->IsFunction()) + return 0; + + NodeFilterCondition* cond = new V8NodeFilterCondition(filter); + return NodeFilter::create(cond); +} + + +v8::Local<v8::Object> V8Proxy::InstantiateV8Object( + V8ClassIndex::V8WrapperType desc_type, + V8ClassIndex::V8WrapperType cptr_type, + void* imp) +{ + // Make a special case for document.all + if (desc_type == V8ClassIndex::HTMLCOLLECTION && + static_cast<HTMLCollection*>(imp)->type() == DocAll) { + desc_type = V8ClassIndex::UNDETECTABLEHTMLCOLLECTION; + } + + V8Proxy* proxy = V8Proxy::retrieve(); + v8::Local<v8::Object> instance; + if (proxy) { + instance = proxy->CreateWrapperFromCache(desc_type); + } else { + v8::Local<v8::Function> function = GetTemplate(desc_type)->GetFunction(); + instance = SafeAllocation::NewInstance(function); + } + if (!instance.IsEmpty()) { + // Avoid setting the DOM wrapper for failed allocations. + SetDOMWrapper(instance, V8ClassIndex::ToInt(cptr_type), imp); + } + return instance; +} + +v8::Handle<v8::Value> V8Proxy::CheckNewLegal(const v8::Arguments& args) +{ + if (!AllowAllocation::m_current) + return ThrowError(TYPE_ERROR, "Illegal constructor"); + + return args.This(); +} + +void V8Proxy::SetDOMWrapper(v8::Handle<v8::Object> obj, int type, void* cptr) +{ + ASSERT(obj->InternalFieldCount() >= 2); + obj->SetInternalField(V8Custom::kDOMWrapperObjectIndex, WrapCPointer(cptr)); + obj->SetInternalField(V8Custom::kDOMWrapperTypeIndex, v8::Integer::New(type)); +} + + +#ifndef NDEBUG +bool V8Proxy::MaybeDOMWrapper(v8::Handle<v8::Value> value) +{ + if (value.IsEmpty() || !value->IsObject()) return false; + + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(value); + if (obj->InternalFieldCount() == 0) return false; + + ASSERT(obj->InternalFieldCount() >= + V8Custom::kDefaultWrapperInternalFieldCount); + + v8::Handle<v8::Value> type = + obj->GetInternalField(V8Custom::kDOMWrapperTypeIndex); + ASSERT(type->IsInt32()); + ASSERT(V8ClassIndex::INVALID_CLASS_INDEX < type->Int32Value() && + type->Int32Value() < V8ClassIndex::CLASSINDEX_END); + + v8::Handle<v8::Value> wrapper = + obj->GetInternalField(V8Custom::kDOMWrapperObjectIndex); + ASSERT(wrapper->IsNumber() || wrapper->IsExternal()); + + return true; +} +#endif + + +bool V8Proxy::IsDOMEventWrapper(v8::Handle<v8::Value> value) +{ + // All kinds of events use EVENT as dom type in JS wrappers. + // See EventToV8Object + return IsWrapperOfType(value, V8ClassIndex::EVENT); +} + +bool V8Proxy::IsWrapperOfType(v8::Handle<v8::Value> value, + V8ClassIndex::V8WrapperType classType) +{ + if (value.IsEmpty() || !value->IsObject()) return false; + + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(value); + if (obj->InternalFieldCount() == 0) return false; + + ASSERT(obj->InternalFieldCount() >= + V8Custom::kDefaultWrapperInternalFieldCount); + + v8::Handle<v8::Value> wrapper = + obj->GetInternalField(V8Custom::kDOMWrapperObjectIndex); + ASSERT(wrapper->IsNumber() || wrapper->IsExternal()); + + v8::Handle<v8::Value> type = + obj->GetInternalField(V8Custom::kDOMWrapperTypeIndex); + ASSERT(type->IsInt32()); + ASSERT(V8ClassIndex::INVALID_CLASS_INDEX < type->Int32Value() && + type->Int32Value() < V8ClassIndex::CLASSINDEX_END); + + return V8ClassIndex::FromInt(type->Int32Value()) == classType; +} + +#if ENABLE(VIDEO) +#define FOR_EACH_VIDEO_TAG(macro) \ + macro(audio, AUDIO) \ + macro(source, SOURCE) \ + macro(video, VIDEO) +#else +#define FOR_EACH_VIDEO_TAG(macro) +#endif + +#define FOR_EACH_TAG(macro) \ + macro(a, ANCHOR) \ + macro(applet, APPLET) \ + macro(area, AREA) \ + macro(base, BASE) \ + macro(basefont, BASEFONT) \ + macro(blockquote, BLOCKQUOTE) \ + macro(body, BODY) \ + macro(br, BR) \ + macro(button, BUTTON) \ + macro(caption, TABLECAPTION) \ + macro(col, TABLECOL) \ + macro(colgroup, TABLECOL) \ + macro(del, MOD) \ + macro(canvas, CANVAS) \ + macro(dir, DIRECTORY) \ + macro(div, DIV) \ + macro(dl, DLIST) \ + macro(embed, EMBED) \ + macro(fieldset, FIELDSET) \ + macro(font, FONT) \ + macro(form, FORM) \ + macro(frame, FRAME) \ + macro(frameset, FRAMESET) \ + macro(h1, HEADING) \ + macro(h2, HEADING) \ + macro(h3, HEADING) \ + macro(h4, HEADING) \ + macro(h5, HEADING) \ + macro(h6, HEADING) \ + macro(head, HEAD) \ + macro(hr, HR) \ + macro(html, HTML) \ + macro(img, IMAGE) \ + macro(iframe, IFRAME) \ + macro(image, IMAGE) \ + macro(input, INPUT) \ + macro(ins, MOD) \ + macro(isindex, ISINDEX) \ + macro(keygen, SELECT) \ + macro(label, LABEL) \ + macro(legend, LEGEND) \ + macro(li, LI) \ + macro(link, LINK) \ + macro(listing, PRE) \ + macro(map, MAP) \ + macro(marquee, MARQUEE) \ + macro(menu, MENU) \ + macro(meta, META) \ + macro(object, OBJECT) \ + macro(ol, OLIST) \ + macro(optgroup, OPTGROUP) \ + macro(option, OPTION) \ + macro(p, PARAGRAPH) \ + macro(param, PARAM) \ + macro(pre, PRE) \ + macro(q, QUOTE) \ + macro(script, SCRIPT) \ + macro(select, SELECT) \ + macro(style, STYLE) \ + macro(table, TABLE) \ + macro(thead, TABLESECTION) \ + macro(tbody, TABLESECTION) \ + macro(tfoot, TABLESECTION) \ + macro(td, TABLECELL) \ + macro(th, TABLECELL) \ + macro(tr, TABLEROW) \ + macro(textarea, TEXTAREA) \ + macro(title, TITLE) \ + macro(ul, ULIST) \ + macro(xmp, PRE) + +V8ClassIndex::V8WrapperType V8Proxy::GetHTMLElementType(HTMLElement* element) +{ + static HashMap<String, V8ClassIndex::V8WrapperType> map; + if (map.isEmpty()) { +#define ADD_TO_HASH_MAP(tag, name) \ + map.set(#tag, V8ClassIndex::HTML##name##ELEMENT); +FOR_EACH_TAG(ADD_TO_HASH_MAP) +#if ENABLE(VIDEO) + if (MediaPlayer::isAvailable()) { +FOR_EACH_VIDEO_TAG(ADD_TO_HASH_MAP) + } +#endif +#undef ADD_TO_HASH_MAP + } + + V8ClassIndex::V8WrapperType t = map.get(element->localName().impl()); + if (t == 0) + return V8ClassIndex::HTMLELEMENT; + return t; +} +#undef FOR_EACH_TAG + +#if ENABLE(SVG) + +#if ENABLE(SVG_ANIMATION) +#define FOR_EACH_ANIMATION_TAG(macro) \ + macro(animateColor, ANIMATECOLOR) \ + macro(animate, ANIMATE) \ + macro(animateTransform, ANIMATETRANSFORM) \ + macro(set, SET) +#else +#define FOR_EACH_ANIMATION_TAG(macro) +#endif + +#if ENABLE(SVG_FILTERS) +#define FOR_EACH_FILTERS_TAG(macro) \ + macro(feBlend, FEBLEND) \ + macro(feColorMatrix, FECOLORMATRIX) \ + macro(feComponentTransfer, FECOMPONENTTRANSFER) \ + macro(feComposite, FECOMPOSITE) \ + macro(feDiffuseLighting, FEDIFFUSELIGHTING) \ + macro(feDisplacementMap, FEDISPLACEMENTMAP) \ + macro(feDistantLight, FEDISTANTLIGHT) \ + macro(feFlood, FEFLOOD) \ + macro(feFuncA, FEFUNCA) \ + macro(feFuncB, FEFUNCB) \ + macro(feFuncG, FEFUNCG) \ + macro(feFuncR, FEFUNCR) \ + macro(feGaussianBlur, FEGAUSSIANBLUR) \ + macro(feImage, FEIMAGE) \ + macro(feMerge, FEMERGE) \ + macro(feMergeNode, FEMERGENODE) \ + macro(feOffset, FEOFFSET) \ + macro(fePointLight, FEPOINTLIGHT) \ + macro(feSpecularLighting, FESPECULARLIGHTING) \ + macro(feSpotLight, FESPOTLIGHT) \ + macro(feTile, FETILE) \ + macro(feTurbulence, FETURBULENCE) \ + macro(filter, FILTER) +#else +#define FOR_EACH_FILTERS_TAG(macro) +#endif + +#if ENABLE(SVG_FONTS) +#define FOR_EACH_FONTS_TAG(macro) \ + macro(definition-src, DEFINITIONSRC) \ + macro(font-face, FONTFACE) \ + macro(font-face-format, FONTFACEFORMAT) \ + macro(font-face-name, FONTFACENAME) \ + macro(font-face-src, FONTFACESRC) \ + macro(font-face-uri, FONTFACEURI) +#else +#define FOR_EACH_FONTS_TAG(marco) +#endif + +#if ENABLE(SVG_FOREIGN_OBJECT) +#define FOR_EACH_FOREIGN_OBJECT_TAG(macro) \ + macro(foreignObject, FOREIGNOBJECT) +#else +#define FOR_EACH_FOREIGN_OBJECT_TAG(macro) +#endif + +#if ENABLE(SVG_USE) +#define FOR_EACH_USE_TAG(macro) \ + macro(use, USE) +#else +#define FOR_EACH_USE_TAG(macro) +#endif + +#define FOR_EACH_TAG(macro) \ + FOR_EACH_ANIMATION_TAG(macro) \ + FOR_EACH_FILTERS_TAG(macro) \ + FOR_EACH_FONTS_TAG(macro) \ + FOR_EACH_FOREIGN_OBJECT_TAG(macro) \ + FOR_EACH_USE_TAG(macro) \ + macro(a, A) \ + macro(altGlyph, ALTGLYPH) \ + macro(circle, CIRCLE) \ + macro(clipPath, CLIPPATH) \ + macro(cursor, CURSOR) \ + macro(defs, DEFS) \ + macro(desc, DESC) \ + macro(ellipse, ELLIPSE) \ + macro(g, G) \ + macro(glyph, GLYPH) \ + macro(image, IMAGE) \ + macro(linearGradient, LINEARGRADIENT) \ + macro(line, LINE) \ + macro(marker, MARKER) \ + macro(mask, MASK) \ + macro(metadata, METADATA) \ + macro(path, PATH) \ + macro(pattern, PATTERN) \ + macro(polyline, POLYLINE) \ + macro(polygon, POLYGON) \ + macro(radialGradient, RADIALGRADIENT) \ + macro(rect, RECT) \ + macro(script, SCRIPT) \ + macro(stop, STOP) \ + macro(style, STYLE) \ + macro(svg, SVG) \ + macro(switch, SWITCH) \ + macro(symbol, SYMBOL) \ + macro(text, TEXT) \ + macro(textPath, TEXTPATH) \ + macro(title, TITLE) \ + macro(tref, TREF) \ + macro(tspan, TSPAN) \ + macro(view, VIEW) \ + // end of macro + +V8ClassIndex::V8WrapperType V8Proxy::GetSVGElementType(SVGElement* element) +{ + static HashMap<String, V8ClassIndex::V8WrapperType> map; + if (map.isEmpty()) { +#define ADD_TO_HASH_MAP(tag, name) \ + map.set(#tag, V8ClassIndex::SVG##name##ELEMENT); +FOR_EACH_TAG(ADD_TO_HASH_MAP) +#undef ADD_TO_HASH_MAP + } + + V8ClassIndex::V8WrapperType t = map.get(element->localName().impl()); + if (t == 0) return V8ClassIndex::SVGELEMENT; + return t; +} +#undef FOR_EACH_TAG + +#endif // ENABLE(SVG) + + +v8::Handle<v8::Value> V8Proxy::EventToV8Object(Event* event) +{ + if (!event) + return v8::Null(); + + v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(event); + if (!wrapper.IsEmpty()) + return wrapper; + + V8ClassIndex::V8WrapperType type = V8ClassIndex::EVENT; + + if (event->isUIEvent()) { + if (event->isKeyboardEvent()) + type = V8ClassIndex::KEYBOARDEVENT; + else if (event->isTextEvent()) + type = V8ClassIndex::TEXTEVENT; + else if (event->isMouseEvent()) + type = V8ClassIndex::MOUSEEVENT; + else if (event->isWheelEvent()) + type = V8ClassIndex::WHEELEVENT; +#if ENABLE(SVG) + else if (event->isSVGZoomEvent()) + type = V8ClassIndex::SVGZOOMEVENT; +#endif + else + type = V8ClassIndex::UIEVENT; + } else if (event->isMutationEvent()) + type = V8ClassIndex::MUTATIONEVENT; + else if (event->isOverflowEvent()) + type = V8ClassIndex::OVERFLOWEVENT; + else if (event->isMessageEvent()) + type = V8ClassIndex::MESSAGEEVENT; + else if (event->isProgressEvent()) { + if (event->isXMLHttpRequestProgressEvent()) + type = V8ClassIndex::XMLHTTPREQUESTPROGRESSEVENT; + else + type = V8ClassIndex::PROGRESSEVENT; + } else if (event->isWebKitAnimationEvent()) + type = V8ClassIndex::WEBKITANIMATIONEVENT; + else if (event->isWebKitTransitionEvent()) + type = V8ClassIndex::WEBKITTRANSITIONEVENT; + + + v8::Handle<v8::Object> result = + InstantiateV8Object(type, V8ClassIndex::EVENT, event); + if (result.IsEmpty()) { + // Instantiation failed. Avoid updating the DOM object map and + // return null which is already handled by callers of this function + // in case the event is NULL. + return v8::Null(); + } + + event->ref(); // fast ref + SetJSWrapperForDOMObject(event, v8::Persistent<v8::Object>::New(result)); + + return result; +} + + +// Caller checks node is not null. +v8::Handle<v8::Value> V8Proxy::NodeToV8Object(Node* node) +{ + if (!node) return v8::Null(); + + // Find the context to which the node belongs and create the wrapper + // in that context. If the node is not in a document, the current + // context is used. + // + // Getting the context might initialize the context which can instantiate + // a document wrapper. Therefore, we get the context before checking if + // the node already has a wrapper. + v8::Local<v8::Context> context; + Document* doc = node->document(); + if (doc) { + context = V8Proxy::GetContext(doc->frame()); + } + + v8::Handle<v8::Object> wrapper = getDOMNodeMap().get(node); + if (!wrapper.IsEmpty()) + return wrapper; + + bool is_document = false; // document type node has special handling + V8ClassIndex::V8WrapperType type; + + switch (node->nodeType()) { + case Node::ELEMENT_NODE: + if (node->isHTMLElement()) + type = GetHTMLElementType(static_cast<HTMLElement*>(node)); +#if ENABLE(SVG) + else if (node->isSVGElement()) + type = GetSVGElementType(static_cast<SVGElement*>(node)); +#endif + else + type = V8ClassIndex::ELEMENT; + break; + case Node::ATTRIBUTE_NODE: + type = V8ClassIndex::ATTR; + break; + case Node::TEXT_NODE: + type = V8ClassIndex::TEXT; + break; + case Node::CDATA_SECTION_NODE: + type = V8ClassIndex::CDATASECTION; + break; + case Node::ENTITY_NODE: + type = V8ClassIndex::ENTITY; + break; + case Node::PROCESSING_INSTRUCTION_NODE: + type = V8ClassIndex::PROCESSINGINSTRUCTION; + break; + case Node::COMMENT_NODE: + type = V8ClassIndex::COMMENT; + break; + case Node::DOCUMENT_NODE: { + is_document = true; + Document* doc = static_cast<Document*>(node); + if (doc->isHTMLDocument()) + type = V8ClassIndex::HTMLDOCUMENT; +#if ENABLE(SVG) + else if (doc->isSVGDocument()) + type = V8ClassIndex::SVGDOCUMENT; +#endif + else + type = V8ClassIndex::DOCUMENT; + break; + } + case Node::DOCUMENT_TYPE_NODE: + type = V8ClassIndex::DOCUMENTTYPE; + break; + case Node::NOTATION_NODE: + type = V8ClassIndex::NOTATION; + break; + case Node::DOCUMENT_FRAGMENT_NODE: + type = V8ClassIndex::DOCUMENTFRAGMENT; + break; + case Node::ENTITY_REFERENCE_NODE: + type = V8ClassIndex::ENTITYREFERENCE; + break; + default: + type = V8ClassIndex::NODE; + } + + // Enter the node's context and create the wrapper in that context. + if (!context.IsEmpty()) { + context->Enter(); + } + + v8::Local<v8::Object> result = + InstantiateV8Object(type, V8ClassIndex::NODE, node); + + // Exit the node's context if it was entered. + if (!context.IsEmpty()) { + context->Exit(); + } + + if (result.IsEmpty()) { + // If instantiation failed it's important not to add the result + // to the DOM node map. Instead we return an empty handle, which + // should already be handled by callers of this function in case + // the node is NULL. + return result; + } + + node->ref(); + SetJSWrapperForDOMNode(node, v8::Persistent<v8::Object>::New(result)); + + if (is_document) { + Document* doc = static_cast<Document*>(node); + V8Proxy* proxy = V8Proxy::retrieve(doc->frame()); + if (proxy) + proxy->UpdateDocumentWrapper(result); + + if (type == V8ClassIndex::HTMLDOCUMENT) { + // Create marker object and insert it in two internal fields. + // This is used to implement temporary shadowing of + // document.all. + ASSERT(result->InternalFieldCount() == + V8Custom::kHTMLDocumentInternalFieldCount); + v8::Local<v8::Object> marker = v8::Object::New(); + result->SetInternalField(V8Custom::kHTMLDocumentMarkerIndex, marker); + result->SetInternalField(V8Custom::kHTMLDocumentShadowIndex, marker); + } + } + + return result; +} + + +// A JS object of type EventTarget can only be the following possible types: +// 1) EventTargetNode; 2) DOMWindow 3) XMLHttpRequest; 4) MessagePort; +// 5) XMLHttpRequestUpload +// check EventTarget.h for new type conversion methods +v8::Handle<v8::Value> V8Proxy::EventTargetToV8Object(EventTarget* target) +{ + if (!target) + return v8::Null(); + +#if ENABLE(SVG) + SVGElementInstance* instance = target->toSVGElementInstance(); + if (instance) + return ToV8Object(V8ClassIndex::SVGELEMENTINSTANCE, instance); +#endif + +#if ENABLE(WORKERS) + Worker* worker = target->toWorker(); + if (worker) + return ToV8Object(V8ClassIndex::WORKER, worker); +#endif // WORKERS + + Node* node = target->toNode(); + if (node) + return NodeToV8Object(node); + + if (DOMWindow* domWindow = target->toDOMWindow()) + return ToV8Object(V8ClassIndex::DOMWINDOW, domWindow); + + // XMLHttpRequest is created within its JS counterpart. + XMLHttpRequest* xhr = target->toXMLHttpRequest(); + if (xhr) { + v8::Handle<v8::Object> wrapper = getActiveDOMObjectMap().get(xhr); + ASSERT(!wrapper.IsEmpty()); + return wrapper; + } + + // MessagePort is created within its JS counterpart + MessagePort* port = target->toMessagePort(); + if (port) { + v8::Handle<v8::Object> wrapper = getActiveDOMObjectMap().get(port); + ASSERT(!wrapper.IsEmpty()); + return wrapper; + } + + XMLHttpRequestUpload* upload = target->toXMLHttpRequestUpload(); + if (upload) { + v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(upload); + ASSERT(!wrapper.IsEmpty()); + return wrapper; + } + + ASSERT(0); + return v8::Handle<v8::Value>(); +} + + +v8::Handle<v8::Value> V8Proxy::EventListenerToV8Object( + EventListener* listener) +{ + if (listener == 0) return v8::Null(); + + // TODO(fqian): can a user take a lazy event listener and set to other places? + V8AbstractEventListener* v8listener = + static_cast<V8AbstractEventListener*>(listener); + return v8listener->getListenerObject(); +} + + +v8::Handle<v8::Value> V8Proxy::DOMImplementationToV8Object( + DOMImplementation* impl) +{ + v8::Handle<v8::Object> result = + InstantiateV8Object(V8ClassIndex::DOMIMPLEMENTATION, + V8ClassIndex::DOMIMPLEMENTATION, + impl); + if (result.IsEmpty()) { + // If the instantiation failed, we ignore it and return null instead + // of returning an empty handle. + return v8::Null(); + } + return result; +} + + +v8::Handle<v8::Value> V8Proxy::StyleSheetToV8Object(StyleSheet* sheet) +{ + if (!sheet) return v8::Null(); + + v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(sheet); + if (!wrapper.IsEmpty()) + return wrapper; + + V8ClassIndex::V8WrapperType type = V8ClassIndex::STYLESHEET; + if (sheet->isCSSStyleSheet()) + type = V8ClassIndex::CSSSTYLESHEET; + + v8::Handle<v8::Object> result = + InstantiateV8Object(type, V8ClassIndex::STYLESHEET, sheet); + if (!result.IsEmpty()) { + // Only update the DOM object map if the result is non-empty. + sheet->ref(); + SetJSWrapperForDOMObject(sheet, v8::Persistent<v8::Object>::New(result)); + } + + // Add a hidden reference from stylesheet object to its owner node. + Node* owner_node = sheet->ownerNode(); + if (owner_node) { + v8::Handle<v8::Object> owner = + v8::Handle<v8::Object>::Cast(NodeToV8Object(owner_node)); + result->SetInternalField(V8Custom::kStyleSheetOwnerNodeIndex, owner); + } + + return result; +} + + +v8::Handle<v8::Value> V8Proxy::CSSValueToV8Object(CSSValue* value) +{ + if (!value) return v8::Null(); + + v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(value); + if (!wrapper.IsEmpty()) + return wrapper; + + V8ClassIndex::V8WrapperType type; + + if (value->isWebKitCSSTransformValue()) + type = V8ClassIndex::WEBKITCSSTRANSFORMVALUE; + else if (value->isValueList()) + type = V8ClassIndex::CSSVALUELIST; + else if (value->isPrimitiveValue()) + type = V8ClassIndex::CSSPRIMITIVEVALUE; +#if ENABLE(SVG) + else if (value->isSVGPaint()) + type = V8ClassIndex::SVGPAINT; + else if (value->isSVGColor()) + type = V8ClassIndex::SVGCOLOR; +#endif + else + type = V8ClassIndex::CSSVALUE; + + v8::Handle<v8::Object> result = + InstantiateV8Object(type, V8ClassIndex::CSSVALUE, value); + if (!result.IsEmpty()) { + // Only update the DOM object map if the result is non-empty. + value->ref(); + SetJSWrapperForDOMObject(value, v8::Persistent<v8::Object>::New(result)); + } + + return result; +} + + +v8::Handle<v8::Value> V8Proxy::CSSRuleToV8Object(CSSRule* rule) +{ + if (!rule) return v8::Null(); + + v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(rule); + if (!wrapper.IsEmpty()) + return wrapper; + + V8ClassIndex::V8WrapperType type; + + switch (rule->type()) { + case CSSRule::STYLE_RULE: + type = V8ClassIndex::CSSSTYLERULE; + break; + case CSSRule::CHARSET_RULE: + type = V8ClassIndex::CSSCHARSETRULE; + break; + case CSSRule::IMPORT_RULE: + type = V8ClassIndex::CSSIMPORTRULE; + break; + case CSSRule::MEDIA_RULE: + type = V8ClassIndex::CSSMEDIARULE; + break; + case CSSRule::FONT_FACE_RULE: + type = V8ClassIndex::CSSFONTFACERULE; + break; + case CSSRule::PAGE_RULE: + type = V8ClassIndex::CSSPAGERULE; + break; + case CSSRule::VARIABLES_RULE: + type = V8ClassIndex::CSSVARIABLESRULE; + break; + case CSSRule::WEBKIT_KEYFRAME_RULE: + type = V8ClassIndex::WEBKITCSSKEYFRAMERULE; + break; + case CSSRule::WEBKIT_KEYFRAMES_RULE: + type = V8ClassIndex::WEBKITCSSKEYFRAMESRULE; + break; + default: // CSSRule::UNKNOWN_RULE + type = V8ClassIndex::CSSRULE; + break; + } + + v8::Handle<v8::Object> result = + InstantiateV8Object(type, V8ClassIndex::CSSRULE, rule); + if (!result.IsEmpty()) { + // Only update the DOM object map if the result is non-empty. + rule->ref(); + SetJSWrapperForDOMObject(rule, v8::Persistent<v8::Object>::New(result)); + } + return result; +} + +v8::Handle<v8::Value> V8Proxy::WindowToV8Object(DOMWindow* window) +{ + if (!window) return v8::Null(); + // Initializes environment of a frame, and return the global object + // of the frame. + Frame* frame = window->frame(); + if (!frame) + return v8::Handle<v8::Object>(); + + // Special case: Because of evaluateInNewContext() one DOMWindow can have + // multiple contexts and multiple global objects associated with it. When + // code running in one of those contexts accesses the window object, we + // want to return the global object associated with that context, not + // necessarily the first global object associated with that DOMWindow. + v8::Handle<v8::Context> current_context = v8::Context::GetCurrent(); + v8::Handle<v8::Object> current_global = current_context->Global(); + v8::Handle<v8::Object> windowWrapper = + LookupDOMWrapper(V8ClassIndex::DOMWINDOW, current_global); + if (!windowWrapper.IsEmpty()) + if (DOMWrapperToNative<DOMWindow>(windowWrapper) == window) + return current_global; + + // Otherwise, return the global object associated with this frame. + v8::Handle<v8::Context> context = GetContext(frame); + if (context.IsEmpty()) + return v8::Handle<v8::Object>(); + + v8::Handle<v8::Object> global = context->Global(); + ASSERT(!global.IsEmpty()); + return global; +} + +void V8Proxy::BindJSObjectToWindow(Frame* frame, + const char* name, + int type, + v8::Handle<v8::FunctionTemplate> desc, + void* imp) +{ + // Get environment. + v8::Handle<v8::Context> context = V8Proxy::GetContext(frame); + if (context.IsEmpty()) + return; // JS not enabled. + + v8::Context::Scope scope(context); + v8::Handle<v8::Object> instance = desc->GetFunction(); + SetDOMWrapper(instance, type, imp); + + v8::Handle<v8::Object> global = context->Global(); + global->Set(v8::String::New(name), instance); +} + +void V8Proxy::ProcessConsoleMessages() +{ + ConsoleMessageManager::ProcessDelayedMessages(); +} + + +// Create the utility context for holding JavaScript functions used internally +// which are not visible to JavaScript executing on the page. +void V8Proxy::CreateUtilityContext() { + ASSERT(m_utilityContext.IsEmpty()); + + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); + m_utilityContext = v8::Context::New(NULL, global_template); + v8::Context::Scope context_scope(m_utilityContext); + + // Compile JavaScript function for retrieving the source line of the top + // JavaScript stack frame. + static const char* frame_source_line_source = + "function frame_source_line(exec_state) {" + " return exec_state.frame(0).sourceLine();" + "}"; + v8::Script::Compile(v8::String::New(frame_source_line_source))->Run(); + + // Compile JavaScript function for retrieving the source name of the top + // JavaScript stack frame. + static const char* frame_source_name_source = + "function frame_source_name(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(frame_source_name_source))->Run(); +} + + +int V8Proxy::GetSourceLineNumber() { + v8::HandleScope scope; + v8::Handle<v8::Context> utility_context = V8Proxy::GetUtilityContext(); + if (utility_context.IsEmpty()) { + return 0; + } + v8::Context::Scope context_scope(utility_context); + v8::Handle<v8::Function> frame_source_line; + frame_source_line = v8::Local<v8::Function>::Cast( + utility_context->Global()->Get(v8::String::New("frame_source_line"))); + if (frame_source_line.IsEmpty()) { + return 0; + } + v8::Handle<v8::Value> result = v8::Debug::Call(frame_source_line); + if (result.IsEmpty()) { + return 0; + } + return result->Int32Value(); +} + + +String V8Proxy::GetSourceName() { + v8::HandleScope scope; + v8::Handle<v8::Context> utility_context = GetUtilityContext(); + if (utility_context.IsEmpty()) { + return String(); + } + v8::Context::Scope context_scope(utility_context); + v8::Handle<v8::Function> frame_source_name; + frame_source_name = v8::Local<v8::Function>::Cast( + utility_context->Global()->Get(v8::String::New("frame_source_name"))); + if (frame_source_name.IsEmpty()) { + return String(); + } + return ToWebCoreString(v8::Debug::Call(frame_source_name)); +} + +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 debug_id) { + ASSERT(debug_id > 0); + if (m_context.IsEmpty()) { + return false; + } + v8::HandleScope scope; + if (!m_context->GetData()->IsUndefined()) { + return false; + } + + v8::Handle<v8::Object> context_data = v8::Object::New(); + context_data->Set(v8::String::New(kContextDebugDataType), + v8::String::New("page")); + context_data->Set(v8::String::New(kContextDebugDataValue), + v8::Integer::New(debug_id)); + m_context->SetData(context_data); + return true; +} + +// static +int V8Proxy::GetContextDebugId(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; +} + +} // namespace WebCore diff --git a/webkit/port/bindings/v8/v8_proxy.h b/webkit/port/bindings/v8/v8_proxy.h new file mode 100644 index 0000000..fbb74f3 --- /dev/null +++ b/webkit/port/bindings/v8/v8_proxy.h @@ -0,0 +1,673 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_PROXY_H__ +#define V8_PROXY_H__ + +#include <v8.h> +#include "v8_utility.h" +#include "ChromiumBridge.h" +#include "Node.h" +#include "NodeFilter.h" +#include "PlatformString.h" // for WebCore::String +#include "ScriptSourceCode.h" // for WebCore::ScriptSourceCode +#include "SecurityOrigin.h" // for WebCore::SecurityOrigin +#include "V8CustomBinding.h" +#include "V8DOMMap.h" +#include "V8EventListenerList.h" +#include "V8Index.h" +#include <wtf/Assertions.h> +#include <wtf/PassRefPtr.h> // so generated bindings don't have to +#include <wtf/Vector.h> + +#include <iterator> +#include <list> + +#ifdef ENABLE_DOM_STATS_COUNTERS +#define INC_STATS(name) ChromiumBridge::incrementStatsCounter(name) +#else +#define INC_STATS(name) +#endif + +// FIXME: Remove the following hack when we replace all references to GetDOMObjectMap. +#define GetDOMObjectMap getDOMObjectMap + +namespace WebCore { + +class CSSStyleDeclaration; +class ClientRectList; +class DOMImplementation; +class Element; +class Event; +class EventListener; +class Frame; +class HTMLCollection; +class HTMLOptionsCollection; +class HTMLElement; +class HTMLDocument; +class MediaList; +class NamedNodeMap; +class Node; +class NodeList; +class Screen; +class String; +class StyleSheet; +class SVGElement; +class DOMWindow; +class Document; +class EventTarget; +class Event; +class EventListener; +class Navigator; +class MimeType; +class MimeTypeArray; +class Plugin; +class PluginArray; +class StyleSheetList; +class CSSValue; +class CSSRule; +class CSSRuleList; +class CSSValueList; +class NodeFilter; +class ScriptExecutionContext; + +#if ENABLE(DOM_STORAGE) +class Storage; +class StorageEvent; +#endif + +#if ENABLE(SVG) +class SVGElementInstance; +#endif + +class V8EventListener; +class V8ObjectEventListener; + + +// TODO(fqian): use standard logging facilities in WebCore. +void log_info(Frame* frame, const String& msg, const String& url); + + +#ifndef NDEBUG + +#define GlobalHandleTypeList(V) \ + V(PROXY) \ + V(NPOBJECT) \ + V(SCHEDULED_ACTION) \ + V(EVENT_LISTENER) \ + V(NODE_FILTER) \ + V(SCRIPTINSTANCE) \ + V(SCRIPTVALUE) + + +// Host information of persistent handles. +enum GlobalHandleType { +#define ENUM(name) name, + GlobalHandleTypeList(ENUM) +#undef ENUM +}; + + +class GlobalHandleInfo { + public: + GlobalHandleInfo(void* host, GlobalHandleType type) + : host_(host), type_(type) { } + void* host_; + GlobalHandleType type_; +}; + +#endif // NDEBUG + +// The following Batch structs and methods are used for setting multiple +// properties on an ObjectTemplate, used from the generated bindings +// initialization (ConfigureXXXTemplate). This greatly reduces the binary +// size by moving from code driven setup to data table driven setup. + +// BatchedAttribute translates into calls to SetAccessor() on either the +// instance or the prototype ObjectTemplate, based on |on_proto|. +struct BatchedAttribute { + const char* const name; + v8::AccessorGetter getter; + v8::AccessorSetter setter; + V8ClassIndex::V8WrapperType data; + v8::AccessControl settings; + v8::PropertyAttribute attribute; + bool on_proto; +}; + +void BatchConfigureAttributes(v8::Handle<v8::ObjectTemplate> inst, + v8::Handle<v8::ObjectTemplate> proto, + const BatchedAttribute* attrs, + size_t num_attrs); + +// BatchedConstant translates into calls to Set() for setting up an object's +// constants. It sets the constant on both the FunctionTemplate |desc| and the +// ObjectTemplate |proto|. PropertyAttributes is always ReadOnly. +struct BatchedConstant { + const char* const name; + int value; +}; + +void BatchConfigureConstants(v8::Handle<v8::FunctionTemplate> desc, + v8::Handle<v8::ObjectTemplate> proto, + const BatchedConstant* consts, + size_t num_consts); + +const int kMaxRecursionDepth = 20; + +// Information about an extension that is registered for use with V8. If scheme +// is non-empty, it contains the URL scheme the extension should be used with. +// Otherwise, the extension is used with all schemes. +struct V8ExtensionInfo { + String scheme; + v8::Extension* extension; +}; +typedef std::list<V8ExtensionInfo> V8ExtensionList; + +class V8Proxy { + public: + // The types of javascript errors that can be thrown. + enum ErrorType { + RANGE_ERROR, + REFERENCE_ERROR, + SYNTAX_ERROR, + TYPE_ERROR, + GENERAL_ERROR + }; + + explicit V8Proxy(Frame* frame) + : m_frame(frame), m_inlineCode(false), + m_timerCallback(false), m_recursion(0) { } + + ~V8Proxy(); + + Frame* frame() { return m_frame; } + + // Clear page-specific data, but keep the global object identify. + void clearForNavigation(); + + // Clear page-specific data before shutting down the proxy object. + void clearForClose(); + + // Update document object of the frame. + void updateDocument(); + + // Update the security origin of a document + // (e.g., after setting docoument.domain). + void updateSecurityOrigin(); + + // Destroy the global object. + void DestroyGlobal(); + + // TODO(mpcomplete): Need comment. User Gesture related. + bool inlineCode() const { return m_inlineCode; } + void setInlineCode(bool value) { m_inlineCode = value; } + + bool timerCallback() const { return m_timerCallback; } + void setTimerCallback(bool value) { m_timerCallback = value; } + + // Has the context for this proxy been initialized? + bool ContextInitialized(); + + // Disconnects the proxy from its owner frame, + // and clears all timeouts on the DOM window. + void disconnectFrame(); + + bool isEnabled(); + + // Find/Create/Remove event listener wrappers. + PassRefPtr<V8EventListener> FindV8EventListener(v8::Local<v8::Value> listener, + bool html); + PassRefPtr<V8EventListener> FindOrCreateV8EventListener(v8::Local<v8::Value> listener, + bool html); + + PassRefPtr<V8EventListener> FindObjectEventListener(v8::Local<v8::Value> listener, + bool html); + PassRefPtr<V8EventListener> FindOrCreateObjectEventListener(v8::Local<v8::Value> listener, + bool html); + + void RemoveV8EventListener(V8EventListener* listener); + void RemoveObjectEventListener(V8ObjectEventListener* listener); + + // Protect/Unprotect JS wrappers of a DOM object. + static void GCProtect(void* dom_object); + static void GCUnprotect(void* dom_object); + +#if ENABLE(SVG) + static void SetSVGContext(void* object, SVGElement* context); + static SVGElement* GetSVGContext(void* object); +#endif + + void setEventHandlerLineno(int lineno) { m_handlerLineno = lineno; } + void finishedWithEvent(Event* event) { } + + // Evaluate JavaScript in a new isolated world. The script gets its own + // global scope, its own prototypes for intrinsic JavaScript objects (String, + // Array, and so-on), and its own wrappers for all DOM nodes and DOM + // constructors. + void evaluateInNewWorld(const Vector<ScriptSourceCode>& sources); + + // Evaluate JavaScript in a new context. The script gets its own global scope + // and its own prototypes for intrinsic JavaScript objects (String, Array, + // and so-on). It shares the wrappers for all DOM nodes and DOM constructors. + void evaluateInNewContext(const Vector<ScriptSourceCode>& sources); + + // Evaluate a script file in the current execution environment. + // The caller must hold an execution context. + // If cannot evalute the script, it returns an error. + v8::Local<v8::Value> evaluate(const ScriptSourceCode& source, + Node* node); + + // Run an already compiled script. + v8::Local<v8::Value> RunScript(v8::Handle<v8::Script> script, + bool inline_code); + + // Call the function with the given receiver and arguments. + v8::Local<v8::Value> CallFunction(v8::Handle<v8::Function> function, + v8::Handle<v8::Object> receiver, + int argc, + v8::Handle<v8::Value> argv[]); + + // Call the function as constructor with the given arguments. + v8::Local<v8::Value> NewInstance(v8::Handle<v8::Function> constructor, + int argc, + v8::Handle<v8::Value> argv[]); + + // Returns the dom constructor function for the given node type. + v8::Local<v8::Function> GetConstructor(V8ClassIndex::V8WrapperType type); + + // To create JS Wrapper objects, we create a cache of a 'boiler plate' + // object, and then simply Clone that object each time we need a new one. + // This is faster than going through the full object creation process. + v8::Local<v8::Object> CreateWrapperFromCache(V8ClassIndex::V8WrapperType type); + + // Returns the window object of the currently executing context. + static DOMWindow* retrieveWindow(); + // Returns the window object associated with a context. + static DOMWindow* retrieveWindow(v8::Handle<v8::Context> context); + // Returns V8Proxy object of the currently executing context. + static V8Proxy* retrieve(); + // Returns V8Proxy object associated with a frame. + static V8Proxy* retrieve(Frame* frame); + // Returns V8Proxy object associated with a script execution context. + static V8Proxy* retrieve(ScriptExecutionContext* context); + + // Returns the frame object of the window object associated + // with the currently executing context. + static Frame* retrieveFrame(); + // Returns the frame object of the window object associated with + // a context. + static Frame* retrieveFrame(v8::Handle<v8::Context> context); + + + // The three functions below retrieve WebFrame instances relating the + // currently executing JavaScript. Since JavaScript can make function calls + // across frames, though, we need to be more precise. + // + // For example, imagine that a JS function in frame A calls a function in + // frame B, which calls native code, which wants to know what the 'active' + // frame is. + // + // The 'entered context' is the context where execution first entered the + // script engine; the context that is at the bottom of the JS function stack. + // RetrieveFrameForEnteredContext() would return Frame A in our example. + // This frame is often referred to as the "dynamic global object." + // + // The 'current context' is the context the JS engine is currently inside of; + // the context that is at the top of the JS function stack. + // RetrieveFrameForCurrentContext() would return Frame B in our example. + // This frame is often referred to as the "lexical global object." + // + // Finally, the 'calling context' is the context one below the current + // context on the JS function stack. For example, if function f calls + // function g, then the calling context will be the context associated with + // f. This context is commonly used by DOM security checks because they want + // to know who called them. + // + // If you are unsure which of these functions to use, ask abarth. + // + // NOTE: These cannot be declared as inline function, because VS complains at + // linking time. + static Frame* retrieveFrameForEnteredContext(); + static Frame* retrieveFrameForCurrentContext(); + static Frame* retrieveFrameForCallingContext(); + + // Returns V8 Context of a frame. If none exists, creates + // a new context. It is potentially slow and consumes memory. + static v8::Local<v8::Context> GetContext(Frame* frame); + static v8::Local<v8::Context> GetCurrentContext(); + + // If the current context causes out of memory, JavaScript setting + // is disabled and it returns true. + static bool HandleOutOfMemory(); + + // Check if the active execution context can access the target frame. + static bool CanAccessFrame(Frame* target, bool report_error); + + // Check if it is safe to access the given node from the + // current security context. + static bool CheckNodeSecurity(Node* node); + + static v8::Handle<v8::Value> CheckNewLegal(const v8::Arguments& args); + + // Create a V8 wrapper for a C pointer + static v8::Handle<v8::Value> WrapCPointer(void* cptr) { + // Represent void* as int + int addr = reinterpret_cast<int>(cptr); + ASSERT((addr & 0x01) == 0); // the address must be aligned. + return v8::Integer::New(addr >> 1); + } + + // Take C pointer out of a v8 wrapper + template <class C> + static C* ExtractCPointer(v8::Handle<v8::Value> obj) { + return static_cast<C*>(ExtractCPointerImpl(obj)); + } + + static v8::Handle<v8::Script> CompileScript(v8::Handle<v8::String> code, + const String& fileName, + int baseLine); + +#ifndef NDEBUG + // Checks if a v8 value can be a DOM wrapper + static bool MaybeDOMWrapper(v8::Handle<v8::Value> value); +#endif + + // Sets contents of a DOM wrapper. + static void SetDOMWrapper(v8::Handle<v8::Object> obj, int type, void* ptr); + + static v8::Handle<v8::Object> LookupDOMWrapper( + V8ClassIndex::V8WrapperType type, v8::Handle<v8::Value> value); + + // A helper function extract native object pointer from a DOM wrapper + // and cast to the specified type. + template <class C> + static C* DOMWrapperToNative(v8::Handle<v8::Value> object) { + ASSERT(MaybeDOMWrapper(object)); + v8::Handle<v8::Value> ptr = + v8::Handle<v8::Object>::Cast(object)->GetInternalField( + V8Custom::kDOMWrapperObjectIndex); + return ExtractCPointer<C>(ptr); + } + + // A help function extract a node type pointer from a DOM wrapper. + // Wrapped pointer must be cast to Node* first. + static void* DOMWrapperToNodeHelper(v8::Handle<v8::Value> value); + + template <class C> + static C* DOMWrapperToNode(v8::Handle<v8::Value> value) { + return static_cast<C*>(DOMWrapperToNodeHelper(value)); + } + + template<typename T> + static v8::Handle<v8::Value> ToV8Object(V8ClassIndex::V8WrapperType type, PassRefPtr<T> imp) + { + return ToV8Object(type, imp.get()); + } + static v8::Handle<v8::Value> ToV8Object(V8ClassIndex::V8WrapperType type, + void* imp); + // Fast-path for Node objects. + static v8::Handle<v8::Value> NodeToV8Object(Node* node); + + template <class C> + static C* ToNativeObject(V8ClassIndex::V8WrapperType type, + v8::Handle<v8::Value> object) { + return static_cast<C*>(ToNativeObjectImpl(type, object)); + } + + static V8ClassIndex::V8WrapperType GetDOMWrapperType( + v8::Handle<v8::Object> object); + + // If the exception code is different from zero, a DOM exception is + // schedule to be thrown. + static void SetDOMException(int exception_code); + + // Schedule an error object to be thrown. + static v8::Handle<v8::Value> ThrowError(ErrorType type, const char* message); + + // Create an instance of a function descriptor and set to the global object + // as a named property. Used by v8_test_shell. + static void BindJSObjectToWindow(Frame* frame, + const char* name, + int type, + v8::Handle<v8::FunctionTemplate> desc, + void* imp); + + static v8::Handle<v8::Value> EventToV8Object(Event* event); + static Event* ToNativeEvent(v8::Handle<v8::Value> jsevent) { + if (!IsDOMEventWrapper(jsevent)) return 0; + return DOMWrapperToNative<Event>(jsevent); + } + + static v8::Handle<v8::Value> EventTargetToV8Object(EventTarget* target); + // Wrap and unwrap JS event listeners + static v8::Handle<v8::Value> EventListenerToV8Object(EventListener* target); + + // DOMImplementation is a singleton and it is handled in a special + // way. A wrapper is generated per document and stored in an + // internal field of the document. + static v8::Handle<v8::Value> DOMImplementationToV8Object( + DOMImplementation* impl); + + // Wrap JS node filter in C++ + static PassRefPtr<NodeFilter> ToNativeNodeFilter(v8::Handle<v8::Value> filter); + + static v8::Persistent<v8::FunctionTemplate> GetTemplate( + V8ClassIndex::V8WrapperType type); + + template <int tag, typename T> + static v8::Handle<v8::Value> ConstructDOMObject(const v8::Arguments& args); + + // Checks whether a DOM object has a JS wrapper. + static bool DOMObjectHasJSWrapper(void* obj); + // Set JS wrapper of a DOM object, the caller in charge of increase ref. + static void SetJSWrapperForDOMObject(void* obj, + v8::Persistent<v8::Object> wrapper); + static void SetJSWrapperForActiveDOMObject(void* obj, + v8::Persistent<v8::Object> wrapper); + static void SetJSWrapperForDOMNode(Node* node, + v8::Persistent<v8::Object> wrapper); + + // Process any pending JavaScript console messages. + static void ProcessConsoleMessages(); + +#ifndef NDEBUG + // For debugging and leak detection purpose + static void RegisterGlobalHandle(GlobalHandleType type, + void* host, + v8::Persistent<v8::Value> handle); + static void UnregisterGlobalHandle(void* host, + v8::Persistent<v8::Value> handle); +#endif + + // Check whether a V8 value is a wrapper of type |classType|. + static bool IsWrapperOfType(v8::Handle<v8::Value> obj, + V8ClassIndex::V8WrapperType classType); + + // Function for retrieving the line number and source name for the top + // JavaScript stack frame. + static int GetSourceLineNumber(); + static String GetSourceName(); + + + // Returns a local handle of the context. + v8::Local<v8::Context> GetContext() { + return v8::Local<v8::Context>::New(m_context); + } + + bool SetContextDebugId(int id); + static int GetContextDebugId(v8::Handle<v8::Context> context); + + // Registers an extension to be available on webpages with a particular scheme + // If the scheme argument is empty, the extension is available on all pages. + // Will only affect v8 contexts initialized after this call. Takes ownership + // of the v8::Extension object passed. + static void RegisterExtension(v8::Extension* extension, + const String& schemeRestriction); + + static void* ToSVGPODTypeImpl(V8ClassIndex::V8WrapperType type, + v8::Handle<v8::Value> object); + + // TODO(abarth): Separate these concerns from V8Proxy? + v8::Persistent<v8::Context> createNewContext(v8::Handle<v8::Object> global); + bool installDOMWindow(v8::Handle<v8::Context> context, DOMWindow* window); + + private: + static const char* kContextDebugDataType; + static const char* kContextDebugDataValue; + + void InitContextIfNeeded(); + void DisconnectEventListeners(); + void SetSecurityToken(); + void ClearDocumentWrapper(); + void UpdateDocumentWrapper(v8::Handle<v8::Value> wrapper); + + // The JavaScript wrapper for the document object is cached on the global + // object for fast access. UpdateDocumentWrapperCache sets the wrapper + // for the current document on the global object. ClearDocumentWrapperCache + // deletes the document wrapper from the global object. + void UpdateDocumentWrapperCache(); + void ClearDocumentWrapperCache(); + + // Dispose global handles of m_contexts and friends. + void DisposeContextHandles(); + + static bool CanAccessPrivate(DOMWindow* target); + + // Check whether a V8 value is a DOM Event wrapper + static bool IsDOMEventWrapper(v8::Handle<v8::Value> obj); + + static void* ToNativeObjectImpl(V8ClassIndex::V8WrapperType type, + v8::Handle<v8::Value> object); + + // Take C pointer out of a v8 wrapper + static void* ExtractCPointerImpl(v8::Handle<v8::Value> obj) { + ASSERT(obj->IsNumber()); + int addr = obj->Int32Value(); + return reinterpret_cast<void*>(addr << 1); + } + + + static v8::Handle<v8::Value> StyleSheetToV8Object(StyleSheet* sheet); + static v8::Handle<v8::Value> CSSValueToV8Object(CSSValue* value); + static v8::Handle<v8::Value> CSSRuleToV8Object(CSSRule* rule); + // Returns the JS wrapper of a window object, initializes the environment + // of the window frame if needed. + static v8::Handle<v8::Value> WindowToV8Object(DOMWindow* window); + +#if ENABLE(SVG) + static v8::Handle<v8::Value> SVGElementInstanceToV8Object( + SVGElementInstance* instance); + static v8::Handle<v8::Value> SVGObjectWithContextToV8Object( + V8ClassIndex::V8WrapperType type, void* object); +#endif + + // Set hidden references in a DOMWindow object of a frame. + static void SetHiddenWindowReference(Frame* frame, + const int internal_index, + v8::Handle<v8::Object> jsobj); + + static V8ClassIndex::V8WrapperType GetHTMLElementType(HTMLElement* elm); + + // The first parameter, desc_type, specifies the function descriptor + // used to create JS object. The second parameter, cptr_type, specifies + // the type of third parameter, impl, for type casting. + // For example, a HTML element has HTMLELEMENT desc_type, but always + // use NODE as cptr_type. JS wrapper stores cptr_type and impl as + // internal fields. + static v8::Local<v8::Object> InstantiateV8Object( + V8ClassIndex::V8WrapperType desc_type, + V8ClassIndex::V8WrapperType cptr_type, + void* impl); + + static const char* GetRangeExceptionName(int exception_code); + static const char* GetEventExceptionName(int exception_code); + static const char* GetXMLHttpRequestExceptionName(int exception_code); + static const char* GetDOMExceptionName(int exception_code); + +#if ENABLE(XPATH) + static const char* GetXPathExceptionName(int exception_code); +#endif + +#if ENABLE(SVG) + static V8ClassIndex::V8WrapperType GetSVGElementType(SVGElement* elm); + static const char* GetSVGExceptionName(int exception_code); +#endif + + // Create and populate the utility context. + static void CreateUtilityContext(); + + // Returns a local handle of the utility context. + static v8::Local<v8::Context> GetUtilityContext() { + if (m_utilityContext.IsEmpty()) { + CreateUtilityContext(); + } + return v8::Local<v8::Context>::New(m_utilityContext); + } + + Frame* m_frame; + + v8::Persistent<v8::Context> m_context; + // For each possible type of wrapper, we keep a boilerplate object. + // The boilerplate is used to create additional wrappers of the same type. + // We keep a single persistent handle to an array of the activated + // boilerplates. + v8::Persistent<v8::Array> m_wrapper_boilerplates; + v8::Persistent<v8::Value> m_object_prototype; + + v8::Persistent<v8::Object> m_global; + v8::Persistent<v8::Value> m_document; + + // Utility context holding JavaScript functions used internally. + static v8::Persistent<v8::Context> m_utilityContext; + + int m_handlerLineno; + + // A list of event listeners created for this frame, + // the list gets cleared when removing all timeouts. + V8EventListenerList m_event_listeners; + + // A list of event listeners create for XMLHttpRequest object for this frame, + // the list gets cleared when removing all timeouts. + V8EventListenerList m_xhr_listeners; + + // True for <a href="javascript:foo()"> and false for <script>foo()</script>. + // Only valid during execution. + bool m_inlineCode; + + // True when executing from within a timer callback. Only valid during + // execution. + bool m_timerCallback; + + // Track the recursion depth to be able to avoid too deep recursion. The V8 + // engine allows much more recursion than KJS does so we need to guard against + // excessive recursion in the binding layer. + int m_recursion; + + // List of extensions registered with the context. + static V8ExtensionList m_extensions; +}; + +template <int tag, typename T> +v8::Handle<v8::Value> V8Proxy::ConstructDOMObject(const v8::Arguments& args) { + if (!args.IsConstructCall()) { + V8Proxy::ThrowError(V8Proxy::TYPE_ERROR, + "DOM object constructor cannot be called as a function."); + return v8::Undefined(); + } + + + // Note: it's OK to let this RefPtr go out of scope because we also call + // SetDOMWrapper(), which effectively holds a reference to obj. + RefPtr<T> obj = T::create(); + V8Proxy::SetDOMWrapper(args.Holder(), tag, obj.get()); + obj->ref(); + V8Proxy::SetJSWrapperForDOMObject( + obj.get(), v8::Persistent<v8::Object>::New(args.Holder())); + return args.Holder(); +} + +} // namespace WebCore + +#endif // V8_PROXY_H__ diff --git a/webkit/port/bindings/v8/v8_utility.h b/webkit/port/bindings/v8/v8_utility.h new file mode 100644 index 0000000..a7c3325 --- /dev/null +++ b/webkit/port/bindings/v8/v8_utility.h @@ -0,0 +1,68 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_UTILITY_H__ +#define V8_UTILITY_H__ + +#include "V8Utilities.h" + +// To break a cycle dependency during upstreaming this block of code, +// ifdefing it out based on this #define. +// +// TODO(ajwong): After https://bugs.webkit.org/show_bug.cgi?id=25595 +// lands and is rolled down into chromium, migrate all headaers to use +// the code in V8Utilities.h directly, and then, remove this code. +#ifndef V8UTILITIES_DEFINED +namespace WebCore { + +class AllowAllocation { + public: + inline AllowAllocation() { + m_prev = m_current; + m_current = true; + } + inline ~AllowAllocation() { + m_current = m_prev; + } + static bool m_current; + private: + bool m_prev; +}; + +class SafeAllocation { + public: + static inline v8::Local<v8::Object> NewInstance( + v8::Handle<v8::Function> fun); + static inline v8::Local<v8::Object> NewInstance( + v8::Handle<v8::ObjectTemplate> templ); + static inline v8::Local<v8::Object> NewInstance( + v8::Handle<v8::Function> fun, int argc, v8::Handle<v8::Value> argv[]); +}; + +v8::Local<v8::Object> SafeAllocation::NewInstance( + v8::Handle<v8::Function> fun) { + if (fun.IsEmpty()) + return v8::Local<v8::Object>(); + AllowAllocation allow; + return fun->NewInstance(); +} + +v8::Local<v8::Object> SafeAllocation::NewInstance( + v8::Handle<v8::ObjectTemplate> templ) { + if (templ.IsEmpty()) return v8::Local<v8::Object>(); + AllowAllocation allow; + return templ->NewInstance(); +} + +v8::Local<v8::Object> SafeAllocation::NewInstance( + v8::Handle<v8::Function> fun, int argc, v8::Handle<v8::Value> argv[]) { + if (fun.IsEmpty()) return v8::Local<v8::Object>(); + AllowAllocation allow; + return fun->NewInstance(argc, argv); +} + +} // namespace WebCore +#endif // V8UTILITIES_DEFINED + +#endif // V8_UTILITY_H__ diff --git a/webkit/tools/test_shell/test_shell.cc b/webkit/tools/test_shell/test_shell.cc index 13a5e89..caa450d 100644 --- a/webkit/tools/test_shell/test_shell.cc +++ b/webkit/tools/test_shell/test_shell.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "V8Proxy.h" +#include "v8_proxy.h" #undef LOG #include "webkit/tools/test_shell/test_shell.h" @@ -183,7 +183,7 @@ void TestShell::Dump(TestShell* shell) { if ((shell == NULL) || ((params = shell->test_params()) == NULL)) return; - WebCore::V8Proxy::processConsoleMessages(); + WebCore::V8Proxy::ProcessConsoleMessages(); // Echo the url in the output so we know we're not getting out of sync. printf("#URL:%s\n", params->test_url.c_str()); diff --git a/webkit/webkit.gyp b/webkit/webkit.gyp index ea3b0b8..598ebb3 100644 --- a/webkit/webkit.gyp +++ b/webkit/webkit.gyp @@ -1130,7 +1130,6 @@ '../third_party/WebKit/WebCore/bindings/v8/V8NodeFilterCondition.h', '../third_party/WebKit/WebCore/bindings/v8/V8ObjectEventListener.cpp', '../third_party/WebKit/WebCore/bindings/v8/V8ObjectEventListener.h', - '../third_party/WebKit/WebCore/bindings/v8/V8Proxy.cpp', '../third_party/WebKit/WebCore/bindings/v8/V8Proxy.h', '../third_party/WebKit/WebCore/bindings/v8/V8Utilities.cpp', '../third_party/WebKit/WebCore/bindings/v8/V8Utilities.h', @@ -1170,6 +1169,9 @@ 'port/bindings/v8/V8NPUtils.h', 'port/bindings/v8/V8NPObject.cpp', 'port/bindings/v8/V8NPObject.h', + 'port/bindings/v8/v8_proxy.cpp', + 'port/bindings/v8/v8_proxy.h', + 'port/bindings/v8/v8_utility.h', # This list contains every .cpp, .h, .m, and .mm file in the # subdirectories of ../third_party/WebKit/WebCore, excluding the |