Index: webkit/port/bindings/v8/v8_proxy.cpp |
=================================================================== |
--- webkit/port/bindings/v8/v8_proxy.cpp (revision 19423) |
+++ webkit/port/bindings/v8/v8_proxy.cpp (working copy) |
@@ -1,3566 +0,0 @@ |
-// 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 "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_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()->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 |